]>
Commit | Line | Data |
---|---|---|
d8408326 SWK |
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 | ||
760285e7 | 14 | #include <drm/drmP.h> |
d8408326 SWK |
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 | ||
ae9dace2 RS |
32 | /* platform device pointer for common drm hdmi device. */ |
33 | static struct platform_device *exynos_drm_hdmi_pdev; | |
34 | ||
768c3059 RS |
35 | /* Common hdmi subdrv needs to access the hdmi and mixer though context. |
36 | * These should be initialied by the repective drivers */ | |
37 | static struct exynos_drm_hdmi_context *hdmi_ctx; | |
38 | static struct exynos_drm_hdmi_context *mixer_ctx; | |
39 | ||
d8408326 | 40 | /* these callback points shoud be set by specific drivers. */ |
578b6065 JS |
41 | static struct exynos_hdmi_ops *hdmi_ops; |
42 | static struct exynos_mixer_ops *mixer_ops; | |
d8408326 SWK |
43 | |
44 | struct drm_hdmi_context { | |
45 | struct exynos_drm_subdrv subdrv; | |
46 | struct exynos_drm_hdmi_context *hdmi_ctx; | |
47 | struct exynos_drm_hdmi_context *mixer_ctx; | |
cf8fc4f1 JS |
48 | |
49 | bool enabled[MIXER_WIN_NR]; | |
d8408326 SWK |
50 | }; |
51 | ||
ae9dace2 RS |
52 | int exynos_platform_device_hdmi_register(void) |
53 | { | |
54 | if (exynos_drm_hdmi_pdev) | |
55 | return -EEXIST; | |
56 | ||
57 | exynos_drm_hdmi_pdev = platform_device_register_simple( | |
58 | "exynos-drm-hdmi", -1, NULL, 0); | |
59 | if (IS_ERR_OR_NULL(exynos_drm_hdmi_pdev)) | |
60 | return PTR_ERR(exynos_drm_hdmi_pdev); | |
61 | ||
62 | return 0; | |
63 | } | |
64 | ||
65 | void exynos_platform_device_hdmi_unregister(void) | |
66 | { | |
67 | if (exynos_drm_hdmi_pdev) | |
68 | platform_device_unregister(exynos_drm_hdmi_pdev); | |
69 | } | |
70 | ||
768c3059 RS |
71 | void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx) |
72 | { | |
73 | if (ctx) | |
74 | hdmi_ctx = ctx; | |
75 | } | |
76 | ||
77 | void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx) | |
78 | { | |
79 | if (ctx) | |
80 | mixer_ctx = ctx; | |
81 | } | |
82 | ||
578b6065 | 83 | void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops) |
d8408326 SWK |
84 | { |
85 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
86 | ||
578b6065 JS |
87 | if (ops) |
88 | hdmi_ops = ops; | |
d8408326 | 89 | } |
d8408326 | 90 | |
578b6065 | 91 | void exynos_mixer_ops_register(struct exynos_mixer_ops *ops) |
d8408326 SWK |
92 | { |
93 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
94 | ||
578b6065 JS |
95 | if (ops) |
96 | mixer_ops = ops; | |
d8408326 | 97 | } |
d8408326 SWK |
98 | |
99 | static bool drm_hdmi_is_connected(struct device *dev) | |
100 | { | |
101 | struct drm_hdmi_context *ctx = to_context(dev); | |
102 | ||
103 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
104 | ||
578b6065 JS |
105 | if (hdmi_ops && hdmi_ops->is_connected) |
106 | return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx); | |
d8408326 SWK |
107 | |
108 | return false; | |
109 | } | |
110 | ||
111 | static int drm_hdmi_get_edid(struct device *dev, | |
112 | struct drm_connector *connector, u8 *edid, int len) | |
113 | { | |
114 | struct drm_hdmi_context *ctx = to_context(dev); | |
115 | ||
116 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
117 | ||
578b6065 JS |
118 | if (hdmi_ops && hdmi_ops->get_edid) |
119 | return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector, edid, | |
120 | len); | |
d8408326 SWK |
121 | |
122 | return 0; | |
123 | } | |
124 | ||
125 | static int drm_hdmi_check_timing(struct device *dev, void *timing) | |
126 | { | |
127 | struct drm_hdmi_context *ctx = to_context(dev); | |
128 | ||
129 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
130 | ||
578b6065 JS |
131 | if (hdmi_ops && hdmi_ops->check_timing) |
132 | return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing); | |
d8408326 SWK |
133 | |
134 | return 0; | |
135 | } | |
136 | ||
137 | static int drm_hdmi_power_on(struct device *dev, int mode) | |
138 | { | |
139 | struct drm_hdmi_context *ctx = to_context(dev); | |
140 | ||
141 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
142 | ||
578b6065 JS |
143 | if (hdmi_ops && hdmi_ops->power_on) |
144 | return hdmi_ops->power_on(ctx->hdmi_ctx->ctx, mode); | |
d8408326 SWK |
145 | |
146 | return 0; | |
147 | } | |
148 | ||
149 | static struct exynos_drm_display_ops drm_hdmi_display_ops = { | |
150 | .type = EXYNOS_DISPLAY_TYPE_HDMI, | |
151 | .is_connected = drm_hdmi_is_connected, | |
152 | .get_edid = drm_hdmi_get_edid, | |
153 | .check_timing = drm_hdmi_check_timing, | |
154 | .power_on = drm_hdmi_power_on, | |
155 | }; | |
156 | ||
157 | static int drm_hdmi_enable_vblank(struct device *subdrv_dev) | |
158 | { | |
159 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | |
160 | struct exynos_drm_subdrv *subdrv = &ctx->subdrv; | |
677e84c1 | 161 | struct exynos_drm_manager *manager = subdrv->manager; |
d8408326 SWK |
162 | |
163 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
164 | ||
578b6065 JS |
165 | if (mixer_ops && mixer_ops->enable_vblank) |
166 | return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx, | |
167 | manager->pipe); | |
d8408326 SWK |
168 | |
169 | return 0; | |
170 | } | |
171 | ||
172 | static void drm_hdmi_disable_vblank(struct device *subdrv_dev) | |
173 | { | |
174 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | |
175 | ||
176 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
177 | ||
578b6065 JS |
178 | if (mixer_ops && mixer_ops->disable_vblank) |
179 | return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx); | |
d8408326 SWK |
180 | } |
181 | ||
8137a2e2 P |
182 | static void drm_hdmi_wait_for_vblank(struct device *subdrv_dev) |
183 | { | |
184 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | |
185 | ||
186 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
187 | ||
188 | if (mixer_ops && mixer_ops->wait_for_vblank) | |
189 | mixer_ops->wait_for_vblank(ctx->mixer_ctx->ctx); | |
190 | } | |
191 | ||
1de425b0 ID |
192 | static void drm_hdmi_mode_fixup(struct device *subdrv_dev, |
193 | struct drm_connector *connector, | |
e811f5ae | 194 | const struct drm_display_mode *mode, |
1de425b0 ID |
195 | struct drm_display_mode *adjusted_mode) |
196 | { | |
197 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | |
198 | ||
199 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
200 | ||
578b6065 JS |
201 | if (hdmi_ops && hdmi_ops->mode_fixup) |
202 | hdmi_ops->mode_fixup(ctx->hdmi_ctx->ctx, connector, mode, | |
203 | adjusted_mode); | |
1de425b0 ID |
204 | } |
205 | ||
d8408326 SWK |
206 | static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode) |
207 | { | |
208 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | |
209 | ||
210 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
211 | ||
578b6065 JS |
212 | if (hdmi_ops && hdmi_ops->mode_set) |
213 | hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode); | |
d8408326 SWK |
214 | } |
215 | ||
1de425b0 ID |
216 | static void drm_hdmi_get_max_resol(struct device *subdrv_dev, |
217 | unsigned int *width, unsigned int *height) | |
218 | { | |
219 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | |
220 | ||
221 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
222 | ||
578b6065 JS |
223 | if (hdmi_ops && hdmi_ops->get_max_resol) |
224 | hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height); | |
1de425b0 ID |
225 | } |
226 | ||
d8408326 SWK |
227 | static void drm_hdmi_commit(struct device *subdrv_dev) |
228 | { | |
229 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | |
230 | ||
231 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
232 | ||
578b6065 JS |
233 | if (hdmi_ops && hdmi_ops->commit) |
234 | hdmi_ops->commit(ctx->hdmi_ctx->ctx); | |
d8408326 SWK |
235 | } |
236 | ||
237 | static void drm_hdmi_dpms(struct device *subdrv_dev, int mode) | |
238 | { | |
239 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | |
240 | ||
241 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
242 | ||
cf8fc4f1 JS |
243 | if (mixer_ops && mixer_ops->dpms) |
244 | mixer_ops->dpms(ctx->mixer_ctx->ctx, mode); | |
245 | ||
246 | if (hdmi_ops && hdmi_ops->dpms) | |
247 | hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode); | |
248 | } | |
249 | ||
250 | static void drm_hdmi_apply(struct device *subdrv_dev) | |
251 | { | |
252 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | |
253 | int i; | |
254 | ||
255 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
256 | ||
257 | for (i = 0; i < MIXER_WIN_NR; i++) { | |
258 | if (!ctx->enabled[i]) | |
259 | continue; | |
260 | if (mixer_ops && mixer_ops->win_commit) | |
261 | mixer_ops->win_commit(ctx->mixer_ctx->ctx, i); | |
d8408326 | 262 | } |
cf8fc4f1 JS |
263 | |
264 | if (hdmi_ops && hdmi_ops->commit) | |
265 | hdmi_ops->commit(ctx->hdmi_ctx->ctx); | |
d8408326 SWK |
266 | } |
267 | ||
268 | static struct exynos_drm_manager_ops drm_hdmi_manager_ops = { | |
269 | .dpms = drm_hdmi_dpms, | |
cf8fc4f1 | 270 | .apply = drm_hdmi_apply, |
d8408326 SWK |
271 | .enable_vblank = drm_hdmi_enable_vblank, |
272 | .disable_vblank = drm_hdmi_disable_vblank, | |
8137a2e2 | 273 | .wait_for_vblank = drm_hdmi_wait_for_vblank, |
1de425b0 | 274 | .mode_fixup = drm_hdmi_mode_fixup, |
d8408326 | 275 | .mode_set = drm_hdmi_mode_set, |
1de425b0 | 276 | .get_max_resol = drm_hdmi_get_max_resol, |
d8408326 SWK |
277 | .commit = drm_hdmi_commit, |
278 | }; | |
279 | ||
280 | static void drm_mixer_mode_set(struct device *subdrv_dev, | |
281 | struct exynos_drm_overlay *overlay) | |
282 | { | |
283 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | |
284 | ||
285 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
286 | ||
578b6065 JS |
287 | if (mixer_ops && mixer_ops->win_mode_set) |
288 | mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay); | |
d8408326 SWK |
289 | } |
290 | ||
291 | static void drm_mixer_commit(struct device *subdrv_dev, int zpos) | |
292 | { | |
293 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | |
cf8fc4f1 | 294 | int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos; |
d8408326 SWK |
295 | |
296 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
297 | ||
cf8fc4f1 JS |
298 | if (win < 0 || win > MIXER_WIN_NR) { |
299 | DRM_ERROR("mixer window[%d] is wrong\n", win); | |
300 | return; | |
301 | } | |
302 | ||
578b6065 | 303 | if (mixer_ops && mixer_ops->win_commit) |
cf8fc4f1 JS |
304 | mixer_ops->win_commit(ctx->mixer_ctx->ctx, win); |
305 | ||
306 | ctx->enabled[win] = true; | |
d8408326 SWK |
307 | } |
308 | ||
309 | static void drm_mixer_disable(struct device *subdrv_dev, int zpos) | |
310 | { | |
311 | struct drm_hdmi_context *ctx = to_context(subdrv_dev); | |
cf8fc4f1 | 312 | int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos; |
d8408326 SWK |
313 | |
314 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
315 | ||
cf8fc4f1 JS |
316 | if (win < 0 || win > MIXER_WIN_NR) { |
317 | DRM_ERROR("mixer window[%d] is wrong\n", win); | |
318 | return; | |
319 | } | |
320 | ||
578b6065 | 321 | if (mixer_ops && mixer_ops->win_disable) |
cf8fc4f1 JS |
322 | mixer_ops->win_disable(ctx->mixer_ctx->ctx, win); |
323 | ||
324 | ctx->enabled[win] = false; | |
d8408326 SWK |
325 | } |
326 | ||
327 | static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = { | |
328 | .mode_set = drm_mixer_mode_set, | |
329 | .commit = drm_mixer_commit, | |
330 | .disable = drm_mixer_disable, | |
331 | }; | |
332 | ||
677e84c1 JS |
333 | static struct exynos_drm_manager hdmi_manager = { |
334 | .pipe = -1, | |
335 | .ops = &drm_hdmi_manager_ops, | |
336 | .overlay_ops = &drm_hdmi_overlay_ops, | |
337 | .display_ops = &drm_hdmi_display_ops, | |
338 | }; | |
d8408326 SWK |
339 | |
340 | static int hdmi_subdrv_probe(struct drm_device *drm_dev, | |
341 | struct device *dev) | |
342 | { | |
343 | struct exynos_drm_subdrv *subdrv = to_subdrv(dev); | |
344 | struct drm_hdmi_context *ctx; | |
d8408326 SWK |
345 | |
346 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
347 | ||
768c3059 RS |
348 | if (!hdmi_ctx) { |
349 | DRM_ERROR("hdmi context not initialized.\n"); | |
d8408326 SWK |
350 | return -EFAULT; |
351 | } | |
352 | ||
768c3059 RS |
353 | if (!mixer_ctx) { |
354 | DRM_ERROR("mixer context not initialized.\n"); | |
d8408326 SWK |
355 | return -EFAULT; |
356 | } | |
357 | ||
d8408326 SWK |
358 | ctx = get_ctx_from_subdrv(subdrv); |
359 | ||
768c3059 RS |
360 | if (!ctx) { |
361 | DRM_ERROR("no drm hdmi context.\n"); | |
132a5b91 | 362 | return -EFAULT; |
d8408326 SWK |
363 | } |
364 | ||
768c3059 RS |
365 | ctx->hdmi_ctx = hdmi_ctx; |
366 | ctx->mixer_ctx = mixer_ctx; | |
d8408326 | 367 | |
768c3059 | 368 | ctx->hdmi_ctx->drm_dev = drm_dev; |
d8408326 SWK |
369 | ctx->mixer_ctx->drm_dev = drm_dev; |
370 | ||
1055b39f ID |
371 | if (mixer_ops->iommu_on) |
372 | mixer_ops->iommu_on(ctx->mixer_ctx->ctx, true); | |
373 | ||
d8408326 | 374 | return 0; |
d8408326 SWK |
375 | } |
376 | ||
1055b39f ID |
377 | static void hdmi_subdrv_remove(struct drm_device *drm_dev, struct device *dev) |
378 | { | |
379 | struct drm_hdmi_context *ctx; | |
380 | struct exynos_drm_subdrv *subdrv = to_subdrv(dev); | |
381 | ||
382 | ctx = get_ctx_from_subdrv(subdrv); | |
383 | ||
384 | if (mixer_ops->iommu_on) | |
385 | mixer_ops->iommu_on(ctx->mixer_ctx->ctx, false); | |
386 | } | |
387 | ||
d8408326 SWK |
388 | static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev) |
389 | { | |
390 | struct device *dev = &pdev->dev; | |
391 | struct exynos_drm_subdrv *subdrv; | |
392 | struct drm_hdmi_context *ctx; | |
393 | ||
394 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
395 | ||
ae182940 | 396 | ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); |
d8408326 SWK |
397 | if (!ctx) { |
398 | DRM_LOG_KMS("failed to alloc common hdmi context.\n"); | |
399 | return -ENOMEM; | |
400 | } | |
401 | ||
402 | subdrv = &ctx->subdrv; | |
403 | ||
677e84c1 JS |
404 | subdrv->dev = dev; |
405 | subdrv->manager = &hdmi_manager; | |
d8408326 | 406 | subdrv->probe = hdmi_subdrv_probe; |
1055b39f | 407 | subdrv->remove = hdmi_subdrv_remove; |
d8408326 SWK |
408 | |
409 | platform_set_drvdata(pdev, subdrv); | |
410 | ||
132a5b91 | 411 | exynos_drm_subdrv_register(subdrv); |
d8408326 SWK |
412 | |
413 | return 0; | |
414 | } | |
415 | ||
d8408326 SWK |
416 | static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev) |
417 | { | |
418 | struct drm_hdmi_context *ctx = platform_get_drvdata(pdev); | |
419 | ||
420 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
421 | ||
422 | exynos_drm_subdrv_unregister(&ctx->subdrv); | |
d8408326 SWK |
423 | |
424 | return 0; | |
425 | } | |
426 | ||
132a5b91 | 427 | struct platform_driver exynos_drm_common_hdmi_driver = { |
d8408326 SWK |
428 | .probe = exynos_drm_hdmi_probe, |
429 | .remove = __devexit_p(exynos_drm_hdmi_remove), | |
430 | .driver = { | |
431 | .name = "exynos-drm-hdmi", | |
432 | .owner = THIS_MODULE, | |
d8408326 SWK |
433 | }, |
434 | }; |