Commit | Line | Data |
---|---|---|
23e7b2ab XL |
1 | /* |
2 | * Hisilicon Kirin SoCs drm master driver | |
3 | * | |
4 | * Copyright (c) 2016 Linaro Limited. | |
5 | * Copyright (c) 2014-2016 Hisilicon Limited. | |
6 | * | |
7 | * Author: | |
8 | * Xinliang Liu <z.liuxinliang@hisilicon.com> | |
9 | * Xinliang Liu <xinliang.liu@linaro.org> | |
10 | * Xinwei Kong <kong.kongxinwei@hisilicon.com> | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License version 2 as | |
14 | * published by the Free Software Foundation. | |
15 | * | |
16 | */ | |
17 | ||
18 | #include <linux/of_platform.h> | |
19 | #include <linux/component.h> | |
20 | #include <linux/of_graph.h> | |
21 | ||
22 | #include <drm/drmP.h> | |
23 | #include <drm/drm_gem_cma_helper.h> | |
24 | #include <drm/drm_fb_cma_helper.h> | |
25 | #include <drm/drm_atomic_helper.h> | |
6b7a5965 | 26 | #include <drm/drm_crtc_helper.h> |
23e7b2ab XL |
27 | |
28 | #include "kirin_drm_drv.h" | |
29 | ||
30 | static struct kirin_dc_ops *dc_ops; | |
31 | ||
32 | static int kirin_drm_kms_cleanup(struct drm_device *dev) | |
33 | { | |
783ad972 XL |
34 | struct kirin_drm_private *priv = dev->dev_private; |
35 | ||
6b7a5965 XL |
36 | #ifdef CONFIG_DRM_FBDEV_EMULATION |
37 | if (priv->fbdev) { | |
38 | drm_fbdev_cma_fini(priv->fbdev); | |
39 | priv->fbdev = NULL; | |
40 | } | |
41 | #endif | |
42 | drm_kms_helper_poll_fini(dev); | |
bc4611e8 | 43 | drm_vblank_cleanup(dev); |
9cd2e854 | 44 | dc_ops->cleanup(dev); |
23e7b2ab | 45 | drm_mode_config_cleanup(dev); |
783ad972 XL |
46 | devm_kfree(dev->dev, priv); |
47 | dev->dev_private = NULL; | |
23e7b2ab XL |
48 | |
49 | return 0; | |
50 | } | |
51 | ||
6b7a5965 XL |
52 | #ifdef CONFIG_DRM_FBDEV_EMULATION |
53 | static void kirin_fbdev_output_poll_changed(struct drm_device *dev) | |
54 | { | |
55 | struct kirin_drm_private *priv = dev->dev_private; | |
56 | ||
57 | if (priv->fbdev) { | |
58 | drm_fbdev_cma_hotplug_event(priv->fbdev); | |
59 | } else { | |
60 | priv->fbdev = drm_fbdev_cma_init(dev, 32, | |
61 | dev->mode_config.num_crtc, | |
62 | dev->mode_config.num_connector); | |
63 | if (IS_ERR(priv->fbdev)) | |
64 | priv->fbdev = NULL; | |
65 | } | |
66 | } | |
67 | #endif | |
68 | ||
23e7b2ab XL |
69 | static const struct drm_mode_config_funcs kirin_drm_mode_config_funcs = { |
70 | .fb_create = drm_fb_cma_create, | |
6b7a5965 XL |
71 | #ifdef CONFIG_DRM_FBDEV_EMULATION |
72 | .output_poll_changed = kirin_fbdev_output_poll_changed, | |
73 | #endif | |
23e7b2ab XL |
74 | .atomic_check = drm_atomic_helper_check, |
75 | .atomic_commit = drm_atomic_helper_commit, | |
76 | }; | |
77 | ||
78 | static void kirin_drm_mode_config_init(struct drm_device *dev) | |
79 | { | |
80 | dev->mode_config.min_width = 0; | |
81 | dev->mode_config.min_height = 0; | |
82 | ||
83 | dev->mode_config.max_width = 2048; | |
84 | dev->mode_config.max_height = 2048; | |
85 | ||
86 | dev->mode_config.funcs = &kirin_drm_mode_config_funcs; | |
87 | } | |
88 | ||
89 | static int kirin_drm_kms_init(struct drm_device *dev) | |
90 | { | |
783ad972 | 91 | struct kirin_drm_private *priv; |
23e7b2ab XL |
92 | int ret; |
93 | ||
783ad972 XL |
94 | priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL); |
95 | if (!priv) | |
96 | return -ENOMEM; | |
97 | ||
98 | dev->dev_private = priv; | |
23e7b2ab XL |
99 | dev_set_drvdata(dev->dev, dev); |
100 | ||
101 | /* dev->mode_config initialization */ | |
102 | drm_mode_config_init(dev); | |
103 | kirin_drm_mode_config_init(dev); | |
104 | ||
105 | /* display controller init */ | |
9cd2e854 | 106 | ret = dc_ops->init(dev); |
23e7b2ab XL |
107 | if (ret) |
108 | goto err_mode_config_cleanup; | |
109 | ||
110 | /* bind and init sub drivers */ | |
111 | ret = component_bind_all(dev->dev, dev); | |
112 | if (ret) { | |
113 | DRM_ERROR("failed to bind all component.\n"); | |
114 | goto err_dc_cleanup; | |
115 | } | |
116 | ||
bc4611e8 XL |
117 | /* vblank init */ |
118 | ret = drm_vblank_init(dev, dev->mode_config.num_crtc); | |
119 | if (ret) { | |
120 | DRM_ERROR("failed to initialize vblank.\n"); | |
121 | goto err_unbind_all; | |
122 | } | |
123 | /* with irq_enabled = true, we can use the vblank feature. */ | |
124 | dev->irq_enabled = true; | |
125 | ||
23e7b2ab XL |
126 | /* reset all the states of crtc/plane/encoder/connector */ |
127 | drm_mode_config_reset(dev); | |
128 | ||
6b7a5965 XL |
129 | /* init kms poll for handling hpd */ |
130 | drm_kms_helper_poll_init(dev); | |
131 | ||
132 | /* force detection after connectors init */ | |
133 | (void)drm_helper_hpd_irq_event(dev); | |
134 | ||
23e7b2ab XL |
135 | return 0; |
136 | ||
bc4611e8 XL |
137 | err_unbind_all: |
138 | component_unbind_all(dev->dev, dev); | |
23e7b2ab XL |
139 | err_dc_cleanup: |
140 | dc_ops->cleanup(dev); | |
141 | err_mode_config_cleanup: | |
142 | drm_mode_config_cleanup(dev); | |
783ad972 XL |
143 | devm_kfree(dev->dev, priv); |
144 | dev->dev_private = NULL; | |
23e7b2ab XL |
145 | |
146 | return ret; | |
147 | } | |
148 | ||
149 | static const struct file_operations kirin_drm_fops = { | |
150 | .owner = THIS_MODULE, | |
151 | .open = drm_open, | |
152 | .release = drm_release, | |
153 | .unlocked_ioctl = drm_ioctl, | |
154 | #ifdef CONFIG_COMPAT | |
155 | .compat_ioctl = drm_compat_ioctl, | |
156 | #endif | |
157 | .poll = drm_poll, | |
158 | .read = drm_read, | |
159 | .llseek = no_llseek, | |
160 | .mmap = drm_gem_cma_mmap, | |
161 | }; | |
162 | ||
163 | static int kirin_gem_cma_dumb_create(struct drm_file *file, | |
164 | struct drm_device *dev, | |
165 | struct drm_mode_create_dumb *args) | |
166 | { | |
167 | return drm_gem_cma_dumb_create_internal(file, dev, args); | |
168 | } | |
169 | ||
170 | static struct drm_driver kirin_drm_driver = { | |
171 | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | | |
e1f96ef4 | 172 | DRIVER_ATOMIC, |
23e7b2ab | 173 | .fops = &kirin_drm_fops, |
23e7b2ab | 174 | |
3d6ab094 | 175 | .gem_free_object_unlocked = drm_gem_cma_free_object, |
23e7b2ab XL |
176 | .gem_vm_ops = &drm_gem_cma_vm_ops, |
177 | .dumb_create = kirin_gem_cma_dumb_create, | |
178 | .dumb_map_offset = drm_gem_cma_dumb_map_offset, | |
179 | .dumb_destroy = drm_gem_dumb_destroy, | |
180 | ||
181 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | |
182 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | |
183 | .gem_prime_export = drm_gem_prime_export, | |
184 | .gem_prime_import = drm_gem_prime_import, | |
185 | .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, | |
186 | .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, | |
187 | .gem_prime_vmap = drm_gem_cma_prime_vmap, | |
188 | .gem_prime_vunmap = drm_gem_cma_prime_vunmap, | |
189 | .gem_prime_mmap = drm_gem_cma_prime_mmap, | |
190 | ||
191 | .name = "kirin", | |
192 | .desc = "Hisilicon Kirin SoCs' DRM Driver", | |
193 | .date = "20150718", | |
194 | .major = 1, | |
195 | .minor = 0, | |
196 | }; | |
197 | ||
198 | static int compare_of(struct device *dev, void *data) | |
199 | { | |
200 | return dev->of_node == data; | |
201 | } | |
202 | ||
23e7b2ab XL |
203 | static int kirin_drm_bind(struct device *dev) |
204 | { | |
205 | struct drm_driver *driver = &kirin_drm_driver; | |
206 | struct drm_device *drm_dev; | |
207 | int ret; | |
208 | ||
209 | drm_dev = drm_dev_alloc(driver, dev); | |
210 | if (!drm_dev) | |
211 | return -ENOMEM; | |
212 | ||
9cd2e854 DV |
213 | drm_dev->platformdev = to_platform_device(dev); |
214 | ||
23e7b2ab XL |
215 | ret = kirin_drm_kms_init(drm_dev); |
216 | if (ret) | |
217 | goto err_drm_dev_unref; | |
218 | ||
219 | ret = drm_dev_register(drm_dev, 0); | |
220 | if (ret) | |
221 | goto err_kms_cleanup; | |
222 | ||
23e7b2ab XL |
223 | DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", |
224 | driver->name, driver->major, driver->minor, driver->patchlevel, | |
225 | driver->date, drm_dev->primary->index); | |
226 | ||
227 | return 0; | |
228 | ||
23e7b2ab XL |
229 | err_kms_cleanup: |
230 | kirin_drm_kms_cleanup(drm_dev); | |
231 | err_drm_dev_unref: | |
232 | drm_dev_unref(drm_dev); | |
233 | ||
234 | return ret; | |
235 | } | |
236 | ||
237 | static void kirin_drm_unbind(struct device *dev) | |
238 | { | |
12cb4c87 XL |
239 | struct drm_device *drm_dev = dev_get_drvdata(dev); |
240 | ||
12cb4c87 XL |
241 | drm_dev_unregister(drm_dev); |
242 | kirin_drm_kms_cleanup(drm_dev); | |
243 | drm_dev_unref(drm_dev); | |
23e7b2ab XL |
244 | } |
245 | ||
246 | static const struct component_master_ops kirin_drm_ops = { | |
247 | .bind = kirin_drm_bind, | |
248 | .unbind = kirin_drm_unbind, | |
249 | }; | |
250 | ||
251 | static struct device_node *kirin_get_remote_node(struct device_node *np) | |
252 | { | |
253 | struct device_node *endpoint, *remote; | |
254 | ||
255 | /* get the first endpoint, in our case only one remote node | |
256 | * is connected to display controller. | |
257 | */ | |
258 | endpoint = of_graph_get_next_endpoint(np, NULL); | |
259 | if (!endpoint) { | |
260 | DRM_ERROR("no valid endpoint node\n"); | |
261 | return ERR_PTR(-ENODEV); | |
262 | } | |
263 | of_node_put(endpoint); | |
264 | ||
265 | remote = of_graph_get_remote_port_parent(endpoint); | |
266 | if (!remote) { | |
267 | DRM_ERROR("no valid remote node\n"); | |
268 | return ERR_PTR(-ENODEV); | |
269 | } | |
270 | of_node_put(remote); | |
271 | ||
272 | if (!of_device_is_available(remote)) { | |
273 | DRM_ERROR("not available for remote node\n"); | |
274 | return ERR_PTR(-ENODEV); | |
275 | } | |
276 | ||
277 | return remote; | |
278 | } | |
279 | ||
280 | static int kirin_drm_platform_probe(struct platform_device *pdev) | |
281 | { | |
282 | struct device *dev = &pdev->dev; | |
283 | struct device_node *np = dev->of_node; | |
284 | struct component_match *match = NULL; | |
285 | struct device_node *remote; | |
286 | ||
287 | dc_ops = (struct kirin_dc_ops *)of_device_get_match_data(dev); | |
288 | if (!dc_ops) { | |
289 | DRM_ERROR("failed to get dt id data\n"); | |
290 | return -EINVAL; | |
291 | } | |
292 | ||
293 | remote = kirin_get_remote_node(np); | |
294 | if (IS_ERR(remote)) | |
295 | return PTR_ERR(remote); | |
296 | ||
297 | component_match_add(dev, &match, compare_of, remote); | |
298 | ||
299 | return component_master_add_with_match(dev, &kirin_drm_ops, match); | |
300 | ||
301 | return 0; | |
302 | } | |
303 | ||
304 | static int kirin_drm_platform_remove(struct platform_device *pdev) | |
305 | { | |
306 | component_master_del(&pdev->dev, &kirin_drm_ops); | |
307 | dc_ops = NULL; | |
308 | return 0; | |
309 | } | |
310 | ||
311 | static const struct of_device_id kirin_drm_dt_ids[] = { | |
312 | { .compatible = "hisilicon,hi6220-ade", | |
313 | .data = &ade_dc_ops, | |
314 | }, | |
315 | { /* end node */ }, | |
316 | }; | |
317 | MODULE_DEVICE_TABLE(of, kirin_drm_dt_ids); | |
318 | ||
319 | static struct platform_driver kirin_drm_platform_driver = { | |
320 | .probe = kirin_drm_platform_probe, | |
321 | .remove = kirin_drm_platform_remove, | |
322 | .driver = { | |
323 | .name = "kirin-drm", | |
324 | .of_match_table = kirin_drm_dt_ids, | |
325 | }, | |
326 | }; | |
327 | ||
328 | module_platform_driver(kirin_drm_platform_driver); | |
329 | ||
330 | MODULE_AUTHOR("Xinliang Liu <xinliang.liu@linaro.org>"); | |
331 | MODULE_AUTHOR("Xinliang Liu <z.liuxinliang@hisilicon.com>"); | |
332 | MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei@hisilicon.com>"); | |
333 | MODULE_DESCRIPTION("hisilicon Kirin SoCs' DRM master driver"); | |
334 | MODULE_LICENSE("GPL v2"); |