Version:  2.6.34 2.6.35 2.6.36 2.6.37 2.6.38 2.6.39 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14

Linux/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                 return -ENOMEM;
408 
409         subdrv = &ctx->subdrv;
410 
411         subdrv->dev = dev;
412         subdrv->manager = &hdmi_manager;
413         subdrv->probe = hdmi_subdrv_probe;
414         subdrv->remove = hdmi_subdrv_remove;
415 
416         platform_set_drvdata(pdev, subdrv);
417 
418         exynos_drm_subdrv_register(subdrv);
419 
420         return 0;
421 }
422 
423 static int exynos_drm_hdmi_remove(struct platform_device *pdev)
424 {
425         struct drm_hdmi_context *ctx = platform_get_drvdata(pdev);
426 
427         exynos_drm_subdrv_unregister(&ctx->subdrv);
428 
429         return 0;
430 }
431 
432 struct platform_driver exynos_drm_common_hdmi_driver = {
433         .probe          = exynos_drm_hdmi_probe,
434         .remove         = exynos_drm_hdmi_remove,
435         .driver         = {
436                 .name   = "exynos-drm-hdmi",
437                 .owner  = THIS_MODULE,
438         },
439 };
440 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us