diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-10 14:04:36 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-10 14:04:36 -0500 |
commit | 1a464cbb3d483f2f195b614cffa4aa1b910a0440 (patch) | |
tree | af57dee6436532dbb546b8670e9e1f6910d489b5 /drivers/gpu/drm/exynos/exynos_drm_hdmi.c | |
parent | dbe950f201a8edd353b0bd9079e8d536ee4ce37c (diff) | |
parent | 095f979a539245a46b9e5d600ec9c720b4d928e5 (diff) |
Merge branch 'drm-core-next' of git://people.freedesktop.org/~airlied/linux
* 'drm-core-next' of git://people.freedesktop.org/~airlied/linux: (307 commits)
drm/nouveau/pm: fix build with HWMON off
gma500: silence gcc warnings in mid_get_vbt_data()
drm/ttm: fix condition (and vs or)
drm/radeon: double lock typo in radeon_vm_bo_rmv()
drm/radeon: use after free in radeon_vm_bo_add()
drm/sis|via: don't return stack garbage from free_mem ioctl
drm/radeon/kms: remove pointless CS flags priority struct
drm/radeon/kms: check if vm is supported in VA ioctl
drm: introduce drm_can_sleep and use in intel/radeon drivers. (v2)
radeon: Fix disabling PCI bus mastering on big endian hosts.
ttm: fix agp since ttm tt rework
agp: Fix multi-line warning message whitespace
drm/ttm/dma: Fix accounting error when calling ttm_mem_global_free_page and don't try to free freed pages.
drm/ttm/dma: Only call set_pages_array_wb when the page is not in WB pool.
drm/radeon/kms: sync across multiple rings when doing bo moves v3
drm/radeon/kms: Add support for multi-ring sync in CS ioctl (v2)
drm/radeon: GPU virtual memory support v22
drm: make DRM_UNLOCKED ioctls with their own mutex
drm: no need to hold global mutex for static data
drm/radeon/benchmark: common modes sweep ignores 640x480@32
...
Fix up trivial conflicts in radeon/evergreen.c and vmwgfx/vmwgfx_kms.c
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_drm_hdmi.c')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_hdmi.c | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c new file mode 100644 index 00000000000..ed8a319ed84 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c | |||
@@ -0,0 +1,439 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Samsung Electronics Co.Ltd | ||
3 | * Authors: | ||
4 | * Inki Dae <inki.dae@samsung.com> | ||
5 | * Seung-Woo Kim <sw0312.kim@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include "drmP.h" | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/wait.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/pm_runtime.h> | ||
21 | |||
22 | #include <drm/exynos_drm.h> | ||
23 | |||
24 | #include "exynos_drm_drv.h" | ||
25 | #include "exynos_drm_hdmi.h" | ||
26 | |||
27 | #define to_context(dev) platform_get_drvdata(to_platform_device(dev)) | ||
28 | #define to_subdrv(dev) to_context(dev) | ||
29 | #define get_ctx_from_subdrv(subdrv) container_of(subdrv,\ | ||
30 | struct drm_hdmi_context, subdrv); | ||
31 | |||
32 | /* these callback points shoud be set by specific drivers. */ | ||
33 | static struct exynos_hdmi_display_ops *hdmi_display_ops; | ||
34 | static struct exynos_hdmi_manager_ops *hdmi_manager_ops; | ||
35 | static struct exynos_hdmi_overlay_ops *hdmi_overlay_ops; | ||
36 | |||
37 | struct drm_hdmi_context { | ||
38 | struct exynos_drm_subdrv subdrv; | ||
39 | struct exynos_drm_hdmi_context *hdmi_ctx; | ||
40 | struct exynos_drm_hdmi_context *mixer_ctx; | ||
41 | struct work_struct work; | ||
42 | }; | ||
43 | |||
44 | void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops | ||
45 | *display_ops) | ||
46 | { | ||
47 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
48 | |||
49 | if (display_ops) | ||
50 | hdmi_display_ops = display_ops; | ||
51 | } | ||
52 | EXPORT_SYMBOL(exynos_drm_display_ops_register); | ||
53 | |||
54 | void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops | ||
55 | *manager_ops) | ||
56 | { | ||
57 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
58 | |||
59 | if (manager_ops) | ||
60 | hdmi_manager_ops = manager_ops; | ||
61 | } | ||
62 | EXPORT_SYMBOL(exynos_drm_manager_ops_register); | ||
63 | |||
64 | void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops | ||
65 | *overlay_ops) | ||
66 | { | ||
67 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
68 | |||
69 | if (overlay_ops) | ||
70 | hdmi_overlay_ops = overlay_ops; | ||
71 | } | ||
72 | EXPORT_SYMBOL(exynos_drm_overlay_ops_register); | ||
73 | |||
74 | static bool drm_hdmi_is_connected(struct device *dev) | ||
75 | { | ||
76 | struct drm_hdmi_context *ctx = to_context(dev); | ||
77 | |||
78 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
79 | |||
80 | if (hdmi_display_ops && hdmi_display_ops->is_connected) | ||
81 | return hdmi_display_ops->is_connected(ctx->hdmi_ctx->ctx); | ||
82 | |||
83 | return false; | ||
84 | } | ||
85 | |||
86 | static int drm_hdmi_get_edid(struct device *dev, | ||
87 | struct drm_connector *connector, u8 *edid, int len) | ||
88 | { | ||
89 | struct drm_hdmi_context *ctx = to_context(dev); | ||
90 | |||
91 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
92 | |||
93 | if (hdmi_display_ops && hdmi_display_ops->get_edid) | ||
94 | return hdmi_display_ops->get_edid(ctx->hdmi_ctx->ctx, | ||
95 | connector, edid, len); | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static int drm_hdmi_check_timing(struct device *dev, void *timing) | ||
101 | { | ||
102 | struct drm_hdmi_context *ctx = to_context(dev); | ||
103 | |||
104 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
105 | |||
106 | if (hdmi_display_ops && hdmi_display_ops->check_timing) | ||
107 | return hdmi_display_ops->check_timing(ctx->hdmi_ctx->ctx, | ||
108 | timing); | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static int drm_hdmi_power_on(struct device *dev, int mode) | ||
114 | { | ||
115 | struct drm_hdmi_context *ctx = to_context(dev); | ||
116 | |||
117 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
118 | |||
119 | if (hdmi_display_ops && hdmi_display_ops->power_on) | ||
120 | return hdmi_display_ops->power_on(ctx->hdmi_ctx->ctx, mode); | ||
121 | |||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | static struct exynos_drm_display_ops drm_hdmi_display_ops = { | ||
126 | .type = EXYNOS_DISPLAY_TYPE_HDMI, | ||
127 | .is_connected = drm_hdmi_is_connected, | ||
128 | .get_edid = drm_hdmi_get_edid, | ||
129 | .check_timing = drm_hdmi_check_timing, | ||
130 | .power_on = drm_hdmi_power_on, | ||
131 | }; | ||
132 | |||
133 | static int drm_hdmi_enable_vblank(struct device *subdrv_dev) | ||
134 | { | ||
135 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
136 | struct exynos_drm_subdrv *subdrv = &ctx->subdrv; | ||
137 | struct exynos_drm_manager *manager = &subdrv->manager; | ||
138 | |||
139 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
140 | |||
141 | if (hdmi_overlay_ops && hdmi_overlay_ops->enable_vblank) | ||
142 | return hdmi_overlay_ops->enable_vblank(ctx->mixer_ctx->ctx, | ||
143 | manager->pipe); | ||
144 | |||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | static void drm_hdmi_disable_vblank(struct device *subdrv_dev) | ||
149 | { | ||
150 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
151 | |||
152 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
153 | |||
154 | if (hdmi_overlay_ops && hdmi_overlay_ops->disable_vblank) | ||
155 | return hdmi_overlay_ops->disable_vblank(ctx->mixer_ctx->ctx); | ||
156 | } | ||
157 | |||
158 | static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode) | ||
159 | { | ||
160 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
161 | |||
162 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
163 | |||
164 | if (hdmi_manager_ops && hdmi_manager_ops->mode_set) | ||
165 | hdmi_manager_ops->mode_set(ctx->hdmi_ctx->ctx, mode); | ||
166 | } | ||
167 | |||
168 | static void drm_hdmi_commit(struct device *subdrv_dev) | ||
169 | { | ||
170 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
171 | |||
172 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
173 | |||
174 | if (hdmi_manager_ops && hdmi_manager_ops->commit) | ||
175 | hdmi_manager_ops->commit(ctx->hdmi_ctx->ctx); | ||
176 | } | ||
177 | |||
178 | static void drm_hdmi_dpms(struct device *subdrv_dev, int mode) | ||
179 | { | ||
180 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
181 | |||
182 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
183 | |||
184 | switch (mode) { | ||
185 | case DRM_MODE_DPMS_ON: | ||
186 | break; | ||
187 | case DRM_MODE_DPMS_STANDBY: | ||
188 | case DRM_MODE_DPMS_SUSPEND: | ||
189 | case DRM_MODE_DPMS_OFF: | ||
190 | if (hdmi_manager_ops && hdmi_manager_ops->disable) | ||
191 | hdmi_manager_ops->disable(ctx->hdmi_ctx->ctx); | ||
192 | break; | ||
193 | default: | ||
194 | DRM_DEBUG_KMS("unkown dps mode: %d\n", mode); | ||
195 | break; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | static struct exynos_drm_manager_ops drm_hdmi_manager_ops = { | ||
200 | .dpms = drm_hdmi_dpms, | ||
201 | .enable_vblank = drm_hdmi_enable_vblank, | ||
202 | .disable_vblank = drm_hdmi_disable_vblank, | ||
203 | .mode_set = drm_hdmi_mode_set, | ||
204 | .commit = drm_hdmi_commit, | ||
205 | }; | ||
206 | |||
207 | static void drm_mixer_mode_set(struct device *subdrv_dev, | ||
208 | struct exynos_drm_overlay *overlay) | ||
209 | { | ||
210 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
211 | |||
212 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
213 | |||
214 | if (hdmi_overlay_ops && hdmi_overlay_ops->win_mode_set) | ||
215 | hdmi_overlay_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay); | ||
216 | } | ||
217 | |||
218 | static void drm_mixer_commit(struct device *subdrv_dev, int zpos) | ||
219 | { | ||
220 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
221 | |||
222 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
223 | |||
224 | if (hdmi_overlay_ops && hdmi_overlay_ops->win_commit) | ||
225 | hdmi_overlay_ops->win_commit(ctx->mixer_ctx->ctx, zpos); | ||
226 | } | ||
227 | |||
228 | static void drm_mixer_disable(struct device *subdrv_dev, int zpos) | ||
229 | { | ||
230 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | ||
231 | |||
232 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
233 | |||
234 | if (hdmi_overlay_ops && hdmi_overlay_ops->win_disable) | ||
235 | hdmi_overlay_ops->win_disable(ctx->mixer_ctx->ctx, zpos); | ||
236 | } | ||
237 | |||
238 | static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = { | ||
239 | .mode_set = drm_mixer_mode_set, | ||
240 | .commit = drm_mixer_commit, | ||
241 | .disable = drm_mixer_disable, | ||
242 | }; | ||
243 | |||
244 | |||
245 | static int hdmi_subdrv_probe(struct drm_device *drm_dev, | ||
246 | struct device *dev) | ||
247 | { | ||
248 | struct exynos_drm_subdrv *subdrv = to_subdrv(dev); | ||
249 | struct drm_hdmi_context *ctx; | ||
250 | struct platform_device *pdev = to_platform_device(dev); | ||
251 | struct exynos_drm_common_hdmi_pd *pd; | ||
252 | int ret; | ||
253 | |||
254 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
255 | |||
256 | pd = pdev->dev.platform_data; | ||
257 | |||
258 | if (!pd) { | ||
259 | DRM_DEBUG_KMS("platform data is null.\n"); | ||
260 | return -EFAULT; | ||
261 | } | ||
262 | |||
263 | if (!pd->hdmi_dev) { | ||
264 | DRM_DEBUG_KMS("hdmi device is null.\n"); | ||
265 | return -EFAULT; | ||
266 | } | ||
267 | |||
268 | if (!pd->mixer_dev) { | ||
269 | DRM_DEBUG_KMS("mixer device is null.\n"); | ||
270 | return -EFAULT; | ||
271 | } | ||
272 | |||
273 | ret = platform_driver_register(&hdmi_driver); | ||
274 | if (ret) { | ||
275 | DRM_DEBUG_KMS("failed to register hdmi driver.\n"); | ||
276 | return ret; | ||
277 | } | ||
278 | |||
279 | ret = platform_driver_register(&mixer_driver); | ||
280 | if (ret) { | ||
281 | DRM_DEBUG_KMS("failed to register mixer driver.\n"); | ||
282 | goto err_hdmidrv; | ||
283 | } | ||
284 | |||
285 | ctx = get_ctx_from_subdrv(subdrv); | ||
286 | |||
287 | ctx->hdmi_ctx = (struct exynos_drm_hdmi_context *) | ||
288 | to_context(pd->hdmi_dev); | ||
289 | if (!ctx->hdmi_ctx) { | ||
290 | DRM_DEBUG_KMS("hdmi context is null.\n"); | ||
291 | ret = -EFAULT; | ||
292 | goto err_mixerdrv; | ||
293 | } | ||
294 | |||
295 | ctx->hdmi_ctx->drm_dev = drm_dev; | ||
296 | |||
297 | ctx->mixer_ctx = (struct exynos_drm_hdmi_context *) | ||
298 | to_context(pd->mixer_dev); | ||
299 | if (!ctx->mixer_ctx) { | ||
300 | DRM_DEBUG_KMS("mixer context is null.\n"); | ||
301 | ret = -EFAULT; | ||
302 | goto err_mixerdrv; | ||
303 | } | ||
304 | |||
305 | ctx->mixer_ctx->drm_dev = drm_dev; | ||
306 | |||
307 | return 0; | ||
308 | |||
309 | err_mixerdrv: | ||
310 | platform_driver_unregister(&mixer_driver); | ||
311 | err_hdmidrv: | ||
312 | platform_driver_unregister(&hdmi_driver); | ||
313 | return ret; | ||
314 | } | ||
315 | |||
316 | static void hdmi_subdrv_remove(struct drm_device *drm_dev) | ||
317 | { | ||
318 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
319 | |||
320 | platform_driver_unregister(&hdmi_driver); | ||
321 | platform_driver_unregister(&mixer_driver); | ||
322 | } | ||
323 | |||
324 | static void exynos_drm_hdmi_late_probe(struct work_struct *work) | ||
325 | { | ||
326 | struct drm_hdmi_context *ctx = container_of(work, | ||
327 | struct drm_hdmi_context, work); | ||
328 | |||
329 | /* | ||
330 | * this function calls subdrv->probe() so this must be called | ||
331 | * after probe context. | ||
332 | * | ||
333 | * PS. subdrv->probe() will call platform_driver_register() to probe | ||
334 | * hdmi and mixer driver. | ||
335 | */ | ||
336 | exynos_drm_subdrv_register(&ctx->subdrv); | ||
337 | } | ||
338 | |||
339 | static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev) | ||
340 | { | ||
341 | struct device *dev = &pdev->dev; | ||
342 | struct exynos_drm_subdrv *subdrv; | ||
343 | struct drm_hdmi_context *ctx; | ||
344 | |||
345 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
346 | |||
347 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | ||
348 | if (!ctx) { | ||
349 | DRM_LOG_KMS("failed to alloc common hdmi context.\n"); | ||
350 | return -ENOMEM; | ||
351 | } | ||
352 | |||
353 | subdrv = &ctx->subdrv; | ||
354 | |||
355 | subdrv->probe = hdmi_subdrv_probe; | ||
356 | subdrv->remove = hdmi_subdrv_remove; | ||
357 | subdrv->manager.pipe = -1; | ||
358 | subdrv->manager.ops = &drm_hdmi_manager_ops; | ||
359 | subdrv->manager.overlay_ops = &drm_hdmi_overlay_ops; | ||
360 | subdrv->manager.display_ops = &drm_hdmi_display_ops; | ||
361 | subdrv->manager.dev = dev; | ||
362 | |||
363 | platform_set_drvdata(pdev, subdrv); | ||
364 | |||
365 | INIT_WORK(&ctx->work, exynos_drm_hdmi_late_probe); | ||
366 | |||
367 | schedule_work(&ctx->work); | ||
368 | |||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | static int hdmi_runtime_suspend(struct device *dev) | ||
373 | { | ||
374 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
375 | |||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | static int hdmi_runtime_resume(struct device *dev) | ||
380 | { | ||
381 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
382 | |||
383 | return 0; | ||
384 | } | ||
385 | |||
386 | static const struct dev_pm_ops hdmi_pm_ops = { | ||
387 | .runtime_suspend = hdmi_runtime_suspend, | ||
388 | .runtime_resume = hdmi_runtime_resume, | ||
389 | }; | ||
390 | |||
391 | static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev) | ||
392 | { | ||
393 | struct drm_hdmi_context *ctx = platform_get_drvdata(pdev); | ||
394 | |||
395 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
396 | |||
397 | exynos_drm_subdrv_unregister(&ctx->subdrv); | ||
398 | kfree(ctx); | ||
399 | |||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | static struct platform_driver exynos_drm_common_hdmi_driver = { | ||
404 | .probe = exynos_drm_hdmi_probe, | ||
405 | .remove = __devexit_p(exynos_drm_hdmi_remove), | ||
406 | .driver = { | ||
407 | .name = "exynos-drm-hdmi", | ||
408 | .owner = THIS_MODULE, | ||
409 | .pm = &hdmi_pm_ops, | ||
410 | }, | ||
411 | }; | ||
412 | |||
413 | static int __init exynos_drm_hdmi_init(void) | ||
414 | { | ||
415 | int ret; | ||
416 | |||
417 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
418 | |||
419 | ret = platform_driver_register(&exynos_drm_common_hdmi_driver); | ||
420 | if (ret) { | ||
421 | DRM_DEBUG_KMS("failed to register hdmi common driver.\n"); | ||
422 | return ret; | ||
423 | } | ||
424 | |||
425 | return ret; | ||
426 | } | ||
427 | |||
428 | static void __exit exynos_drm_hdmi_exit(void) | ||
429 | { | ||
430 | platform_driver_unregister(&exynos_drm_common_hdmi_driver); | ||
431 | } | ||
432 | |||
433 | module_init(exynos_drm_hdmi_init); | ||
434 | module_exit(exynos_drm_hdmi_exit); | ||
435 | |||
436 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
437 | MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>"); | ||
438 | MODULE_DESCRIPTION("Samsung SoC DRM HDMI Driver"); | ||
439 | MODULE_LICENSE("GPL"); | ||