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