chromium: add drm drivers files based on
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / rockchip / rockchip_drm_hdmi.c
1 /*
2  * Copyright (C) ROCKCHIP, Inc.
3  * Author:yzq<yzq@rock-chips.com>
4  *
5  * based on exynos_drm_hdmi.c
6  *
7  * This software is licensed under the terms of the GNU General Public
8  * License version 2, as published by the Free Software Foundation, and
9  * may be copied, distributed, and modified under those terms.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  */
16
17 #include <drm/drmP.h>
18
19 #include <linux/kernel.h>
20 #include <linux/wait.h>
21 #include <linux/module.h>
22 #include <linux/platform_device.h>
23 #include <linux/pm_runtime.h>
24
25 #include <drm/rockchip_drm.h>
26
27 #include "rockchip_drm_drv.h"
28 #include "rockchip_drm_hdmi.h"
29
30 #define to_context(dev)         platform_get_drvdata(to_platform_device(dev))
31 #define to_subdrv(dev)          to_context(dev)
32 #define get_ctx_from_subdrv(subdrv)     container_of(subdrv,\
33                                         struct drm_hdmi_context, subdrv);
34
35 /* platform device pointer for common drm hdmi device. */
36 static struct platform_device *rockchip_drm_hdmi_pdev;
37
38 /* Common hdmi subdrv needs to access the hdmi and mixer though context.
39 * These should be initialied by the repective drivers */
40 static struct rockchip_drm_hdmi_context *hdmi_ctx;
41 static struct rockchip_drm_hdmi_context *mixer_ctx;
42
43 /* these callback points shoud be set by specific drivers. */
44 static struct rockchip_hdmi_ops *hdmi_ops;
45 static struct rockchip_mixer_ops *mixer_ops;
46
47 struct drm_hdmi_context {
48         struct rockchip_drm_subdrv      subdrv;
49         struct rockchip_drm_hdmi_context        *hdmi_ctx;
50         struct rockchip_drm_hdmi_context        *mixer_ctx;
51
52         bool    enabled[MIXER_WIN_NR];
53 };
54
55 int rockchip_platform_device_hdmi_register(void)
56 {
57         struct platform_device *pdev;
58
59         if (rockchip_drm_hdmi_pdev)
60                 return -EEXIST;
61
62         pdev = platform_device_register_simple(
63                         "rockchip-drm-hdmi", -1, NULL, 0);
64         if (IS_ERR(pdev))
65                 return PTR_ERR(pdev);
66
67         rockchip_drm_hdmi_pdev = pdev;
68
69         return 0;
70 }
71
72 void rockchip_platform_device_hdmi_unregister(void)
73 {
74         if (rockchip_drm_hdmi_pdev) {
75                 platform_device_unregister(rockchip_drm_hdmi_pdev);
76                 rockchip_drm_hdmi_pdev = NULL;
77         }
78 }
79
80 void rockchip_hdmi_drv_attach(struct rockchip_drm_hdmi_context *ctx)
81 {
82         if (ctx)
83                 hdmi_ctx = ctx;
84 }
85
86 void rockchip_mixer_drv_attach(struct rockchip_drm_hdmi_context *ctx)
87 {
88         if (ctx)
89                 mixer_ctx = ctx;
90 }
91
92 void rockchip_hdmi_ops_register(struct rockchip_hdmi_ops *ops)
93 {
94         DRM_DEBUG_KMS("%s\n", __FILE__);
95
96         if (ops)
97                 hdmi_ops = ops;
98 }
99
100 void rockchip_mixer_ops_register(struct rockchip_mixer_ops *ops)
101 {
102         DRM_DEBUG_KMS("%s\n", __FILE__);
103
104         if (ops)
105                 mixer_ops = ops;
106 }
107
108 static bool drm_hdmi_is_connected(struct device *dev)
109 {
110         struct drm_hdmi_context *ctx = to_context(dev);
111
112         DRM_DEBUG_KMS("%s\n", __FILE__);
113
114         if (hdmi_ops && hdmi_ops->is_connected)
115                 return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx);
116
117         return false;
118 }
119
120 static struct edid *drm_hdmi_get_edid(struct device *dev,
121                         struct drm_connector *connector)
122 {
123         struct drm_hdmi_context *ctx = to_context(dev);
124
125         DRM_DEBUG_KMS("%s\n", __FILE__);
126
127         if (hdmi_ops && hdmi_ops->get_edid)
128                 return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector);
129
130         return NULL;
131 }
132
133 static int drm_hdmi_check_timing(struct device *dev, void *timing)
134 {
135         struct drm_hdmi_context *ctx = to_context(dev);
136         int ret = 0;
137
138         DRM_DEBUG_KMS("%s\n", __FILE__);
139
140         /*
141         * Both, mixer and hdmi should be able to handle the requested mode.
142         * If any of the two fails, return mode as BAD.
143         */
144
145         if (mixer_ops && mixer_ops->check_timing)
146                 ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, timing);
147
148         if (ret)
149                 return ret;
150
151         if (hdmi_ops && hdmi_ops->check_timing)
152                 return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing);
153
154         return 0;
155 }
156
157 static int drm_hdmi_power_on(struct device *dev, int mode)
158 {
159         struct drm_hdmi_context *ctx = to_context(dev);
160
161         DRM_DEBUG_KMS("%s\n", __FILE__);
162
163         if (hdmi_ops && hdmi_ops->power_on)
164                 return hdmi_ops->power_on(ctx->hdmi_ctx->ctx, mode);
165
166         return 0;
167 }
168
169 static struct rockchip_drm_display_ops drm_hdmi_display_ops = {
170         .type = ROCKCHIP_DISPLAY_TYPE_HDMI,
171         .is_connected = drm_hdmi_is_connected,
172         .get_edid = drm_hdmi_get_edid,
173         .check_timing = drm_hdmi_check_timing,
174         .power_on = drm_hdmi_power_on,
175 };
176
177 static int drm_hdmi_enable_vblank(struct device *subdrv_dev)
178 {
179         struct drm_hdmi_context *ctx = to_context(subdrv_dev);
180         struct rockchip_drm_subdrv *subdrv = &ctx->subdrv;
181         struct rockchip_drm_manager *manager = subdrv->manager;
182
183         DRM_DEBUG_KMS("%s\n", __FILE__);
184
185         if (mixer_ops && mixer_ops->enable_vblank)
186                 return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx,
187                                                 manager->pipe);
188
189         return 0;
190 }
191
192 static void drm_hdmi_disable_vblank(struct device *subdrv_dev)
193 {
194         struct drm_hdmi_context *ctx = to_context(subdrv_dev);
195
196         DRM_DEBUG_KMS("%s\n", __FILE__);
197
198         if (mixer_ops && mixer_ops->disable_vblank)
199                 return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx);
200 }
201
202 static void drm_hdmi_wait_for_vblank(struct device *subdrv_dev)
203 {
204         struct drm_hdmi_context *ctx = to_context(subdrv_dev);
205
206         DRM_DEBUG_KMS("%s\n", __FILE__);
207
208         if (mixer_ops && mixer_ops->wait_for_vblank)
209                 mixer_ops->wait_for_vblank(ctx->mixer_ctx->ctx);
210 }
211
212 static void drm_hdmi_mode_fixup(struct device *subdrv_dev,
213                                 struct drm_connector *connector,
214                                 const struct drm_display_mode *mode,
215                                 struct drm_display_mode *adjusted_mode)
216 {
217         struct drm_display_mode *m;
218         int mode_ok;
219
220         DRM_DEBUG_KMS("%s\n", __FILE__);
221
222         drm_mode_set_crtcinfo(adjusted_mode, 0);
223
224         mode_ok = drm_hdmi_check_timing(subdrv_dev, adjusted_mode);
225
226         /* just return if user desired mode exists. */
227         if (mode_ok == 0)
228                 return;
229
230         /*
231          * otherwise, find the most suitable mode among modes and change it
232          * to adjusted_mode.
233          */
234         list_for_each_entry(m, &connector->modes, head) {
235                 mode_ok = drm_hdmi_check_timing(subdrv_dev, m);
236
237                 if (mode_ok == 0) {
238                         struct drm_mode_object base;
239                         struct list_head head;
240
241                         DRM_INFO("desired mode doesn't exist so\n");
242                         DRM_INFO("use the most suitable mode among modes.\n");
243
244                         DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
245                                 m->hdisplay, m->vdisplay, m->vrefresh);
246
247                         /* preserve display mode header while copying. */
248                         head = adjusted_mode->head;
249                         base = adjusted_mode->base;
250                         memcpy(adjusted_mode, m, sizeof(*m));
251                         adjusted_mode->head = head;
252                         adjusted_mode->base = base;
253                         break;
254                 }
255         }
256 }
257
258 static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
259 {
260         struct drm_hdmi_context *ctx = to_context(subdrv_dev);
261
262         DRM_DEBUG_KMS("%s\n", __FILE__);
263
264         if (hdmi_ops && hdmi_ops->mode_set)
265                 hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
266 }
267
268 static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
269                                 unsigned int *width, unsigned int *height)
270 {
271         struct drm_hdmi_context *ctx = to_context(subdrv_dev);
272
273         DRM_DEBUG_KMS("%s\n", __FILE__);
274
275         if (hdmi_ops && hdmi_ops->get_max_resol)
276                 hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height);
277 }
278
279 static void drm_hdmi_commit(struct device *subdrv_dev)
280 {
281         struct drm_hdmi_context *ctx = to_context(subdrv_dev);
282
283         DRM_DEBUG_KMS("%s\n", __FILE__);
284
285         if (hdmi_ops && hdmi_ops->commit)
286                 hdmi_ops->commit(ctx->hdmi_ctx->ctx);
287 }
288
289 static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
290 {
291         struct drm_hdmi_context *ctx = to_context(subdrv_dev);
292
293         DRM_DEBUG_KMS("%s\n", __FILE__);
294
295         if (mixer_ops && mixer_ops->dpms)
296                 mixer_ops->dpms(ctx->mixer_ctx->ctx, mode);
297
298         if (hdmi_ops && hdmi_ops->dpms)
299                 hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode);
300 }
301
302 static void drm_hdmi_apply(struct device *subdrv_dev)
303 {
304         struct drm_hdmi_context *ctx = to_context(subdrv_dev);
305         int i;
306
307         DRM_DEBUG_KMS("%s\n", __FILE__);
308
309         for (i = 0; i < MIXER_WIN_NR; i++) {
310                 if (!ctx->enabled[i])
311                         continue;
312                 if (mixer_ops && mixer_ops->win_commit)
313                         mixer_ops->win_commit(ctx->mixer_ctx->ctx, i);
314         }
315
316         if (hdmi_ops && hdmi_ops->commit)
317                 hdmi_ops->commit(ctx->hdmi_ctx->ctx);
318 }
319
320 static struct rockchip_drm_manager_ops drm_hdmi_manager_ops = {
321         .dpms = drm_hdmi_dpms,
322         .apply = drm_hdmi_apply,
323         .enable_vblank = drm_hdmi_enable_vblank,
324         .disable_vblank = drm_hdmi_disable_vblank,
325         .wait_for_vblank = drm_hdmi_wait_for_vblank,
326         .mode_fixup = drm_hdmi_mode_fixup,
327         .mode_set = drm_hdmi_mode_set,
328         .get_max_resol = drm_hdmi_get_max_resol,
329         .commit = drm_hdmi_commit,
330 };
331
332 static void drm_mixer_mode_set(struct device *subdrv_dev,
333                 struct rockchip_drm_overlay *overlay)
334 {
335         struct drm_hdmi_context *ctx = to_context(subdrv_dev);
336
337         DRM_DEBUG_KMS("%s\n", __FILE__);
338
339         if (mixer_ops && mixer_ops->win_mode_set)
340                 mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay);
341 }
342
343 static void drm_mixer_commit(struct device *subdrv_dev, int zpos)
344 {
345         struct drm_hdmi_context *ctx = to_context(subdrv_dev);
346         int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
347
348         DRM_DEBUG_KMS("%s\n", __FILE__);
349
350         if (win < 0 || win > MIXER_WIN_NR) {
351                 DRM_ERROR("mixer window[%d] is wrong\n", win);
352                 return;
353         }
354
355         if (mixer_ops && mixer_ops->win_commit)
356                 mixer_ops->win_commit(ctx->mixer_ctx->ctx, win);
357
358         ctx->enabled[win] = true;
359 }
360
361 static void drm_mixer_disable(struct device *subdrv_dev, int zpos)
362 {
363         struct drm_hdmi_context *ctx = to_context(subdrv_dev);
364         int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
365
366         DRM_DEBUG_KMS("%s\n", __FILE__);
367
368         if (win < 0 || win > MIXER_WIN_NR) {
369                 DRM_ERROR("mixer window[%d] is wrong\n", win);
370                 return;
371         }
372
373         if (mixer_ops && mixer_ops->win_disable)
374                 mixer_ops->win_disable(ctx->mixer_ctx->ctx, win);
375
376         ctx->enabled[win] = false;
377 }
378
379 static struct rockchip_drm_overlay_ops drm_hdmi_overlay_ops = {
380         .mode_set = drm_mixer_mode_set,
381         .commit = drm_mixer_commit,
382         .disable = drm_mixer_disable,
383 };
384
385 static struct rockchip_drm_manager hdmi_manager = {
386         .pipe           = -1,
387         .ops            = &drm_hdmi_manager_ops,
388         .overlay_ops    = &drm_hdmi_overlay_ops,
389         .display_ops    = &drm_hdmi_display_ops,
390 };
391
392 static int hdmi_subdrv_probe(struct drm_device *drm_dev,
393                 struct device *dev)
394 {
395         struct rockchip_drm_subdrv *subdrv = to_subdrv(dev);
396         struct drm_hdmi_context *ctx;
397
398         DRM_DEBUG_KMS("%s\n", __FILE__);
399
400         if (!hdmi_ctx) {
401                 DRM_ERROR("hdmi context not initialized.\n");
402                 return -EFAULT;
403         }
404
405         if (!mixer_ctx) {
406                 DRM_ERROR("mixer context not initialized.\n");
407                 return -EFAULT;
408         }
409
410         ctx = get_ctx_from_subdrv(subdrv);
411
412         if (!ctx) {
413                 DRM_ERROR("no drm hdmi context.\n");
414                 return -EFAULT;
415         }
416
417         ctx->hdmi_ctx = hdmi_ctx;
418         ctx->mixer_ctx = mixer_ctx;
419
420         ctx->hdmi_ctx->drm_dev = drm_dev;
421         ctx->mixer_ctx->drm_dev = drm_dev;
422
423         if (mixer_ops->iommu_on)
424                 mixer_ops->iommu_on(ctx->mixer_ctx->ctx, true);
425
426         return 0;
427 }
428
429 static void hdmi_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
430 {
431         struct drm_hdmi_context *ctx;
432         struct rockchip_drm_subdrv *subdrv = to_subdrv(dev);
433
434         ctx = get_ctx_from_subdrv(subdrv);
435
436         if (mixer_ops->iommu_on)
437                 mixer_ops->iommu_on(ctx->mixer_ctx->ctx, false);
438 }
439
440 static int rockchip_drm_hdmi_probe(struct platform_device *pdev)
441 {
442         struct device *dev = &pdev->dev;
443         struct rockchip_drm_subdrv *subdrv;
444         struct drm_hdmi_context *ctx;
445
446         DRM_DEBUG_KMS("%s\n", __FILE__);
447
448         ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
449         if (!ctx) {
450                 DRM_LOG_KMS("failed to alloc common hdmi context.\n");
451                 return -ENOMEM;
452         }
453
454         subdrv = &ctx->subdrv;
455
456         subdrv->dev = dev;
457         subdrv->manager = &hdmi_manager;
458         subdrv->probe = hdmi_subdrv_probe;
459         subdrv->remove = hdmi_subdrv_remove;
460
461         platform_set_drvdata(pdev, subdrv);
462
463         rockchip_drm_subdrv_register(subdrv);
464
465         return 0;
466 }
467
468 static int rockchip_drm_hdmi_remove(struct platform_device *pdev)
469 {
470         struct drm_hdmi_context *ctx = platform_get_drvdata(pdev);
471
472         DRM_DEBUG_KMS("%s\n", __FILE__);
473
474         rockchip_drm_subdrv_unregister(&ctx->subdrv);
475
476         return 0;
477 }
478
479 struct platform_driver rockchip_drm_common_hdmi_driver = {
480         .probe          = rockchip_drm_hdmi_probe,
481         .remove         = rockchip_drm_hdmi_remove,
482         .driver         = {
483                 .name   = "rockchip-drm-hdmi",
484                 .owner  = THIS_MODULE,
485         },
486 };