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