]>
Commit | Line | Data |
---|---|---|
caab277b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
c8afe684 | 2 | /* |
25fdd593 | 3 | * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. |
c8afe684 RC |
4 | * Copyright (C) 2013 Red Hat |
5 | * Author: Rob Clark <robdclark@gmail.com> | |
c8afe684 RC |
6 | */ |
7 | ||
25fdd593 JS |
8 | #include <linux/kthread.h> |
9 | #include <uapi/linux/sched/types.h> | |
97ac0e47 RK |
10 | #include <drm/drm_of.h> |
11 | ||
c8afe684 | 12 | #include "msm_drv.h" |
edcd60ce | 13 | #include "msm_debugfs.h" |
fde5de6c | 14 | #include "msm_fence.h" |
f05c83e7 | 15 | #include "msm_gem.h" |
7198e6b0 | 16 | #include "msm_gpu.h" |
dd2da6e3 | 17 | #include "msm_kms.h" |
c2052a4e | 18 | #include "adreno/adreno_gpu.h" |
c8afe684 | 19 | |
a8d854c1 RC |
20 | |
21 | /* | |
22 | * MSM driver version: | |
23 | * - 1.0.0 - initial interface | |
24 | * - 1.1.0 - adds madvise, and support for submits with > 4 cmd buffers | |
7a3bcc0a | 25 | * - 1.2.0 - adds explicit fence support for submit ioctl |
f7de1545 JC |
26 | * - 1.3.0 - adds GMEM_BASE + NR_RINGS params, SUBMITQUEUE_NEW + |
27 | * SUBMITQUEUE_CLOSE ioctls, and MSM_INFO_IOVA flag for | |
28 | * MSM_GEM_INFO ioctl. | |
1fed8df3 RC |
29 | * - 1.4.0 - softpin, MSM_RELOC_BO_DUMP, and GEM_INFO support to set/get |
30 | * GEM object's debug name | |
b0fb6604 | 31 | * - 1.5.0 - Add SUBMITQUERY_QUERY ioctl |
a8d854c1 RC |
32 | */ |
33 | #define MSM_VERSION_MAJOR 1 | |
b0fb6604 | 34 | #define MSM_VERSION_MINOR 5 |
a8d854c1 RC |
35 | #define MSM_VERSION_PATCHLEVEL 0 |
36 | ||
c8afe684 RC |
37 | static const struct drm_mode_config_funcs mode_config_funcs = { |
38 | .fb_create = msm_framebuffer_create, | |
4ccbc6e5 | 39 | .output_poll_changed = drm_fb_helper_output_poll_changed, |
1f920175 | 40 | .atomic_check = drm_atomic_helper_check, |
d14659f5 SP |
41 | .atomic_commit = drm_atomic_helper_commit, |
42 | }; | |
43 | ||
44 | static const struct drm_mode_config_helper_funcs mode_config_helper_funcs = { | |
45 | .atomic_commit_tail = msm_atomic_commit_tail, | |
c8afe684 RC |
46 | }; |
47 | ||
c8afe684 RC |
48 | #ifdef CONFIG_DRM_MSM_REGISTER_LOGGING |
49 | static bool reglog = false; | |
50 | MODULE_PARM_DESC(reglog, "Enable register read/write logging"); | |
51 | module_param(reglog, bool, 0600); | |
52 | #else | |
53 | #define reglog 0 | |
54 | #endif | |
55 | ||
a9ee34b7 | 56 | #ifdef CONFIG_DRM_FBDEV_EMULATION |
e90dfec7 RC |
57 | static bool fbdev = true; |
58 | MODULE_PARM_DESC(fbdev, "Enable fbdev compat layer"); | |
59 | module_param(fbdev, bool, 0600); | |
60 | #endif | |
61 | ||
3a10ba8c | 62 | static char *vram = "16m"; |
4313c744 | 63 | MODULE_PARM_DESC(vram, "Configure VRAM size (for devices without IOMMU/GPUMMU)"); |
871d812a RC |
64 | module_param(vram, charp, 0); |
65 | ||
06d9f56f RC |
66 | bool dumpstate = false; |
67 | MODULE_PARM_DESC(dumpstate, "Dump KMS state on errors"); | |
68 | module_param(dumpstate, bool, 0600); | |
69 | ||
ba4dd718 RC |
70 | static bool modeset = true; |
71 | MODULE_PARM_DESC(modeset, "Use kernel modesetting [KMS] (1=on (default), 0=disable)"); | |
72 | module_param(modeset, bool, 0600); | |
73 | ||
060530f1 RC |
74 | /* |
75 | * Util/helpers: | |
76 | */ | |
77 | ||
8e54eea5 JC |
78 | int msm_clk_bulk_get(struct device *dev, struct clk_bulk_data **bulk) |
79 | { | |
80 | struct property *prop; | |
81 | const char *name; | |
82 | struct clk_bulk_data *local; | |
83 | int i = 0, ret, count; | |
84 | ||
85 | count = of_property_count_strings(dev->of_node, "clock-names"); | |
86 | if (count < 1) | |
87 | return 0; | |
88 | ||
89 | local = devm_kcalloc(dev, sizeof(struct clk_bulk_data *), | |
90 | count, GFP_KERNEL); | |
91 | if (!local) | |
92 | return -ENOMEM; | |
93 | ||
94 | of_property_for_each_string(dev->of_node, "clock-names", prop, name) { | |
95 | local[i].id = devm_kstrdup(dev, name, GFP_KERNEL); | |
96 | if (!local[i].id) { | |
97 | devm_kfree(dev, local); | |
98 | return -ENOMEM; | |
99 | } | |
100 | ||
101 | i++; | |
102 | } | |
103 | ||
104 | ret = devm_clk_bulk_get(dev, count, local); | |
105 | ||
106 | if (ret) { | |
107 | for (i = 0; i < count; i++) | |
108 | devm_kfree(dev, (void *) local[i].id); | |
109 | devm_kfree(dev, local); | |
110 | ||
111 | return ret; | |
112 | } | |
113 | ||
114 | *bulk = local; | |
115 | return count; | |
116 | } | |
117 | ||
118 | struct clk *msm_clk_bulk_get_clock(struct clk_bulk_data *bulk, int count, | |
119 | const char *name) | |
120 | { | |
121 | int i; | |
122 | char n[32]; | |
123 | ||
124 | snprintf(n, sizeof(n), "%s_clk", name); | |
125 | ||
126 | for (i = 0; bulk && i < count; i++) { | |
127 | if (!strcmp(bulk[i].id, name) || !strcmp(bulk[i].id, n)) | |
128 | return bulk[i].clk; | |
129 | } | |
130 | ||
131 | ||
132 | return NULL; | |
133 | } | |
134 | ||
720c3bb8 RC |
135 | struct clk *msm_clk_get(struct platform_device *pdev, const char *name) |
136 | { | |
137 | struct clk *clk; | |
138 | char name2[32]; | |
139 | ||
140 | clk = devm_clk_get(&pdev->dev, name); | |
141 | if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER) | |
142 | return clk; | |
143 | ||
144 | snprintf(name2, sizeof(name2), "%s_clk", name); | |
145 | ||
146 | clk = devm_clk_get(&pdev->dev, name2); | |
147 | if (!IS_ERR(clk)) | |
148 | dev_warn(&pdev->dev, "Using legacy clk name binding. Use " | |
149 | "\"%s\" instead of \"%s\"\n", name, name2); | |
150 | ||
151 | return clk; | |
152 | } | |
153 | ||
c8afe684 RC |
154 | void __iomem *msm_ioremap(struct platform_device *pdev, const char *name, |
155 | const char *dbgname) | |
156 | { | |
157 | struct resource *res; | |
158 | unsigned long size; | |
159 | void __iomem *ptr; | |
160 | ||
161 | if (name) | |
162 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); | |
163 | else | |
164 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
165 | ||
166 | if (!res) { | |
6a41da17 | 167 | DRM_DEV_ERROR(&pdev->dev, "failed to get memory resource: %s\n", name); |
c8afe684 RC |
168 | return ERR_PTR(-EINVAL); |
169 | } | |
170 | ||
171 | size = resource_size(res); | |
172 | ||
173 | ptr = devm_ioremap_nocache(&pdev->dev, res->start, size); | |
174 | if (!ptr) { | |
6a41da17 | 175 | DRM_DEV_ERROR(&pdev->dev, "failed to ioremap: %s\n", name); |
c8afe684 RC |
176 | return ERR_PTR(-ENOMEM); |
177 | } | |
178 | ||
179 | if (reglog) | |
fc99f97a | 180 | printk(KERN_DEBUG "IO:region %s %p %08lx\n", dbgname, ptr, size); |
c8afe684 RC |
181 | |
182 | return ptr; | |
183 | } | |
184 | ||
185 | void msm_writel(u32 data, void __iomem *addr) | |
186 | { | |
187 | if (reglog) | |
fc99f97a | 188 | printk(KERN_DEBUG "IO:W %p %08x\n", addr, data); |
c8afe684 RC |
189 | writel(data, addr); |
190 | } | |
191 | ||
192 | u32 msm_readl(const void __iomem *addr) | |
193 | { | |
194 | u32 val = readl(addr); | |
195 | if (reglog) | |
8dfe162a | 196 | pr_err("IO:R %p %08x\n", addr, val); |
c8afe684 RC |
197 | return val; |
198 | } | |
199 | ||
48d1d28e JS |
200 | struct msm_vblank_work { |
201 | struct work_struct work; | |
78b1d470 HL |
202 | int crtc_id; |
203 | bool enable; | |
48d1d28e | 204 | struct msm_drm_private *priv; |
78b1d470 HL |
205 | }; |
206 | ||
5aeb6656 | 207 | static void vblank_ctrl_worker(struct work_struct *work) |
78b1d470 | 208 | { |
48d1d28e JS |
209 | struct msm_vblank_work *vbl_work = container_of(work, |
210 | struct msm_vblank_work, work); | |
211 | struct msm_drm_private *priv = vbl_work->priv; | |
78b1d470 | 212 | struct msm_kms *kms = priv->kms; |
78b1d470 | 213 | |
48d1d28e JS |
214 | if (vbl_work->enable) |
215 | kms->funcs->enable_vblank(kms, priv->crtcs[vbl_work->crtc_id]); | |
216 | else | |
217 | kms->funcs->disable_vblank(kms, priv->crtcs[vbl_work->crtc_id]); | |
78b1d470 | 218 | |
48d1d28e | 219 | kfree(vbl_work); |
78b1d470 HL |
220 | } |
221 | ||
222 | static int vblank_ctrl_queue_work(struct msm_drm_private *priv, | |
223 | int crtc_id, bool enable) | |
224 | { | |
48d1d28e | 225 | struct msm_vblank_work *vbl_work; |
78b1d470 | 226 | |
48d1d28e JS |
227 | vbl_work = kzalloc(sizeof(*vbl_work), GFP_ATOMIC); |
228 | if (!vbl_work) | |
78b1d470 HL |
229 | return -ENOMEM; |
230 | ||
48d1d28e | 231 | INIT_WORK(&vbl_work->work, vblank_ctrl_worker); |
78b1d470 | 232 | |
48d1d28e JS |
233 | vbl_work->crtc_id = crtc_id; |
234 | vbl_work->enable = enable; | |
235 | vbl_work->priv = priv; | |
78b1d470 | 236 | |
48d1d28e | 237 | queue_work(priv->wq, &vbl_work->work); |
78b1d470 HL |
238 | |
239 | return 0; | |
240 | } | |
241 | ||
2b669875 | 242 | static int msm_drm_uninit(struct device *dev) |
c8afe684 | 243 | { |
2b669875 AT |
244 | struct platform_device *pdev = to_platform_device(dev); |
245 | struct drm_device *ddev = platform_get_drvdata(pdev); | |
246 | struct msm_drm_private *priv = ddev->dev_private; | |
c8afe684 | 247 | struct msm_kms *kms = priv->kms; |
bc3220be | 248 | struct msm_mdss *mdss = priv->mdss; |
25fdd593 | 249 | int i; |
78b1d470 | 250 | |
2aa31767 SP |
251 | /* |
252 | * Shutdown the hw if we're far enough along where things might be on. | |
253 | * If we run this too early, we'll end up panicking in any variety of | |
254 | * places. Since we don't register the drm device until late in | |
255 | * msm_drm_init, drm_dev->registered is used as an indicator that the | |
256 | * shutdown will be successful. | |
257 | */ | |
258 | if (ddev->registered) { | |
259 | drm_dev_unregister(ddev); | |
260 | drm_atomic_helper_shutdown(ddev); | |
261 | } | |
262 | ||
78b1d470 HL |
263 | /* We must cancel and cleanup any pending vblank enable/disable |
264 | * work before drm_irq_uninstall() to avoid work re-enabling an | |
265 | * irq after uninstall has disabled it. | |
266 | */ | |
c8afe684 | 267 | |
48d1d28e | 268 | flush_workqueue(priv->wq); |
25fdd593 | 269 | |
d9db30ce | 270 | /* clean up event worker threads */ |
25fdd593 | 271 | for (i = 0; i < priv->num_crtcs; i++) { |
25fdd593 | 272 | if (priv->event_thread[i].thread) { |
3c125682 | 273 | kthread_destroy_worker(&priv->event_thread[i].worker); |
25fdd593 JS |
274 | priv->event_thread[i].thread = NULL; |
275 | } | |
276 | } | |
277 | ||
68209390 RC |
278 | msm_gem_shrinker_cleanup(ddev); |
279 | ||
2b669875 AT |
280 | drm_kms_helper_poll_fini(ddev); |
281 | ||
85eac470 NT |
282 | msm_perf_debugfs_cleanup(priv); |
283 | msm_rd_debugfs_cleanup(priv); | |
284 | ||
1aaa57f5 AT |
285 | #ifdef CONFIG_DRM_FBDEV_EMULATION |
286 | if (fbdev && priv->fbdev) | |
2b669875 | 287 | msm_fbdev_free(ddev); |
1aaa57f5 | 288 | #endif |
2aa31767 | 289 | |
2b669875 | 290 | drm_mode_config_cleanup(ddev); |
c8afe684 | 291 | |
2b669875 AT |
292 | pm_runtime_get_sync(dev); |
293 | drm_irq_uninstall(ddev); | |
294 | pm_runtime_put_sync(dev); | |
c8afe684 | 295 | |
16976085 | 296 | if (kms && kms->funcs) |
c8afe684 | 297 | kms->funcs->destroy(kms); |
c8afe684 | 298 | |
871d812a | 299 | if (priv->vram.paddr) { |
00085f1e | 300 | unsigned long attrs = DMA_ATTR_NO_KERNEL_MAPPING; |
871d812a | 301 | drm_mm_takedown(&priv->vram.mm); |
2b669875 | 302 | dma_free_attrs(dev, priv->vram.size, NULL, |
00085f1e | 303 | priv->vram.paddr, attrs); |
871d812a RC |
304 | } |
305 | ||
2b669875 | 306 | component_unbind_all(dev, ddev); |
060530f1 | 307 | |
bc3220be RY |
308 | if (mdss && mdss->funcs) |
309 | mdss->funcs->destroy(ddev); | |
0a6030d2 | 310 | |
2b669875 | 311 | ddev->dev_private = NULL; |
4d8dc2df | 312 | drm_dev_put(ddev); |
c8afe684 | 313 | |
2aa31767 | 314 | destroy_workqueue(priv->wq); |
c8afe684 RC |
315 | kfree(priv); |
316 | ||
317 | return 0; | |
318 | } | |
319 | ||
aaded2e3 JS |
320 | #define KMS_MDP4 4 |
321 | #define KMS_MDP5 5 | |
25fdd593 | 322 | #define KMS_DPU 3 |
aaded2e3 | 323 | |
06c0dd96 RC |
324 | static int get_mdp_ver(struct platform_device *pdev) |
325 | { | |
06c0dd96 | 326 | struct device *dev = &pdev->dev; |
e9fbdaf2 AT |
327 | |
328 | return (int) (unsigned long) of_device_get_match_data(dev); | |
06c0dd96 RC |
329 | } |
330 | ||
072f1f91 RC |
331 | #include <linux/of_address.h> |
332 | ||
c2052a4e JM |
333 | bool msm_use_mmu(struct drm_device *dev) |
334 | { | |
335 | struct msm_drm_private *priv = dev->dev_private; | |
336 | ||
337 | /* a2xx comes with its own MMU */ | |
338 | return priv->is_a2xx || iommu_present(&platform_bus_type); | |
339 | } | |
340 | ||
5bf9c0b6 | 341 | static int msm_init_vram(struct drm_device *dev) |
c8afe684 | 342 | { |
5bf9c0b6 | 343 | struct msm_drm_private *priv = dev->dev_private; |
e9fbdaf2 | 344 | struct device_node *node; |
072f1f91 RC |
345 | unsigned long size = 0; |
346 | int ret = 0; | |
347 | ||
072f1f91 RC |
348 | /* In the device-tree world, we could have a 'memory-region' |
349 | * phandle, which gives us a link to our "vram". Allocating | |
350 | * is all nicely abstracted behind the dma api, but we need | |
351 | * to know the entire size to allocate it all in one go. There | |
352 | * are two cases: | |
353 | * 1) device with no IOMMU, in which case we need exclusive | |
354 | * access to a VRAM carveout big enough for all gpu | |
355 | * buffers | |
356 | * 2) device with IOMMU, but where the bootloader puts up | |
357 | * a splash screen. In this case, the VRAM carveout | |
358 | * need only be large enough for fbdev fb. But we need | |
359 | * exclusive access to the buffer to avoid the kernel | |
360 | * using those pages for other purposes (which appears | |
361 | * as corruption on screen before we have a chance to | |
362 | * load and do initial modeset) | |
363 | */ | |
072f1f91 RC |
364 | |
365 | node = of_parse_phandle(dev->dev->of_node, "memory-region", 0); | |
366 | if (node) { | |
367 | struct resource r; | |
368 | ret = of_address_to_resource(node, 0, &r); | |
2ca41c17 | 369 | of_node_put(node); |
072f1f91 RC |
370 | if (ret) |
371 | return ret; | |
372 | size = r.end - r.start; | |
fc99f97a | 373 | DRM_INFO("using VRAM carveout: %lx@%pa\n", size, &r.start); |
c8afe684 | 374 | |
e9fbdaf2 AT |
375 | /* if we have no IOMMU, then we need to use carveout allocator. |
376 | * Grab the entire CMA chunk carved out in early startup in | |
377 | * mach-msm: | |
378 | */ | |
c2052a4e | 379 | } else if (!msm_use_mmu(dev)) { |
072f1f91 RC |
380 | DRM_INFO("using %s VRAM carveout\n", vram); |
381 | size = memparse(vram, NULL); | |
382 | } | |
383 | ||
384 | if (size) { | |
00085f1e | 385 | unsigned long attrs = 0; |
871d812a RC |
386 | void *p; |
387 | ||
871d812a RC |
388 | priv->vram.size = size; |
389 | ||
390 | drm_mm_init(&priv->vram.mm, 0, (size >> PAGE_SHIFT) - 1); | |
0e08270a | 391 | spin_lock_init(&priv->vram.lock); |
871d812a | 392 | |
00085f1e KK |
393 | attrs |= DMA_ATTR_NO_KERNEL_MAPPING; |
394 | attrs |= DMA_ATTR_WRITE_COMBINE; | |
871d812a RC |
395 | |
396 | /* note that for no-kernel-mapping, the vaddr returned | |
397 | * is bogus, but non-null if allocation succeeded: | |
398 | */ | |
399 | p = dma_alloc_attrs(dev->dev, size, | |
00085f1e | 400 | &priv->vram.paddr, GFP_KERNEL, attrs); |
871d812a | 401 | if (!p) { |
6a41da17 | 402 | DRM_DEV_ERROR(dev->dev, "failed to allocate VRAM\n"); |
871d812a | 403 | priv->vram.paddr = 0; |
5bf9c0b6 | 404 | return -ENOMEM; |
871d812a RC |
405 | } |
406 | ||
6a41da17 | 407 | DRM_DEV_INFO(dev->dev, "VRAM: %08x->%08x\n", |
871d812a RC |
408 | (uint32_t)priv->vram.paddr, |
409 | (uint32_t)(priv->vram.paddr + size)); | |
410 | } | |
411 | ||
072f1f91 | 412 | return ret; |
5bf9c0b6 RC |
413 | } |
414 | ||
2b669875 | 415 | static int msm_drm_init(struct device *dev, struct drm_driver *drv) |
5bf9c0b6 | 416 | { |
2b669875 AT |
417 | struct platform_device *pdev = to_platform_device(dev); |
418 | struct drm_device *ddev; | |
5bf9c0b6 RC |
419 | struct msm_drm_private *priv; |
420 | struct msm_kms *kms; | |
bc3220be | 421 | struct msm_mdss *mdss; |
25fdd593 JS |
422 | int ret, i; |
423 | struct sched_param param; | |
5bf9c0b6 | 424 | |
2b669875 | 425 | ddev = drm_dev_alloc(drv, dev); |
0f288605 | 426 | if (IS_ERR(ddev)) { |
6a41da17 | 427 | DRM_DEV_ERROR(dev, "failed to allocate drm_device\n"); |
0f288605 | 428 | return PTR_ERR(ddev); |
2b669875 AT |
429 | } |
430 | ||
431 | platform_set_drvdata(pdev, ddev); | |
2b669875 | 432 | |
5bf9c0b6 RC |
433 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
434 | if (!priv) { | |
77050c3f | 435 | ret = -ENOMEM; |
4d8dc2df | 436 | goto err_put_drm_dev; |
5bf9c0b6 RC |
437 | } |
438 | ||
2b669875 | 439 | ddev->dev_private = priv; |
68209390 | 440 | priv->dev = ddev; |
5bf9c0b6 | 441 | |
25fdd593 JS |
442 | switch (get_mdp_ver(pdev)) { |
443 | case KMS_MDP5: | |
444 | ret = mdp5_mdss_init(ddev); | |
445 | break; | |
446 | case KMS_DPU: | |
447 | ret = dpu_mdss_init(ddev); | |
448 | break; | |
449 | default: | |
450 | ret = 0; | |
451 | break; | |
452 | } | |
77050c3f JS |
453 | if (ret) |
454 | goto err_free_priv; | |
0a6030d2 | 455 | |
bc3220be RY |
456 | mdss = priv->mdss; |
457 | ||
5bf9c0b6 | 458 | priv->wq = alloc_ordered_workqueue("msm", 0); |
5bf9c0b6 | 459 | |
48e7f183 KK |
460 | INIT_WORK(&priv->free_work, msm_gem_free_work); |
461 | init_llist_head(&priv->free_list); | |
462 | ||
5bf9c0b6 | 463 | INIT_LIST_HEAD(&priv->inactive_list); |
5bf9c0b6 | 464 | |
2b669875 | 465 | drm_mode_config_init(ddev); |
060530f1 RC |
466 | |
467 | /* Bind all our sub-components: */ | |
2b669875 | 468 | ret = component_bind_all(dev, ddev); |
77050c3f JS |
469 | if (ret) |
470 | goto err_destroy_mdss; | |
060530f1 | 471 | |
2b669875 | 472 | ret = msm_init_vram(ddev); |
13f15565 | 473 | if (ret) |
77050c3f | 474 | goto err_msm_uninit; |
13f15565 | 475 | |
68209390 RC |
476 | msm_gem_shrinker_init(ddev); |
477 | ||
06c0dd96 | 478 | switch (get_mdp_ver(pdev)) { |
aaded2e3 | 479 | case KMS_MDP4: |
2b669875 | 480 | kms = mdp4_kms_init(ddev); |
0a6030d2 | 481 | priv->kms = kms; |
06c0dd96 | 482 | break; |
aaded2e3 | 483 | case KMS_MDP5: |
392ae6e0 | 484 | kms = mdp5_kms_init(ddev); |
06c0dd96 | 485 | break; |
25fdd593 JS |
486 | case KMS_DPU: |
487 | kms = dpu_kms_init(ddev); | |
488 | priv->kms = kms; | |
489 | break; | |
06c0dd96 | 490 | default: |
e6f6d63e JM |
491 | /* valid only for the dummy headless case, where of_node=NULL */ |
492 | WARN_ON(dev->of_node); | |
493 | kms = NULL; | |
06c0dd96 RC |
494 | break; |
495 | } | |
496 | ||
c8afe684 | 497 | if (IS_ERR(kms)) { |
6a41da17 | 498 | DRM_DEV_ERROR(dev, "failed to load kms\n"); |
e4826a94 | 499 | ret = PTR_ERR(kms); |
b2ccfdf1 | 500 | priv->kms = NULL; |
77050c3f | 501 | goto err_msm_uninit; |
c8afe684 RC |
502 | } |
503 | ||
bb676df1 JS |
504 | /* Enable normalization of plane zpos */ |
505 | ddev->mode_config.normalize_zpos = true; | |
506 | ||
c8afe684 | 507 | if (kms) { |
c8afe684 RC |
508 | ret = kms->funcs->hw_init(kms); |
509 | if (ret) { | |
6a41da17 | 510 | DRM_DEV_ERROR(dev, "kms hw init failed: %d\n", ret); |
77050c3f | 511 | goto err_msm_uninit; |
c8afe684 RC |
512 | } |
513 | } | |
514 | ||
2b669875 | 515 | ddev->mode_config.funcs = &mode_config_funcs; |
d14659f5 | 516 | ddev->mode_config.helper_private = &mode_config_helper_funcs; |
c8afe684 | 517 | |
25fdd593 JS |
518 | /** |
519 | * this priority was found during empiric testing to have appropriate | |
520 | * realtime scheduling to process display updates and interact with | |
521 | * other real time and normal priority task | |
522 | */ | |
523 | param.sched_priority = 16; | |
524 | for (i = 0; i < priv->num_crtcs; i++) { | |
25fdd593 JS |
525 | /* initialize event thread */ |
526 | priv->event_thread[i].crtc_id = priv->crtcs[i]->base.id; | |
527 | kthread_init_worker(&priv->event_thread[i].worker); | |
528 | priv->event_thread[i].dev = ddev; | |
529 | priv->event_thread[i].thread = | |
530 | kthread_run(kthread_worker_fn, | |
531 | &priv->event_thread[i].worker, | |
532 | "crtc_event:%d", priv->event_thread[i].crtc_id); | |
7f9743ab | 533 | if (IS_ERR(priv->event_thread[i].thread)) { |
4971f090 | 534 | DRM_DEV_ERROR(dev, "failed to create crtc_event kthread\n"); |
7f9743ab JS |
535 | priv->event_thread[i].thread = NULL; |
536 | goto err_msm_uninit; | |
537 | } | |
538 | ||
25fdd593 | 539 | ret = sched_setscheduler(priv->event_thread[i].thread, |
7f9743ab | 540 | SCHED_FIFO, ¶m); |
25fdd593 | 541 | if (ret) |
7f9743ab JS |
542 | dev_warn(dev, "event_thread set priority failed:%d\n", |
543 | ret); | |
25fdd593 JS |
544 | } |
545 | ||
2b669875 | 546 | ret = drm_vblank_init(ddev, priv->num_crtcs); |
c8afe684 | 547 | if (ret < 0) { |
6a41da17 | 548 | DRM_DEV_ERROR(dev, "failed to initialize vblank\n"); |
77050c3f | 549 | goto err_msm_uninit; |
c8afe684 RC |
550 | } |
551 | ||
a2b3a557 AT |
552 | if (kms) { |
553 | pm_runtime_get_sync(dev); | |
554 | ret = drm_irq_install(ddev, kms->irq); | |
555 | pm_runtime_put_sync(dev); | |
556 | if (ret < 0) { | |
6a41da17 | 557 | DRM_DEV_ERROR(dev, "failed to install IRQ handler\n"); |
77050c3f | 558 | goto err_msm_uninit; |
a2b3a557 | 559 | } |
c8afe684 RC |
560 | } |
561 | ||
2b669875 AT |
562 | ret = drm_dev_register(ddev, 0); |
563 | if (ret) | |
77050c3f | 564 | goto err_msm_uninit; |
2b669875 | 565 | |
2b669875 | 566 | drm_mode_config_reset(ddev); |
cf3a7e4c | 567 | |
a9ee34b7 | 568 | #ifdef CONFIG_DRM_FBDEV_EMULATION |
e6f6d63e | 569 | if (kms && fbdev) |
2b669875 | 570 | priv->fbdev = msm_fbdev_init(ddev); |
c8afe684 RC |
571 | #endif |
572 | ||
2b669875 | 573 | ret = msm_debugfs_late_init(ddev); |
a7d3c950 | 574 | if (ret) |
77050c3f | 575 | goto err_msm_uninit; |
a7d3c950 | 576 | |
2b669875 | 577 | drm_kms_helper_poll_init(ddev); |
c8afe684 RC |
578 | |
579 | return 0; | |
580 | ||
77050c3f | 581 | err_msm_uninit: |
2b669875 | 582 | msm_drm_uninit(dev); |
c8afe684 | 583 | return ret; |
77050c3f JS |
584 | err_destroy_mdss: |
585 | if (mdss && mdss->funcs) | |
586 | mdss->funcs->destroy(ddev); | |
587 | err_free_priv: | |
588 | kfree(priv); | |
4d8dc2df TZ |
589 | err_put_drm_dev: |
590 | drm_dev_put(ddev); | |
77050c3f | 591 | return ret; |
c8afe684 RC |
592 | } |
593 | ||
2b669875 AT |
594 | /* |
595 | * DRM operations: | |
596 | */ | |
597 | ||
7198e6b0 RC |
598 | static void load_gpu(struct drm_device *dev) |
599 | { | |
a1ad3523 | 600 | static DEFINE_MUTEX(init_lock); |
7198e6b0 | 601 | struct msm_drm_private *priv = dev->dev_private; |
7198e6b0 | 602 | |
a1ad3523 RC |
603 | mutex_lock(&init_lock); |
604 | ||
e2550b7a RC |
605 | if (!priv->gpu) |
606 | priv->gpu = adreno_load_gpu(dev); | |
7198e6b0 | 607 | |
a1ad3523 | 608 | mutex_unlock(&init_lock); |
7198e6b0 RC |
609 | } |
610 | ||
f97decac | 611 | static int context_init(struct drm_device *dev, struct drm_file *file) |
7198e6b0 | 612 | { |
295b22ae | 613 | struct msm_drm_private *priv = dev->dev_private; |
7198e6b0 RC |
614 | struct msm_file_private *ctx; |
615 | ||
7198e6b0 RC |
616 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); |
617 | if (!ctx) | |
618 | return -ENOMEM; | |
619 | ||
f97decac | 620 | msm_submitqueue_init(dev, ctx); |
f7de1545 | 621 | |
7af5cdb1 | 622 | ctx->aspace = priv->gpu ? priv->gpu->aspace : NULL; |
7198e6b0 RC |
623 | file->driver_priv = ctx; |
624 | ||
625 | return 0; | |
626 | } | |
627 | ||
f7de1545 JC |
628 | static int msm_open(struct drm_device *dev, struct drm_file *file) |
629 | { | |
630 | /* For now, load gpu on open.. to avoid the requirement of having | |
631 | * firmware in the initrd. | |
632 | */ | |
633 | load_gpu(dev); | |
634 | ||
f97decac | 635 | return context_init(dev, file); |
f7de1545 JC |
636 | } |
637 | ||
638 | static void context_close(struct msm_file_private *ctx) | |
639 | { | |
640 | msm_submitqueue_close(ctx); | |
641 | kfree(ctx); | |
642 | } | |
643 | ||
94df145c | 644 | static void msm_postclose(struct drm_device *dev, struct drm_file *file) |
c8afe684 RC |
645 | { |
646 | struct msm_drm_private *priv = dev->dev_private; | |
7198e6b0 | 647 | struct msm_file_private *ctx = file->driver_priv; |
7198e6b0 | 648 | |
7198e6b0 RC |
649 | mutex_lock(&dev->struct_mutex); |
650 | if (ctx == priv->lastctx) | |
651 | priv->lastctx = NULL; | |
652 | mutex_unlock(&dev->struct_mutex); | |
653 | ||
f7de1545 | 654 | context_close(ctx); |
c8afe684 RC |
655 | } |
656 | ||
e9f0d76f | 657 | static irqreturn_t msm_irq(int irq, void *arg) |
c8afe684 RC |
658 | { |
659 | struct drm_device *dev = arg; | |
660 | struct msm_drm_private *priv = dev->dev_private; | |
661 | struct msm_kms *kms = priv->kms; | |
662 | BUG_ON(!kms); | |
663 | return kms->funcs->irq(kms); | |
664 | } | |
665 | ||
666 | static void msm_irq_preinstall(struct drm_device *dev) | |
667 | { | |
668 | struct msm_drm_private *priv = dev->dev_private; | |
669 | struct msm_kms *kms = priv->kms; | |
670 | BUG_ON(!kms); | |
671 | kms->funcs->irq_preinstall(kms); | |
672 | } | |
673 | ||
674 | static int msm_irq_postinstall(struct drm_device *dev) | |
675 | { | |
676 | struct msm_drm_private *priv = dev->dev_private; | |
677 | struct msm_kms *kms = priv->kms; | |
678 | BUG_ON(!kms); | |
ab07e0c1 JC |
679 | |
680 | if (kms->funcs->irq_postinstall) | |
681 | return kms->funcs->irq_postinstall(kms); | |
682 | ||
683 | return 0; | |
c8afe684 RC |
684 | } |
685 | ||
686 | static void msm_irq_uninstall(struct drm_device *dev) | |
687 | { | |
688 | struct msm_drm_private *priv = dev->dev_private; | |
689 | struct msm_kms *kms = priv->kms; | |
690 | BUG_ON(!kms); | |
691 | kms->funcs->irq_uninstall(kms); | |
692 | } | |
693 | ||
88e72717 | 694 | static int msm_enable_vblank(struct drm_device *dev, unsigned int pipe) |
c8afe684 RC |
695 | { |
696 | struct msm_drm_private *priv = dev->dev_private; | |
697 | struct msm_kms *kms = priv->kms; | |
698 | if (!kms) | |
699 | return -ENXIO; | |
88e72717 TR |
700 | DBG("dev=%p, crtc=%u", dev, pipe); |
701 | return vblank_ctrl_queue_work(priv, pipe, true); | |
c8afe684 RC |
702 | } |
703 | ||
88e72717 | 704 | static void msm_disable_vblank(struct drm_device *dev, unsigned int pipe) |
c8afe684 RC |
705 | { |
706 | struct msm_drm_private *priv = dev->dev_private; | |
707 | struct msm_kms *kms = priv->kms; | |
708 | if (!kms) | |
709 | return; | |
88e72717 TR |
710 | DBG("dev=%p, crtc=%u", dev, pipe); |
711 | vblank_ctrl_queue_work(priv, pipe, false); | |
c8afe684 RC |
712 | } |
713 | ||
7198e6b0 RC |
714 | /* |
715 | * DRM ioctls: | |
716 | */ | |
717 | ||
718 | static int msm_ioctl_get_param(struct drm_device *dev, void *data, | |
719 | struct drm_file *file) | |
720 | { | |
721 | struct msm_drm_private *priv = dev->dev_private; | |
722 | struct drm_msm_param *args = data; | |
723 | struct msm_gpu *gpu; | |
724 | ||
725 | /* for now, we just have 3d pipe.. eventually this would need to | |
726 | * be more clever to dispatch to appropriate gpu module: | |
727 | */ | |
728 | if (args->pipe != MSM_PIPE_3D0) | |
729 | return -EINVAL; | |
730 | ||
731 | gpu = priv->gpu; | |
732 | ||
733 | if (!gpu) | |
734 | return -ENXIO; | |
735 | ||
736 | return gpu->funcs->get_param(gpu, args->param, &args->value); | |
737 | } | |
738 | ||
739 | static int msm_ioctl_gem_new(struct drm_device *dev, void *data, | |
740 | struct drm_file *file) | |
741 | { | |
742 | struct drm_msm_gem_new *args = data; | |
93ddb0d3 RC |
743 | |
744 | if (args->flags & ~MSM_BO_FLAGS) { | |
745 | DRM_ERROR("invalid flags: %08x\n", args->flags); | |
746 | return -EINVAL; | |
747 | } | |
748 | ||
7198e6b0 | 749 | return msm_gem_new_handle(dev, file, args->size, |
0815d774 | 750 | args->flags, &args->handle, NULL); |
7198e6b0 RC |
751 | } |
752 | ||
56c2da83 RC |
753 | static inline ktime_t to_ktime(struct drm_msm_timespec timeout) |
754 | { | |
755 | return ktime_set(timeout.tv_sec, timeout.tv_nsec); | |
756 | } | |
7198e6b0 RC |
757 | |
758 | static int msm_ioctl_gem_cpu_prep(struct drm_device *dev, void *data, | |
759 | struct drm_file *file) | |
760 | { | |
761 | struct drm_msm_gem_cpu_prep *args = data; | |
762 | struct drm_gem_object *obj; | |
56c2da83 | 763 | ktime_t timeout = to_ktime(args->timeout); |
7198e6b0 RC |
764 | int ret; |
765 | ||
93ddb0d3 RC |
766 | if (args->op & ~MSM_PREP_FLAGS) { |
767 | DRM_ERROR("invalid op: %08x\n", args->op); | |
768 | return -EINVAL; | |
769 | } | |
770 | ||
a8ad0bd8 | 771 | obj = drm_gem_object_lookup(file, args->handle); |
7198e6b0 RC |
772 | if (!obj) |
773 | return -ENOENT; | |
774 | ||
56c2da83 | 775 | ret = msm_gem_cpu_prep(obj, args->op, &timeout); |
7198e6b0 | 776 | |
dc9a9b32 | 777 | drm_gem_object_put_unlocked(obj); |
7198e6b0 RC |
778 | |
779 | return ret; | |
780 | } | |
781 | ||
782 | static int msm_ioctl_gem_cpu_fini(struct drm_device *dev, void *data, | |
783 | struct drm_file *file) | |
784 | { | |
785 | struct drm_msm_gem_cpu_fini *args = data; | |
786 | struct drm_gem_object *obj; | |
787 | int ret; | |
788 | ||
a8ad0bd8 | 789 | obj = drm_gem_object_lookup(file, args->handle); |
7198e6b0 RC |
790 | if (!obj) |
791 | return -ENOENT; | |
792 | ||
793 | ret = msm_gem_cpu_fini(obj); | |
794 | ||
dc9a9b32 | 795 | drm_gem_object_put_unlocked(obj); |
7198e6b0 RC |
796 | |
797 | return ret; | |
798 | } | |
799 | ||
49fd08ba JC |
800 | static int msm_ioctl_gem_info_iova(struct drm_device *dev, |
801 | struct drm_gem_object *obj, uint64_t *iova) | |
802 | { | |
803 | struct msm_drm_private *priv = dev->dev_private; | |
804 | ||
805 | if (!priv->gpu) | |
806 | return -EINVAL; | |
807 | ||
9fe041f6 JC |
808 | /* |
809 | * Don't pin the memory here - just get an address so that userspace can | |
810 | * be productive | |
811 | */ | |
8bdcd949 | 812 | return msm_gem_get_iova(obj, priv->gpu->aspace, iova); |
49fd08ba JC |
813 | } |
814 | ||
7198e6b0 RC |
815 | static int msm_ioctl_gem_info(struct drm_device *dev, void *data, |
816 | struct drm_file *file) | |
817 | { | |
818 | struct drm_msm_gem_info *args = data; | |
819 | struct drm_gem_object *obj; | |
f05c83e7 RC |
820 | struct msm_gem_object *msm_obj; |
821 | int i, ret = 0; | |
7198e6b0 | 822 | |
789d2e5a | 823 | if (args->pad) |
7198e6b0 RC |
824 | return -EINVAL; |
825 | ||
789d2e5a RC |
826 | switch (args->info) { |
827 | case MSM_INFO_GET_OFFSET: | |
828 | case MSM_INFO_GET_IOVA: | |
829 | /* value returned as immediate, not pointer, so len==0: */ | |
830 | if (args->len) | |
831 | return -EINVAL; | |
832 | break; | |
f05c83e7 RC |
833 | case MSM_INFO_SET_NAME: |
834 | case MSM_INFO_GET_NAME: | |
835 | break; | |
789d2e5a | 836 | default: |
7198e6b0 | 837 | return -EINVAL; |
789d2e5a | 838 | } |
7198e6b0 | 839 | |
a8ad0bd8 | 840 | obj = drm_gem_object_lookup(file, args->handle); |
7198e6b0 RC |
841 | if (!obj) |
842 | return -ENOENT; | |
843 | ||
f05c83e7 | 844 | msm_obj = to_msm_bo(obj); |
49fd08ba | 845 | |
789d2e5a RC |
846 | switch (args->info) { |
847 | case MSM_INFO_GET_OFFSET: | |
848 | args->value = msm_gem_mmap_offset(obj); | |
849 | break; | |
850 | case MSM_INFO_GET_IOVA: | |
851 | ret = msm_ioctl_gem_info_iova(dev, obj, &args->value); | |
852 | break; | |
f05c83e7 RC |
853 | case MSM_INFO_SET_NAME: |
854 | /* length check should leave room for terminating null: */ | |
855 | if (args->len >= sizeof(msm_obj->name)) { | |
856 | ret = -EINVAL; | |
857 | break; | |
858 | } | |
7cce8e4e | 859 | if (copy_from_user(msm_obj->name, u64_to_user_ptr(args->value), |
860433ed JC |
860 | args->len)) { |
861 | msm_obj->name[0] = '\0'; | |
7cce8e4e | 862 | ret = -EFAULT; |
860433ed JC |
863 | break; |
864 | } | |
f05c83e7 RC |
865 | msm_obj->name[args->len] = '\0'; |
866 | for (i = 0; i < args->len; i++) { | |
867 | if (!isprint(msm_obj->name[i])) { | |
868 | msm_obj->name[i] = '\0'; | |
869 | break; | |
870 | } | |
871 | } | |
872 | break; | |
873 | case MSM_INFO_GET_NAME: | |
874 | if (args->value && (args->len < strlen(msm_obj->name))) { | |
875 | ret = -EINVAL; | |
876 | break; | |
877 | } | |
878 | args->len = strlen(msm_obj->name); | |
879 | if (args->value) { | |
7cce8e4e DC |
880 | if (copy_to_user(u64_to_user_ptr(args->value), |
881 | msm_obj->name, args->len)) | |
882 | ret = -EFAULT; | |
f05c83e7 RC |
883 | } |
884 | break; | |
49fd08ba | 885 | } |
7198e6b0 | 886 | |
dc9a9b32 | 887 | drm_gem_object_put_unlocked(obj); |
7198e6b0 RC |
888 | |
889 | return ret; | |
890 | } | |
891 | ||
892 | static int msm_ioctl_wait_fence(struct drm_device *dev, void *data, | |
893 | struct drm_file *file) | |
894 | { | |
ca762a8a | 895 | struct msm_drm_private *priv = dev->dev_private; |
7198e6b0 | 896 | struct drm_msm_wait_fence *args = data; |
56c2da83 | 897 | ktime_t timeout = to_ktime(args->timeout); |
f97decac JC |
898 | struct msm_gpu_submitqueue *queue; |
899 | struct msm_gpu *gpu = priv->gpu; | |
900 | int ret; | |
93ddb0d3 RC |
901 | |
902 | if (args->pad) { | |
903 | DRM_ERROR("invalid pad: %08x\n", args->pad); | |
904 | return -EINVAL; | |
905 | } | |
906 | ||
f97decac | 907 | if (!gpu) |
ca762a8a RC |
908 | return 0; |
909 | ||
f97decac JC |
910 | queue = msm_submitqueue_get(file->driver_priv, args->queueid); |
911 | if (!queue) | |
912 | return -ENOENT; | |
913 | ||
914 | ret = msm_wait_fence(gpu->rb[queue->prio]->fctx, args->fence, &timeout, | |
915 | true); | |
916 | ||
917 | msm_submitqueue_put(queue); | |
918 | return ret; | |
7198e6b0 RC |
919 | } |
920 | ||
4cd33c48 RC |
921 | static int msm_ioctl_gem_madvise(struct drm_device *dev, void *data, |
922 | struct drm_file *file) | |
923 | { | |
924 | struct drm_msm_gem_madvise *args = data; | |
925 | struct drm_gem_object *obj; | |
926 | int ret; | |
927 | ||
928 | switch (args->madv) { | |
929 | case MSM_MADV_DONTNEED: | |
930 | case MSM_MADV_WILLNEED: | |
931 | break; | |
932 | default: | |
933 | return -EINVAL; | |
934 | } | |
935 | ||
936 | ret = mutex_lock_interruptible(&dev->struct_mutex); | |
937 | if (ret) | |
938 | return ret; | |
939 | ||
940 | obj = drm_gem_object_lookup(file, args->handle); | |
941 | if (!obj) { | |
942 | ret = -ENOENT; | |
943 | goto unlock; | |
944 | } | |
945 | ||
946 | ret = msm_gem_madvise(obj, args->madv); | |
947 | if (ret >= 0) { | |
948 | args->retained = ret; | |
949 | ret = 0; | |
950 | } | |
951 | ||
dc9a9b32 | 952 | drm_gem_object_put(obj); |
4cd33c48 RC |
953 | |
954 | unlock: | |
955 | mutex_unlock(&dev->struct_mutex); | |
956 | return ret; | |
957 | } | |
958 | ||
f7de1545 JC |
959 | |
960 | static int msm_ioctl_submitqueue_new(struct drm_device *dev, void *data, | |
961 | struct drm_file *file) | |
962 | { | |
963 | struct drm_msm_submitqueue *args = data; | |
964 | ||
965 | if (args->flags & ~MSM_SUBMITQUEUE_FLAGS) | |
966 | return -EINVAL; | |
967 | ||
f97decac | 968 | return msm_submitqueue_create(dev, file->driver_priv, args->prio, |
f7de1545 JC |
969 | args->flags, &args->id); |
970 | } | |
971 | ||
b0fb6604 JC |
972 | static int msm_ioctl_submitqueue_query(struct drm_device *dev, void *data, |
973 | struct drm_file *file) | |
974 | { | |
975 | return msm_submitqueue_query(dev, file->driver_priv, data); | |
976 | } | |
f7de1545 JC |
977 | |
978 | static int msm_ioctl_submitqueue_close(struct drm_device *dev, void *data, | |
979 | struct drm_file *file) | |
980 | { | |
981 | u32 id = *(u32 *) data; | |
982 | ||
983 | return msm_submitqueue_remove(file->driver_priv, id); | |
984 | } | |
985 | ||
7198e6b0 | 986 | static const struct drm_ioctl_desc msm_ioctls[] = { |
f8c47144 DV |
987 | DRM_IOCTL_DEF_DRV(MSM_GET_PARAM, msm_ioctl_get_param, DRM_AUTH|DRM_RENDER_ALLOW), |
988 | DRM_IOCTL_DEF_DRV(MSM_GEM_NEW, msm_ioctl_gem_new, DRM_AUTH|DRM_RENDER_ALLOW), | |
989 | DRM_IOCTL_DEF_DRV(MSM_GEM_INFO, msm_ioctl_gem_info, DRM_AUTH|DRM_RENDER_ALLOW), | |
990 | DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_PREP, msm_ioctl_gem_cpu_prep, DRM_AUTH|DRM_RENDER_ALLOW), | |
991 | DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_FINI, msm_ioctl_gem_cpu_fini, DRM_AUTH|DRM_RENDER_ALLOW), | |
992 | DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT, msm_ioctl_gem_submit, DRM_AUTH|DRM_RENDER_ALLOW), | |
993 | DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE, msm_ioctl_wait_fence, DRM_AUTH|DRM_RENDER_ALLOW), | |
4cd33c48 | 994 | DRM_IOCTL_DEF_DRV(MSM_GEM_MADVISE, msm_ioctl_gem_madvise, DRM_AUTH|DRM_RENDER_ALLOW), |
f7de1545 JC |
995 | DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_NEW, msm_ioctl_submitqueue_new, DRM_AUTH|DRM_RENDER_ALLOW), |
996 | DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_CLOSE, msm_ioctl_submitqueue_close, DRM_AUTH|DRM_RENDER_ALLOW), | |
b0fb6604 | 997 | DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_QUERY, msm_ioctl_submitqueue_query, DRM_AUTH|DRM_RENDER_ALLOW), |
7198e6b0 RC |
998 | }; |
999 | ||
c8afe684 RC |
1000 | static const struct vm_operations_struct vm_ops = { |
1001 | .fault = msm_gem_fault, | |
1002 | .open = drm_gem_vm_open, | |
1003 | .close = drm_gem_vm_close, | |
1004 | }; | |
1005 | ||
1006 | static const struct file_operations fops = { | |
1007 | .owner = THIS_MODULE, | |
1008 | .open = drm_open, | |
1009 | .release = drm_release, | |
1010 | .unlocked_ioctl = drm_ioctl, | |
c8afe684 | 1011 | .compat_ioctl = drm_compat_ioctl, |
c8afe684 RC |
1012 | .poll = drm_poll, |
1013 | .read = drm_read, | |
1014 | .llseek = no_llseek, | |
1015 | .mmap = msm_gem_mmap, | |
1016 | }; | |
1017 | ||
1018 | static struct drm_driver msm_driver = { | |
5b38e747 | 1019 | .driver_features = DRIVER_GEM | |
05b84911 | 1020 | DRIVER_PRIME | |
b4b15c86 | 1021 | DRIVER_RENDER | |
a5436e1d | 1022 | DRIVER_ATOMIC | |
05b84911 | 1023 | DRIVER_MODESET, |
7198e6b0 | 1024 | .open = msm_open, |
94df145c | 1025 | .postclose = msm_postclose, |
4ccbc6e5 | 1026 | .lastclose = drm_fb_helper_lastclose, |
c8afe684 RC |
1027 | .irq_handler = msm_irq, |
1028 | .irq_preinstall = msm_irq_preinstall, | |
1029 | .irq_postinstall = msm_irq_postinstall, | |
1030 | .irq_uninstall = msm_irq_uninstall, | |
c8afe684 RC |
1031 | .enable_vblank = msm_enable_vblank, |
1032 | .disable_vblank = msm_disable_vblank, | |
48e7f183 | 1033 | .gem_free_object_unlocked = msm_gem_free_object, |
c8afe684 RC |
1034 | .gem_vm_ops = &vm_ops, |
1035 | .dumb_create = msm_gem_dumb_create, | |
1036 | .dumb_map_offset = msm_gem_dumb_map_offset, | |
05b84911 RC |
1037 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, |
1038 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | |
1039 | .gem_prime_export = drm_gem_prime_export, | |
1040 | .gem_prime_import = drm_gem_prime_import, | |
1041 | .gem_prime_pin = msm_gem_prime_pin, | |
1042 | .gem_prime_unpin = msm_gem_prime_unpin, | |
1043 | .gem_prime_get_sg_table = msm_gem_prime_get_sg_table, | |
1044 | .gem_prime_import_sg_table = msm_gem_prime_import_sg_table, | |
1045 | .gem_prime_vmap = msm_gem_prime_vmap, | |
1046 | .gem_prime_vunmap = msm_gem_prime_vunmap, | |
77a147e7 | 1047 | .gem_prime_mmap = msm_gem_prime_mmap, |
c8afe684 RC |
1048 | #ifdef CONFIG_DEBUG_FS |
1049 | .debugfs_init = msm_debugfs_init, | |
c8afe684 | 1050 | #endif |
7198e6b0 | 1051 | .ioctls = msm_ioctls, |
167b606a | 1052 | .num_ioctls = ARRAY_SIZE(msm_ioctls), |
c8afe684 RC |
1053 | .fops = &fops, |
1054 | .name = "msm", | |
1055 | .desc = "MSM Snapdragon DRM", | |
1056 | .date = "20130625", | |
a8d854c1 RC |
1057 | .major = MSM_VERSION_MAJOR, |
1058 | .minor = MSM_VERSION_MINOR, | |
1059 | .patchlevel = MSM_VERSION_PATCHLEVEL, | |
c8afe684 RC |
1060 | }; |
1061 | ||
1062 | #ifdef CONFIG_PM_SLEEP | |
1063 | static int msm_pm_suspend(struct device *dev) | |
1064 | { | |
1065 | struct drm_device *ddev = dev_get_drvdata(dev); | |
ec446d09 | 1066 | struct msm_drm_private *priv = ddev->dev_private; |
c8afe684 | 1067 | |
3750e78c BW |
1068 | if (WARN_ON(priv->pm_state)) |
1069 | drm_atomic_state_put(priv->pm_state); | |
c8afe684 | 1070 | |
ec446d09 DM |
1071 | priv->pm_state = drm_atomic_helper_suspend(ddev); |
1072 | if (IS_ERR(priv->pm_state)) { | |
3750e78c BW |
1073 | int ret = PTR_ERR(priv->pm_state); |
1074 | DRM_ERROR("Failed to suspend dpu, %d\n", ret); | |
1075 | return ret; | |
ec446d09 DM |
1076 | } |
1077 | ||
c8afe684 RC |
1078 | return 0; |
1079 | } | |
1080 | ||
1081 | static int msm_pm_resume(struct device *dev) | |
1082 | { | |
1083 | struct drm_device *ddev = dev_get_drvdata(dev); | |
ec446d09 | 1084 | struct msm_drm_private *priv = ddev->dev_private; |
3750e78c | 1085 | int ret; |
036bfeb3 | 1086 | |
3750e78c BW |
1087 | if (WARN_ON(!priv->pm_state)) |
1088 | return -ENOENT; | |
c8afe684 | 1089 | |
3750e78c BW |
1090 | ret = drm_atomic_helper_resume(ddev, priv->pm_state); |
1091 | if (!ret) | |
1092 | priv->pm_state = NULL; | |
c8afe684 | 1093 | |
3750e78c | 1094 | return ret; |
c8afe684 RC |
1095 | } |
1096 | #endif | |
1097 | ||
774e39ee AT |
1098 | #ifdef CONFIG_PM |
1099 | static int msm_runtime_suspend(struct device *dev) | |
1100 | { | |
1101 | struct drm_device *ddev = dev_get_drvdata(dev); | |
1102 | struct msm_drm_private *priv = ddev->dev_private; | |
bc3220be | 1103 | struct msm_mdss *mdss = priv->mdss; |
774e39ee AT |
1104 | |
1105 | DBG(""); | |
1106 | ||
bc3220be RY |
1107 | if (mdss && mdss->funcs) |
1108 | return mdss->funcs->disable(mdss); | |
774e39ee AT |
1109 | |
1110 | return 0; | |
1111 | } | |
1112 | ||
1113 | static int msm_runtime_resume(struct device *dev) | |
1114 | { | |
1115 | struct drm_device *ddev = dev_get_drvdata(dev); | |
1116 | struct msm_drm_private *priv = ddev->dev_private; | |
bc3220be | 1117 | struct msm_mdss *mdss = priv->mdss; |
774e39ee AT |
1118 | |
1119 | DBG(""); | |
1120 | ||
bc3220be RY |
1121 | if (mdss && mdss->funcs) |
1122 | return mdss->funcs->enable(mdss); | |
774e39ee AT |
1123 | |
1124 | return 0; | |
1125 | } | |
1126 | #endif | |
1127 | ||
c8afe684 RC |
1128 | static const struct dev_pm_ops msm_pm_ops = { |
1129 | SET_SYSTEM_SLEEP_PM_OPS(msm_pm_suspend, msm_pm_resume) | |
774e39ee | 1130 | SET_RUNTIME_PM_OPS(msm_runtime_suspend, msm_runtime_resume, NULL) |
c8afe684 RC |
1131 | }; |
1132 | ||
060530f1 RC |
1133 | /* |
1134 | * Componentized driver support: | |
1135 | */ | |
1136 | ||
e9fbdaf2 AT |
1137 | /* |
1138 | * NOTE: duplication of the same code as exynos or imx (or probably any other). | |
1139 | * so probably some room for some helpers | |
060530f1 RC |
1140 | */ |
1141 | static int compare_of(struct device *dev, void *data) | |
1142 | { | |
1143 | return dev->of_node == data; | |
1144 | } | |
41e69778 | 1145 | |
812070eb AT |
1146 | /* |
1147 | * Identify what components need to be added by parsing what remote-endpoints | |
1148 | * our MDP output ports are connected to. In the case of LVDS on MDP4, there | |
1149 | * is no external component that we need to add since LVDS is within MDP4 | |
1150 | * itself. | |
1151 | */ | |
1152 | static int add_components_mdp(struct device *mdp_dev, | |
1153 | struct component_match **matchptr) | |
1154 | { | |
1155 | struct device_node *np = mdp_dev->of_node; | |
1156 | struct device_node *ep_node; | |
54011e26 AT |
1157 | struct device *master_dev; |
1158 | ||
1159 | /* | |
1160 | * on MDP4 based platforms, the MDP platform device is the component | |
1161 | * master that adds other display interface components to itself. | |
1162 | * | |
1163 | * on MDP5 based platforms, the MDSS platform device is the component | |
1164 | * master that adds MDP5 and other display interface components to | |
1165 | * itself. | |
1166 | */ | |
1167 | if (of_device_is_compatible(np, "qcom,mdp4")) | |
1168 | master_dev = mdp_dev; | |
1169 | else | |
1170 | master_dev = mdp_dev->parent; | |
812070eb AT |
1171 | |
1172 | for_each_endpoint_of_node(np, ep_node) { | |
1173 | struct device_node *intf; | |
1174 | struct of_endpoint ep; | |
1175 | int ret; | |
1176 | ||
1177 | ret = of_graph_parse_endpoint(ep_node, &ep); | |
1178 | if (ret) { | |
6a41da17 | 1179 | DRM_DEV_ERROR(mdp_dev, "unable to parse port endpoint\n"); |
812070eb AT |
1180 | of_node_put(ep_node); |
1181 | return ret; | |
1182 | } | |
1183 | ||
1184 | /* | |
1185 | * The LCDC/LVDS port on MDP4 is a speacial case where the | |
1186 | * remote-endpoint isn't a component that we need to add | |
1187 | */ | |
1188 | if (of_device_is_compatible(np, "qcom,mdp4") && | |
d8dd8052 | 1189 | ep.port == 0) |
812070eb | 1190 | continue; |
812070eb AT |
1191 | |
1192 | /* | |
1193 | * It's okay if some of the ports don't have a remote endpoint | |
1194 | * specified. It just means that the port isn't connected to | |
1195 | * any external interface. | |
1196 | */ | |
1197 | intf = of_graph_get_remote_port_parent(ep_node); | |
d8dd8052 | 1198 | if (!intf) |
812070eb | 1199 | continue; |
812070eb | 1200 | |
d1d9d0e1 DA |
1201 | if (of_device_is_available(intf)) |
1202 | drm_of_component_match_add(master_dev, matchptr, | |
1203 | compare_of, intf); | |
1204 | ||
812070eb | 1205 | of_node_put(intf); |
812070eb AT |
1206 | } |
1207 | ||
1208 | return 0; | |
1209 | } | |
1210 | ||
54011e26 AT |
1211 | static int compare_name_mdp(struct device *dev, void *data) |
1212 | { | |
1213 | return (strstr(dev_name(dev), "mdp") != NULL); | |
1214 | } | |
1215 | ||
7d526fcf AT |
1216 | static int add_display_components(struct device *dev, |
1217 | struct component_match **matchptr) | |
1218 | { | |
54011e26 AT |
1219 | struct device *mdp_dev; |
1220 | int ret; | |
1221 | ||
1222 | /* | |
25fdd593 JS |
1223 | * MDP5/DPU based devices don't have a flat hierarchy. There is a top |
1224 | * level parent: MDSS, and children: MDP5/DPU, DSI, HDMI, eDP etc. | |
1225 | * Populate the children devices, find the MDP5/DPU node, and then add | |
1226 | * the interfaces to our components list. | |
54011e26 | 1227 | */ |
25fdd593 JS |
1228 | if (of_device_is_compatible(dev->of_node, "qcom,mdss") || |
1229 | of_device_is_compatible(dev->of_node, "qcom,sdm845-mdss")) { | |
54011e26 AT |
1230 | ret = of_platform_populate(dev->of_node, NULL, NULL, dev); |
1231 | if (ret) { | |
6a41da17 | 1232 | DRM_DEV_ERROR(dev, "failed to populate children devices\n"); |
54011e26 AT |
1233 | return ret; |
1234 | } | |
1235 | ||
1236 | mdp_dev = device_find_child(dev, NULL, compare_name_mdp); | |
1237 | if (!mdp_dev) { | |
6a41da17 | 1238 | DRM_DEV_ERROR(dev, "failed to find MDSS MDP node\n"); |
54011e26 AT |
1239 | of_platform_depopulate(dev); |
1240 | return -ENODEV; | |
1241 | } | |
1242 | ||
1243 | put_device(mdp_dev); | |
1244 | ||
1245 | /* add the MDP component itself */ | |
97ac0e47 RK |
1246 | drm_of_component_match_add(dev, matchptr, compare_of, |
1247 | mdp_dev->of_node); | |
54011e26 AT |
1248 | } else { |
1249 | /* MDP4 */ | |
1250 | mdp_dev = dev; | |
1251 | } | |
1252 | ||
1253 | ret = add_components_mdp(mdp_dev, matchptr); | |
1254 | if (ret) | |
1255 | of_platform_depopulate(dev); | |
1256 | ||
1257 | return ret; | |
7d526fcf AT |
1258 | } |
1259 | ||
dc3ea265 AT |
1260 | /* |
1261 | * We don't know what's the best binding to link the gpu with the drm device. | |
1262 | * Fow now, we just hunt for all the possible gpus that we support, and add them | |
1263 | * as components. | |
1264 | */ | |
1265 | static const struct of_device_id msm_gpu_match[] = { | |
1db7afa4 | 1266 | { .compatible = "qcom,adreno" }, |
dc3ea265 | 1267 | { .compatible = "qcom,adreno-3xx" }, |
e6f6d63e | 1268 | { .compatible = "amd,imageon" }, |
dc3ea265 AT |
1269 | { .compatible = "qcom,kgsl-3d0" }, |
1270 | { }, | |
1271 | }; | |
1272 | ||
7d526fcf AT |
1273 | static int add_gpu_components(struct device *dev, |
1274 | struct component_match **matchptr) | |
1275 | { | |
dc3ea265 AT |
1276 | struct device_node *np; |
1277 | ||
1278 | np = of_find_matching_node(NULL, msm_gpu_match); | |
1279 | if (!np) | |
1280 | return 0; | |
1281 | ||
9ca7ad6c JH |
1282 | if (of_device_is_available(np)) |
1283 | drm_of_component_match_add(dev, matchptr, compare_of, np); | |
dc3ea265 AT |
1284 | |
1285 | of_node_put(np); | |
1286 | ||
1287 | return 0; | |
7d526fcf AT |
1288 | } |
1289 | ||
84448288 RK |
1290 | static int msm_drm_bind(struct device *dev) |
1291 | { | |
2b669875 | 1292 | return msm_drm_init(dev, &msm_driver); |
84448288 RK |
1293 | } |
1294 | ||
1295 | static void msm_drm_unbind(struct device *dev) | |
1296 | { | |
2b669875 | 1297 | msm_drm_uninit(dev); |
84448288 RK |
1298 | } |
1299 | ||
1300 | static const struct component_master_ops msm_drm_ops = { | |
1301 | .bind = msm_drm_bind, | |
1302 | .unbind = msm_drm_unbind, | |
1303 | }; | |
1304 | ||
1305 | /* | |
1306 | * Platform driver: | |
1307 | */ | |
060530f1 | 1308 | |
84448288 | 1309 | static int msm_pdev_probe(struct platform_device *pdev) |
060530f1 | 1310 | { |
84448288 | 1311 | struct component_match *match = NULL; |
7d526fcf AT |
1312 | int ret; |
1313 | ||
e6f6d63e JM |
1314 | if (get_mdp_ver(pdev)) { |
1315 | ret = add_display_components(&pdev->dev, &match); | |
1316 | if (ret) | |
1317 | return ret; | |
1318 | } | |
e9fbdaf2 | 1319 | |
7d526fcf AT |
1320 | ret = add_gpu_components(&pdev->dev, &match); |
1321 | if (ret) | |
4368a153 | 1322 | goto fail; |
060530f1 | 1323 | |
c83ea576 RC |
1324 | /* on all devices that I am aware of, iommu's which can map |
1325 | * any address the cpu can see are used: | |
1326 | */ | |
1327 | ret = dma_set_mask_and_coherent(&pdev->dev, ~0); | |
1328 | if (ret) | |
4368a153 SP |
1329 | goto fail; |
1330 | ||
1331 | ret = component_master_add_with_match(&pdev->dev, &msm_drm_ops, match); | |
1332 | if (ret) | |
1333 | goto fail; | |
c83ea576 | 1334 | |
4368a153 SP |
1335 | return 0; |
1336 | ||
1337 | fail: | |
1338 | of_platform_depopulate(&pdev->dev); | |
1339 | return ret; | |
c8afe684 RC |
1340 | } |
1341 | ||
1342 | static int msm_pdev_remove(struct platform_device *pdev) | |
1343 | { | |
060530f1 | 1344 | component_master_del(&pdev->dev, &msm_drm_ops); |
54011e26 | 1345 | of_platform_depopulate(&pdev->dev); |
c8afe684 RC |
1346 | |
1347 | return 0; | |
1348 | } | |
1349 | ||
06c0dd96 | 1350 | static const struct of_device_id dt_match[] = { |
aaded2e3 JS |
1351 | { .compatible = "qcom,mdp4", .data = (void *)KMS_MDP4 }, |
1352 | { .compatible = "qcom,mdss", .data = (void *)KMS_MDP5 }, | |
25fdd593 | 1353 | { .compatible = "qcom,sdm845-mdss", .data = (void *)KMS_DPU }, |
06c0dd96 RC |
1354 | {} |
1355 | }; | |
1356 | MODULE_DEVICE_TABLE(of, dt_match); | |
1357 | ||
c8afe684 RC |
1358 | static struct platform_driver msm_platform_driver = { |
1359 | .probe = msm_pdev_probe, | |
1360 | .remove = msm_pdev_remove, | |
1361 | .driver = { | |
c8afe684 | 1362 | .name = "msm", |
06c0dd96 | 1363 | .of_match_table = dt_match, |
c8afe684 RC |
1364 | .pm = &msm_pm_ops, |
1365 | }, | |
c8afe684 RC |
1366 | }; |
1367 | ||
1368 | static int __init msm_drm_register(void) | |
1369 | { | |
ba4dd718 RC |
1370 | if (!modeset) |
1371 | return -EINVAL; | |
1372 | ||
c8afe684 | 1373 | DBG("init"); |
1dd0a0b1 | 1374 | msm_mdp_register(); |
25fdd593 | 1375 | msm_dpu_register(); |
d5af49c9 | 1376 | msm_dsi_register(); |
00453981 | 1377 | msm_edp_register(); |
fcda50c8 | 1378 | msm_hdmi_register(); |
bfd28b13 | 1379 | adreno_register(); |
c8afe684 RC |
1380 | return platform_driver_register(&msm_platform_driver); |
1381 | } | |
1382 | ||
1383 | static void __exit msm_drm_unregister(void) | |
1384 | { | |
1385 | DBG("fini"); | |
1386 | platform_driver_unregister(&msm_platform_driver); | |
fcda50c8 | 1387 | msm_hdmi_unregister(); |
bfd28b13 | 1388 | adreno_unregister(); |
00453981 | 1389 | msm_edp_unregister(); |
d5af49c9 | 1390 | msm_dsi_unregister(); |
1dd0a0b1 | 1391 | msm_mdp_unregister(); |
25fdd593 | 1392 | msm_dpu_unregister(); |
c8afe684 RC |
1393 | } |
1394 | ||
1395 | module_init(msm_drm_register); | |
1396 | module_exit(msm_drm_unregister); | |
1397 | ||
1398 | MODULE_AUTHOR("Rob Clark <robdclark@gmail.com"); | |
1399 | MODULE_DESCRIPTION("MSM DRM Driver"); | |
1400 | MODULE_LICENSE("GPL"); |