aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSeung-Woo Kim <sw0312.kim@samsung.com>2011-12-21 03:39:39 -0500
committerInki Dae <inki.dae@samsung.com>2011-12-28 21:21:42 -0500
commitd84083268bd707ebb8ed2f4fc26ebc7a0c453a83 (patch)
tree864dbd5eb72fb7ac1865a7aa72dce3dd5f7a4c73
parentc32b06ef7dd63a5e0b14a02c96ef308796c157cc (diff)
drm/exynos: added hdmi display support
This patch is hdmi display support for exynos drm driver. There is already v4l2 based exynos hdmi driver in drivers/media/video/s5p-tv and some low level code is already in s5p-tv and even headers for register define are almost same. but in this patch, we decide not to consider separated common code with s5p-tv. Exynos HDMI is composed of 5 blocks, mixer, vp, hdmi, hdmiphy and ddc. 1. mixer. The piece of hardware responsible for mixing and blending multiple data inputs before passing it to an output device. The mixer is capable of handling up to three image layers. One is the output of VP. Other two are images in RGB format. The blending factor, and layers' priority are controlled by mixer's registers. The output is passed to HDMI. 2. vp (video processor). It is used for processing of NV12/NV21 data. An image stored in RAM is accessed by DMA. The output in YCbCr444 format is send to mixer. 3. hdmi. The piece of HW responsible for generation of HDMI packets. It takes pixel data from mixer and transforms it into data frames. The output is send to HDMIPHY interface. 4. hdmiphy. Physical interface for HDMI. Its duties are sending HDMI packets to HDMI connector. Basically, it contains a PLL that produces source clock for mixer, vp and hdmi. 5. ddc (display data channel). It is dedicated i2c channel to exchange display information as edid with display monitor. With plane support, exynos hdmi driver fully supports two mixer layes and vp layer. Also vp layer supports multi buffer plane pixel formats having non contigus memory spaces. In exynos drm driver, common drm_hdmi driver to interface with drm framework has opertion pointers for mixer and hdmi. this drm_hdmi driver is registered as sub driver of exynos_drm. hdmi has hdmiphy and ddc i2c clients and controls them. mixer controls all overlay layers in both mixer and vp. Vblank interrupts for hdmi are handled by mixer internally because drm framework cannot support multiple irq id. And pipe number is used to check which display device irq happens. History v2: this version - drm plane feature support to handle overlay layers. - multi buffer plane pixel format support for vp layer. - vp layer support RFCv1: original - at https://lkml.org/lkml/2011/11/4/164 Signed-off-by: Seung-Woo Kim <sw0312.kim@samsung.com> Signed-off-by: Inki Dae <inki.dae@samsung.com> Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
-rw-r--r--drivers/gpu/drm/exynos/Kconfig7
-rw-r--r--drivers/gpu/drm/exynos/Makefile2
-rw-r--r--drivers/gpu/drm/exynos/exynos_ddc.c58
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_hdmi.c439
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_hdmi.h73
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.c1176
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.h87
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmiphy.c58
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c1070
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.h92
-rw-r--r--drivers/gpu/drm/exynos/regs-hdmi.h147
-rw-r--r--drivers/gpu/drm/exynos/regs-mixer.h141
-rw-r--r--drivers/gpu/drm/exynos/regs-vp.h91
-rw-r--r--include/drm/exynos_drm.h27
14 files changed, 3468 insertions, 0 deletions
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 847466aab435..f9aaa56eae07 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -18,3 +18,10 @@ config DRM_EXYNOS_FIMD
18 help 18 help
19 Choose this option if you want to use Exynos FIMD for DRM. 19 Choose this option if you want to use Exynos FIMD for DRM.
20 If M is selected, the module will be called exynos_drm_fimd 20 If M is selected, the module will be called exynos_drm_fimd
21
22config DRM_EXYNOS_HDMI
23 tristate "Exynos DRM HDMI"
24 depends on DRM_EXYNOS
25 help
26 Choose this option if you want to use Exynos HDMI for DRM.
27 If M is selected, the module will be called exynos_drm_hdmi
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index c99127214f8e..395e69c9a96e 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -10,3 +10,5 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \
10 10
11obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o 11obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o
12obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o 12obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
13obj-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o exynos_ddc.o \
14 exynos_hdmiphy.o exynos_drm_hdmi.o
diff --git a/drivers/gpu/drm/exynos/exynos_ddc.c b/drivers/gpu/drm/exynos/exynos_ddc.c
new file mode 100644
index 000000000000..84b614fe26fd
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_ddc.c
@@ -0,0 +1,58 @@
1/*
2 * Copyright (C) 2011 Samsung Electronics Co.Ltd
3 * Authors:
4 * Seung-Woo Kim <sw0312.kim@samsung.com>
5 * Inki Dae <inki.dae@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/i2c.h>
18#include <linux/module.h>
19
20
21#include "exynos_drm_drv.h"
22#include "exynos_hdmi.h"
23
24static int s5p_ddc_probe(struct i2c_client *client,
25 const struct i2c_device_id *dev_id)
26{
27 hdmi_attach_ddc_client(client);
28
29 dev_info(&client->adapter->dev, "attached s5p_ddc "
30 "into i2c adapter successfully\n");
31
32 return 0;
33}
34
35static int s5p_ddc_remove(struct i2c_client *client)
36{
37 dev_info(&client->adapter->dev, "detached s5p_ddc "
38 "from i2c adapter successfully\n");
39
40 return 0;
41}
42
43static struct i2c_device_id ddc_idtable[] = {
44 {"s5p_ddc", 0},
45 { },
46};
47
48struct i2c_driver ddc_driver = {
49 .driver = {
50 .name = "s5p_ddc",
51 .owner = THIS_MODULE,
52 },
53 .id_table = ddc_idtable,
54 .probe = s5p_ddc_probe,
55 .remove = __devexit_p(s5p_ddc_remove),
56 .command = NULL,
57};
58EXPORT_SYMBOL(ddc_driver);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
new file mode 100644
index 000000000000..ed8a319ed84b
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
@@ -0,0 +1,439 @@
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. */
33static struct exynos_hdmi_display_ops *hdmi_display_ops;
34static struct exynos_hdmi_manager_ops *hdmi_manager_ops;
35static struct exynos_hdmi_overlay_ops *hdmi_overlay_ops;
36
37struct drm_hdmi_context {
38 struct exynos_drm_subdrv subdrv;
39 struct exynos_drm_hdmi_context *hdmi_ctx;
40 struct exynos_drm_hdmi_context *mixer_ctx;
41 struct work_struct work;
42};
43
44void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops
45 *display_ops)
46{
47 DRM_DEBUG_KMS("%s\n", __FILE__);
48
49 if (display_ops)
50 hdmi_display_ops = display_ops;
51}
52EXPORT_SYMBOL(exynos_drm_display_ops_register);
53
54void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops
55 *manager_ops)
56{
57 DRM_DEBUG_KMS("%s\n", __FILE__);
58
59 if (manager_ops)
60 hdmi_manager_ops = manager_ops;
61}
62EXPORT_SYMBOL(exynos_drm_manager_ops_register);
63
64void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops
65 *overlay_ops)
66{
67 DRM_DEBUG_KMS("%s\n", __FILE__);
68
69 if (overlay_ops)
70 hdmi_overlay_ops = overlay_ops;
71}
72EXPORT_SYMBOL(exynos_drm_overlay_ops_register);
73
74static bool drm_hdmi_is_connected(struct device *dev)
75{
76 struct drm_hdmi_context *ctx = to_context(dev);
77
78 DRM_DEBUG_KMS("%s\n", __FILE__);
79
80 if (hdmi_display_ops && hdmi_display_ops->is_connected)
81 return hdmi_display_ops->is_connected(ctx->hdmi_ctx->ctx);
82
83 return false;
84}
85
86static int drm_hdmi_get_edid(struct device *dev,
87 struct drm_connector *connector, u8 *edid, int len)
88{
89 struct drm_hdmi_context *ctx = to_context(dev);
90
91 DRM_DEBUG_KMS("%s\n", __FILE__);
92
93 if (hdmi_display_ops && hdmi_display_ops->get_edid)
94 return hdmi_display_ops->get_edid(ctx->hdmi_ctx->ctx,
95 connector, edid, len);
96
97 return 0;
98}
99
100static int drm_hdmi_check_timing(struct device *dev, void *timing)
101{
102 struct drm_hdmi_context *ctx = to_context(dev);
103
104 DRM_DEBUG_KMS("%s\n", __FILE__);
105
106 if (hdmi_display_ops && hdmi_display_ops->check_timing)
107 return hdmi_display_ops->check_timing(ctx->hdmi_ctx->ctx,
108 timing);
109
110 return 0;
111}
112
113static int drm_hdmi_power_on(struct device *dev, int mode)
114{
115 struct drm_hdmi_context *ctx = to_context(dev);
116
117 DRM_DEBUG_KMS("%s\n", __FILE__);
118
119 if (hdmi_display_ops && hdmi_display_ops->power_on)
120 return hdmi_display_ops->power_on(ctx->hdmi_ctx->ctx, mode);
121
122 return 0;
123}
124
125static struct exynos_drm_display_ops drm_hdmi_display_ops = {
126 .type = EXYNOS_DISPLAY_TYPE_HDMI,
127 .is_connected = drm_hdmi_is_connected,
128 .get_edid = drm_hdmi_get_edid,
129 .check_timing = drm_hdmi_check_timing,
130 .power_on = drm_hdmi_power_on,
131};
132
133static int drm_hdmi_enable_vblank(struct device *subdrv_dev)
134{
135 struct drm_hdmi_context *ctx = to_context(subdrv_dev);
136 struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
137 struct exynos_drm_manager *manager = &subdrv->manager;
138
139 DRM_DEBUG_KMS("%s\n", __FILE__);
140
141 if (hdmi_overlay_ops && hdmi_overlay_ops->enable_vblank)
142 return hdmi_overlay_ops->enable_vblank(ctx->mixer_ctx->ctx,
143 manager->pipe);
144
145 return 0;
146}
147
148static void drm_hdmi_disable_vblank(struct device *subdrv_dev)
149{
150 struct drm_hdmi_context *ctx = to_context(subdrv_dev);
151
152 DRM_DEBUG_KMS("%s\n", __FILE__);
153
154 if (hdmi_overlay_ops && hdmi_overlay_ops->disable_vblank)
155 return hdmi_overlay_ops->disable_vblank(ctx->mixer_ctx->ctx);
156}
157
158static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
159{
160 struct drm_hdmi_context *ctx = to_context(subdrv_dev);
161
162 DRM_DEBUG_KMS("%s\n", __FILE__);
163
164 if (hdmi_manager_ops && hdmi_manager_ops->mode_set)
165 hdmi_manager_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
166}
167
168static void drm_hdmi_commit(struct device *subdrv_dev)
169{
170 struct drm_hdmi_context *ctx = to_context(subdrv_dev);
171
172 DRM_DEBUG_KMS("%s\n", __FILE__);
173
174 if (hdmi_manager_ops && hdmi_manager_ops->commit)
175 hdmi_manager_ops->commit(ctx->hdmi_ctx->ctx);
176}
177
178static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
179{
180 struct drm_hdmi_context *ctx = to_context(subdrv_dev);
181
182 DRM_DEBUG_KMS("%s\n", __FILE__);
183
184 switch (mode) {
185 case DRM_MODE_DPMS_ON:
186 break;
187 case DRM_MODE_DPMS_STANDBY:
188 case DRM_MODE_DPMS_SUSPEND:
189 case DRM_MODE_DPMS_OFF:
190 if (hdmi_manager_ops && hdmi_manager_ops->disable)
191 hdmi_manager_ops->disable(ctx->hdmi_ctx->ctx);
192 break;
193 default:
194 DRM_DEBUG_KMS("unkown dps mode: %d\n", mode);
195 break;
196 }
197}
198
199static struct exynos_drm_manager_ops drm_hdmi_manager_ops = {
200 .dpms = drm_hdmi_dpms,
201 .enable_vblank = drm_hdmi_enable_vblank,
202 .disable_vblank = drm_hdmi_disable_vblank,
203 .mode_set = drm_hdmi_mode_set,
204 .commit = drm_hdmi_commit,
205};
206
207static void drm_mixer_mode_set(struct device *subdrv_dev,
208 struct exynos_drm_overlay *overlay)
209{
210 struct drm_hdmi_context *ctx = to_context(subdrv_dev);
211
212 DRM_DEBUG_KMS("%s\n", __FILE__);
213
214 if (hdmi_overlay_ops && hdmi_overlay_ops->win_mode_set)
215 hdmi_overlay_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay);
216}
217
218static void drm_mixer_commit(struct device *subdrv_dev, int zpos)
219{
220 struct drm_hdmi_context *ctx = to_context(subdrv_dev);
221
222 DRM_DEBUG_KMS("%s\n", __FILE__);
223
224 if (hdmi_overlay_ops && hdmi_overlay_ops->win_commit)
225 hdmi_overlay_ops->win_commit(ctx->mixer_ctx->ctx, zpos);
226}
227
228static void drm_mixer_disable(struct device *subdrv_dev, int zpos)
229{
230 struct drm_hdmi_context *ctx = to_context(subdrv_dev);
231
232 DRM_DEBUG_KMS("%s\n", __FILE__);
233
234 if (hdmi_overlay_ops && hdmi_overlay_ops->win_disable)
235 hdmi_overlay_ops->win_disable(ctx->mixer_ctx->ctx, zpos);
236}
237
238static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = {
239 .mode_set = drm_mixer_mode_set,
240 .commit = drm_mixer_commit,
241 .disable = drm_mixer_disable,
242};
243
244
245static int hdmi_subdrv_probe(struct drm_device *drm_dev,
246 struct device *dev)
247{
248 struct exynos_drm_subdrv *subdrv = to_subdrv(dev);
249 struct drm_hdmi_context *ctx;
250 struct platform_device *pdev = to_platform_device(dev);
251 struct exynos_drm_common_hdmi_pd *pd;
252 int ret;
253
254 DRM_DEBUG_KMS("%s\n", __FILE__);
255
256 pd = pdev->dev.platform_data;
257
258 if (!pd) {
259 DRM_DEBUG_KMS("platform data is null.\n");
260 return -EFAULT;
261 }
262
263 if (!pd->hdmi_dev) {
264 DRM_DEBUG_KMS("hdmi device is null.\n");
265 return -EFAULT;
266 }
267
268 if (!pd->mixer_dev) {
269 DRM_DEBUG_KMS("mixer device is null.\n");
270 return -EFAULT;
271 }
272
273 ret = platform_driver_register(&hdmi_driver);
274 if (ret) {
275 DRM_DEBUG_KMS("failed to register hdmi driver.\n");
276 return ret;
277 }
278
279 ret = platform_driver_register(&mixer_driver);
280 if (ret) {
281 DRM_DEBUG_KMS("failed to register mixer driver.\n");
282 goto err_hdmidrv;
283 }
284
285 ctx = get_ctx_from_subdrv(subdrv);
286
287 ctx->hdmi_ctx = (struct exynos_drm_hdmi_context *)
288 to_context(pd->hdmi_dev);
289 if (!ctx->hdmi_ctx) {
290 DRM_DEBUG_KMS("hdmi context is null.\n");
291 ret = -EFAULT;
292 goto err_mixerdrv;
293 }
294
295 ctx->hdmi_ctx->drm_dev = drm_dev;
296
297 ctx->mixer_ctx = (struct exynos_drm_hdmi_context *)
298 to_context(pd->mixer_dev);
299 if (!ctx->mixer_ctx) {
300 DRM_DEBUG_KMS("mixer context is null.\n");
301 ret = -EFAULT;
302 goto err_mixerdrv;
303 }
304
305 ctx->mixer_ctx->drm_dev = drm_dev;
306
307 return 0;
308
309err_mixerdrv:
310 platform_driver_unregister(&mixer_driver);
311err_hdmidrv:
312 platform_driver_unregister(&hdmi_driver);
313 return ret;
314}
315
316static void hdmi_subdrv_remove(struct drm_device *drm_dev)
317{
318 DRM_DEBUG_KMS("%s\n", __FILE__);
319
320 platform_driver_unregister(&hdmi_driver);
321 platform_driver_unregister(&mixer_driver);
322}
323
324static void exynos_drm_hdmi_late_probe(struct work_struct *work)
325{
326 struct drm_hdmi_context *ctx = container_of(work,
327 struct drm_hdmi_context, work);
328
329 /*
330 * this function calls subdrv->probe() so this must be called
331 * after probe context.
332 *
333 * PS. subdrv->probe() will call platform_driver_register() to probe
334 * hdmi and mixer driver.
335 */
336 exynos_drm_subdrv_register(&ctx->subdrv);
337}
338
339static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
340{
341 struct device *dev = &pdev->dev;
342 struct exynos_drm_subdrv *subdrv;
343 struct drm_hdmi_context *ctx;
344
345 DRM_DEBUG_KMS("%s\n", __FILE__);
346
347 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
348 if (!ctx) {
349 DRM_LOG_KMS("failed to alloc common hdmi context.\n");
350 return -ENOMEM;
351 }
352
353 subdrv = &ctx->subdrv;
354
355 subdrv->probe = hdmi_subdrv_probe;
356 subdrv->remove = hdmi_subdrv_remove;
357 subdrv->manager.pipe = -1;
358 subdrv->manager.ops = &drm_hdmi_manager_ops;
359 subdrv->manager.overlay_ops = &drm_hdmi_overlay_ops;
360 subdrv->manager.display_ops = &drm_hdmi_display_ops;
361 subdrv->manager.dev = dev;
362
363 platform_set_drvdata(pdev, subdrv);
364
365 INIT_WORK(&ctx->work, exynos_drm_hdmi_late_probe);
366
367 schedule_work(&ctx->work);
368
369 return 0;
370}
371
372static int hdmi_runtime_suspend(struct device *dev)
373{
374 DRM_DEBUG_KMS("%s\n", __FILE__);
375
376 return 0;
377}
378
379static int hdmi_runtime_resume(struct device *dev)
380{
381 DRM_DEBUG_KMS("%s\n", __FILE__);
382
383 return 0;
384}
385
386static const struct dev_pm_ops hdmi_pm_ops = {
387 .runtime_suspend = hdmi_runtime_suspend,
388 .runtime_resume = hdmi_runtime_resume,
389};
390
391static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev)
392{
393 struct drm_hdmi_context *ctx = platform_get_drvdata(pdev);
394
395 DRM_DEBUG_KMS("%s\n", __FILE__);
396
397 exynos_drm_subdrv_unregister(&ctx->subdrv);
398 kfree(ctx);
399
400 return 0;
401}
402
403static struct platform_driver exynos_drm_common_hdmi_driver = {
404 .probe = exynos_drm_hdmi_probe,
405 .remove = __devexit_p(exynos_drm_hdmi_remove),
406 .driver = {
407 .name = "exynos-drm-hdmi",
408 .owner = THIS_MODULE,
409 .pm = &hdmi_pm_ops,
410 },
411};
412
413static int __init exynos_drm_hdmi_init(void)
414{
415 int ret;
416
417 DRM_DEBUG_KMS("%s\n", __FILE__);
418
419 ret = platform_driver_register(&exynos_drm_common_hdmi_driver);
420 if (ret) {
421 DRM_DEBUG_KMS("failed to register hdmi common driver.\n");
422 return ret;
423 }
424
425 return ret;
426}
427
428static void __exit exynos_drm_hdmi_exit(void)
429{
430 platform_driver_unregister(&exynos_drm_common_hdmi_driver);
431}
432
433module_init(exynos_drm_hdmi_init);
434module_exit(exynos_drm_hdmi_exit);
435
436MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
437MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>");
438MODULE_DESCRIPTION("Samsung SoC DRM HDMI Driver");
439MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
new file mode 100644
index 000000000000..3c29f790ee45
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
@@ -0,0 +1,73 @@
1/* exynos_drm_hdmi.h
2 *
3 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
4 * Authoer: Inki Dae <inki.dae@samsung.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26#ifndef _EXYNOS_DRM_HDMI_H_
27#define _EXYNOS_DRM_HDMI_H_
28
29/*
30 * exynos hdmi common context structure.
31 *
32 * @drm_dev: pointer to drm_device.
33 * @ctx: pointer to the context of specific device driver.
34 * this context should be hdmi_context or mixer_context.
35 */
36struct exynos_drm_hdmi_context {
37 struct drm_device *drm_dev;
38 void *ctx;
39};
40
41struct exynos_hdmi_display_ops {
42 bool (*is_connected)(void *ctx);
43 int (*get_edid)(void *ctx, struct drm_connector *connector,
44 u8 *edid, int len);
45 int (*check_timing)(void *ctx, void *timing);
46 int (*power_on)(void *ctx, int mode);
47};
48
49struct exynos_hdmi_manager_ops {
50 void (*mode_set)(void *ctx, void *mode);
51 void (*commit)(void *ctx);
52 void (*disable)(void *ctx);
53};
54
55struct exynos_hdmi_overlay_ops {
56 int (*enable_vblank)(void *ctx, int pipe);
57 void (*disable_vblank)(void *ctx);
58 void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay);
59 void (*win_commit)(void *ctx, int zpos);
60 void (*win_disable)(void *ctx, int zpos);
61};
62
63extern struct platform_driver hdmi_driver;
64extern struct platform_driver mixer_driver;
65
66void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops
67 *display_ops);
68void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops
69 *manager_ops);
70void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops
71 *overlay_ops);
72
73#endif
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
new file mode 100644
index 000000000000..f48f7ce92f5f
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -0,0 +1,1176 @@
1/*
2 * Copyright (C) 2011 Samsung Electronics Co.Ltd
3 * Authors:
4 * Seung-Woo Kim <sw0312.kim@samsung.com>
5 * Inki Dae <inki.dae@samsung.com>
6 * Joonyoung Shim <jy0922.shim@samsung.com>
7 *
8 * Based on drivers/media/video/s5p-tv/hdmi_drv.c
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version.
14 *
15 */
16
17#include "drmP.h"
18#include "drm_edid.h"
19#include "drm_crtc_helper.h"
20
21#include "regs-hdmi.h"
22
23#include <linux/kernel.h>
24#include <linux/spinlock.h>
25#include <linux/wait.h>
26#include <linux/i2c.h>
27#include <linux/module.h>
28#include <linux/platform_device.h>
29#include <linux/interrupt.h>
30#include <linux/irq.h>
31#include <linux/delay.h>
32#include <linux/pm_runtime.h>
33#include <linux/clk.h>
34#include <linux/regulator/consumer.h>
35
36#include <drm/exynos_drm.h>
37
38#include "exynos_drm_drv.h"
39#include "exynos_drm_hdmi.h"
40
41#include "exynos_hdmi.h"
42
43#define HDMI_OVERLAY_NUMBER 3
44#define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev))
45
46static const u8 hdmiphy_conf27[32] = {
47 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
48 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
49 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
50 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
51};
52
53static const u8 hdmiphy_conf27_027[32] = {
54 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
55 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
56 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
57 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
58};
59
60static const u8 hdmiphy_conf74_175[32] = {
61 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
62 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
63 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
64 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
65};
66
67static const u8 hdmiphy_conf74_25[32] = {
68 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
69 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
70 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
71 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
72};
73
74static const u8 hdmiphy_conf148_5[32] = {
75 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
76 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
77 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
78 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
79};
80
81struct hdmi_tg_regs {
82 u8 cmd;
83 u8 h_fsz_l;
84 u8 h_fsz_h;
85 u8 hact_st_l;
86 u8 hact_st_h;
87 u8 hact_sz_l;
88 u8 hact_sz_h;
89 u8 v_fsz_l;
90 u8 v_fsz_h;
91 u8 vsync_l;
92 u8 vsync_h;
93 u8 vsync2_l;
94 u8 vsync2_h;
95 u8 vact_st_l;
96 u8 vact_st_h;
97 u8 vact_sz_l;
98 u8 vact_sz_h;
99 u8 field_chg_l;
100 u8 field_chg_h;
101 u8 vact_st2_l;
102 u8 vact_st2_h;
103 u8 vsync_top_hdmi_l;
104 u8 vsync_top_hdmi_h;
105 u8 vsync_bot_hdmi_l;
106 u8 vsync_bot_hdmi_h;
107 u8 field_top_hdmi_l;
108 u8 field_top_hdmi_h;
109 u8 field_bot_hdmi_l;
110 u8 field_bot_hdmi_h;
111};
112
113struct hdmi_core_regs {
114 u8 h_blank[2];
115 u8 v_blank[3];
116 u8 h_v_line[3];
117 u8 vsync_pol[1];
118 u8 int_pro_mode[1];
119 u8 v_blank_f[3];
120 u8 h_sync_gen[3];
121 u8 v_sync_gen1[3];
122 u8 v_sync_gen2[3];
123 u8 v_sync_gen3[3];
124};
125
126struct hdmi_preset_conf {
127 struct hdmi_core_regs core;
128 struct hdmi_tg_regs tg;
129};
130
131static const struct hdmi_preset_conf hdmi_conf_480p = {
132 .core = {
133 .h_blank = {0x8a, 0x00},
134 .v_blank = {0x0d, 0x6a, 0x01},
135 .h_v_line = {0x0d, 0xa2, 0x35},
136 .vsync_pol = {0x01},
137 .int_pro_mode = {0x00},
138 .v_blank_f = {0x00, 0x00, 0x00},
139 .h_sync_gen = {0x0e, 0x30, 0x11},
140 .v_sync_gen1 = {0x0f, 0x90, 0x00},
141 /* other don't care */
142 },
143 .tg = {
144 0x00, /* cmd */
145 0x5a, 0x03, /* h_fsz */
146 0x8a, 0x00, 0xd0, 0x02, /* hact */
147 0x0d, 0x02, /* v_fsz */
148 0x01, 0x00, 0x33, 0x02, /* vsync */
149 0x2d, 0x00, 0xe0, 0x01, /* vact */
150 0x33, 0x02, /* field_chg */
151 0x49, 0x02, /* vact_st2 */
152 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
153 0x01, 0x00, 0x33, 0x02, /* field top/bot */
154 },
155};
156
157static const struct hdmi_preset_conf hdmi_conf_720p60 = {
158 .core = {
159 .h_blank = {0x72, 0x01},
160 .v_blank = {0xee, 0xf2, 0x00},
161 .h_v_line = {0xee, 0x22, 0x67},
162 .vsync_pol = {0x00},
163 .int_pro_mode = {0x00},
164 .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
165 .h_sync_gen = {0x6c, 0x50, 0x02},
166 .v_sync_gen1 = {0x0a, 0x50, 0x00},
167 .v_sync_gen2 = {0x01, 0x10, 0x00},
168 .v_sync_gen3 = {0x01, 0x10, 0x00},
169 /* other don't care */
170 },
171 .tg = {
172 0x00, /* cmd */
173 0x72, 0x06, /* h_fsz */
174 0x71, 0x01, 0x01, 0x05, /* hact */
175 0xee, 0x02, /* v_fsz */
176 0x01, 0x00, 0x33, 0x02, /* vsync */
177 0x1e, 0x00, 0xd0, 0x02, /* vact */
178 0x33, 0x02, /* field_chg */
179 0x49, 0x02, /* vact_st2 */
180 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
181 0x01, 0x00, 0x33, 0x02, /* field top/bot */
182 },
183};
184
185static const struct hdmi_preset_conf hdmi_conf_1080i50 = {
186 .core = {
187 .h_blank = {0xd0, 0x02},
188 .v_blank = {0x32, 0xB2, 0x00},
189 .h_v_line = {0x65, 0x04, 0xa5},
190 .vsync_pol = {0x00},
191 .int_pro_mode = {0x01},
192 .v_blank_f = {0x49, 0x2A, 0x23},
193 .h_sync_gen = {0x0E, 0xEA, 0x08},
194 .v_sync_gen1 = {0x07, 0x20, 0x00},
195 .v_sync_gen2 = {0x39, 0x42, 0x23},
196 .v_sync_gen3 = {0x38, 0x87, 0x73},
197 /* other don't care */
198 },
199 .tg = {
200 0x00, /* cmd */
201 0x50, 0x0A, /* h_fsz */
202 0xCF, 0x02, 0x81, 0x07, /* hact */
203 0x65, 0x04, /* v_fsz */
204 0x01, 0x00, 0x33, 0x02, /* vsync */
205 0x16, 0x00, 0x1c, 0x02, /* vact */
206 0x33, 0x02, /* field_chg */
207 0x49, 0x02, /* vact_st2 */
208 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
209 0x01, 0x00, 0x33, 0x02, /* field top/bot */
210 },
211};
212
213static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
214 .core = {
215 .h_blank = {0xd0, 0x02},
216 .v_blank = {0x65, 0x6c, 0x01},
217 .h_v_line = {0x65, 0x04, 0xa5},
218 .vsync_pol = {0x00},
219 .int_pro_mode = {0x00},
220 .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
221 .h_sync_gen = {0x0e, 0xea, 0x08},
222 .v_sync_gen1 = {0x09, 0x40, 0x00},
223 .v_sync_gen2 = {0x01, 0x10, 0x00},
224 .v_sync_gen3 = {0x01, 0x10, 0x00},
225 /* other don't care */
226 },
227 .tg = {
228 0x00, /* cmd */
229 0x50, 0x0A, /* h_fsz */
230 0xCF, 0x02, 0x81, 0x07, /* hact */
231 0x65, 0x04, /* v_fsz */
232 0x01, 0x00, 0x33, 0x02, /* vsync */
233 0x2d, 0x00, 0x38, 0x04, /* vact */
234 0x33, 0x02, /* field_chg */
235 0x48, 0x02, /* vact_st2 */
236 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
237 0x01, 0x00, 0x33, 0x02, /* field top/bot */
238 },
239};
240
241static const struct hdmi_preset_conf hdmi_conf_1080i60 = {
242 .core = {
243 .h_blank = {0x18, 0x01},
244 .v_blank = {0x32, 0xB2, 0x00},
245 .h_v_line = {0x65, 0x84, 0x89},
246 .vsync_pol = {0x00},
247 .int_pro_mode = {0x01},
248 .v_blank_f = {0x49, 0x2A, 0x23},
249 .h_sync_gen = {0x56, 0x08, 0x02},
250 .v_sync_gen1 = {0x07, 0x20, 0x00},
251 .v_sync_gen2 = {0x39, 0x42, 0x23},
252 .v_sync_gen3 = {0xa4, 0x44, 0x4a},
253 /* other don't care */
254 },
255 .tg = {
256 0x00, /* cmd */
257 0x98, 0x08, /* h_fsz */
258 0x17, 0x01, 0x81, 0x07, /* hact */
259 0x65, 0x04, /* v_fsz */
260 0x01, 0x00, 0x33, 0x02, /* vsync */
261 0x16, 0x00, 0x1c, 0x02, /* vact */
262 0x33, 0x02, /* field_chg */
263 0x49, 0x02, /* vact_st2 */
264 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
265 0x01, 0x00, 0x33, 0x02, /* field top/bot */
266 },
267};
268
269static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
270 .core = {
271 .h_blank = {0x18, 0x01},
272 .v_blank = {0x65, 0x6c, 0x01},
273 .h_v_line = {0x65, 0x84, 0x89},
274 .vsync_pol = {0x00},
275 .int_pro_mode = {0x00},
276 .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
277 .h_sync_gen = {0x56, 0x08, 0x02},
278 .v_sync_gen1 = {0x09, 0x40, 0x00},
279 .v_sync_gen2 = {0x01, 0x10, 0x00},
280 .v_sync_gen3 = {0x01, 0x10, 0x00},
281 /* other don't care */
282 },
283 .tg = {
284 0x00, /* cmd */
285 0x98, 0x08, /* h_fsz */
286 0x17, 0x01, 0x81, 0x07, /* hact */
287 0x65, 0x04, /* v_fsz */
288 0x01, 0x00, 0x33, 0x02, /* vsync */
289 0x2d, 0x00, 0x38, 0x04, /* vact */
290 0x33, 0x02, /* field_chg */
291 0x48, 0x02, /* vact_st2 */
292 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
293 0x01, 0x00, 0x33, 0x02, /* field top/bot */
294 },
295};
296
297static const struct hdmi_conf hdmi_confs[] = {
298 { 1280, 720, 60, false, hdmiphy_conf74_25, &hdmi_conf_720p60 },
299 { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p60 },
300 { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p },
301 { 1920, 1080, 50, true, hdmiphy_conf74_25, &hdmi_conf_1080i50 },
302 { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 },
303 { 1920, 1080, 60, true, hdmiphy_conf74_25, &hdmi_conf_1080i60 },
304 { 1920, 1080, 60, false, hdmiphy_conf148_5, &hdmi_conf_1080p60 },
305};
306
307
308static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
309{
310 return readl(hdata->regs + reg_id);
311}
312
313static inline void hdmi_reg_writeb(struct hdmi_context *hdata,
314 u32 reg_id, u8 value)
315{
316 writeb(value, hdata->regs + reg_id);
317}
318
319static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
320 u32 reg_id, u32 value, u32 mask)
321{
322 u32 old = readl(hdata->regs + reg_id);
323 value = (value & mask) | (old & ~mask);
324 writel(value, hdata->regs + reg_id);
325}
326
327static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
328{
329#define DUMPREG(reg_id) \
330 DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \
331 readl(hdata->regs + reg_id))
332 DRM_DEBUG_KMS("%s: ---- CONTROL REGISTERS ----\n", prefix);
333 DUMPREG(HDMI_INTC_FLAG);
334 DUMPREG(HDMI_INTC_CON);
335 DUMPREG(HDMI_HPD_STATUS);
336 DUMPREG(HDMI_PHY_RSTOUT);
337 DUMPREG(HDMI_PHY_VPLL);
338 DUMPREG(HDMI_PHY_CMU);
339 DUMPREG(HDMI_CORE_RSTOUT);
340
341 DRM_DEBUG_KMS("%s: ---- CORE REGISTERS ----\n", prefix);
342 DUMPREG(HDMI_CON_0);
343 DUMPREG(HDMI_CON_1);
344 DUMPREG(HDMI_CON_2);
345 DUMPREG(HDMI_SYS_STATUS);
346 DUMPREG(HDMI_PHY_STATUS);
347 DUMPREG(HDMI_STATUS_EN);
348 DUMPREG(HDMI_HPD);
349 DUMPREG(HDMI_MODE_SEL);
350 DUMPREG(HDMI_HPD_GEN);
351 DUMPREG(HDMI_DC_CONTROL);
352 DUMPREG(HDMI_VIDEO_PATTERN_GEN);
353
354 DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix);
355 DUMPREG(HDMI_H_BLANK_0);
356 DUMPREG(HDMI_H_BLANK_1);
357 DUMPREG(HDMI_V_BLANK_0);
358 DUMPREG(HDMI_V_BLANK_1);
359 DUMPREG(HDMI_V_BLANK_2);
360 DUMPREG(HDMI_H_V_LINE_0);
361 DUMPREG(HDMI_H_V_LINE_1);
362 DUMPREG(HDMI_H_V_LINE_2);
363 DUMPREG(HDMI_VSYNC_POL);
364 DUMPREG(HDMI_INT_PRO_MODE);
365 DUMPREG(HDMI_V_BLANK_F_0);
366 DUMPREG(HDMI_V_BLANK_F_1);
367 DUMPREG(HDMI_V_BLANK_F_2);
368 DUMPREG(HDMI_H_SYNC_GEN_0);
369 DUMPREG(HDMI_H_SYNC_GEN_1);
370 DUMPREG(HDMI_H_SYNC_GEN_2);
371 DUMPREG(HDMI_V_SYNC_GEN_1_0);
372 DUMPREG(HDMI_V_SYNC_GEN_1_1);
373 DUMPREG(HDMI_V_SYNC_GEN_1_2);
374 DUMPREG(HDMI_V_SYNC_GEN_2_0);
375 DUMPREG(HDMI_V_SYNC_GEN_2_1);
376 DUMPREG(HDMI_V_SYNC_GEN_2_2);
377 DUMPREG(HDMI_V_SYNC_GEN_3_0);
378 DUMPREG(HDMI_V_SYNC_GEN_3_1);
379 DUMPREG(HDMI_V_SYNC_GEN_3_2);
380
381 DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix);
382 DUMPREG(HDMI_TG_CMD);
383 DUMPREG(HDMI_TG_H_FSZ_L);
384 DUMPREG(HDMI_TG_H_FSZ_H);
385 DUMPREG(HDMI_TG_HACT_ST_L);
386 DUMPREG(HDMI_TG_HACT_ST_H);
387 DUMPREG(HDMI_TG_HACT_SZ_L);
388 DUMPREG(HDMI_TG_HACT_SZ_H);
389 DUMPREG(HDMI_TG_V_FSZ_L);
390 DUMPREG(HDMI_TG_V_FSZ_H);
391 DUMPREG(HDMI_TG_VSYNC_L);
392 DUMPREG(HDMI_TG_VSYNC_H);
393 DUMPREG(HDMI_TG_VSYNC2_L);
394 DUMPREG(HDMI_TG_VSYNC2_H);
395 DUMPREG(HDMI_TG_VACT_ST_L);
396 DUMPREG(HDMI_TG_VACT_ST_H);
397 DUMPREG(HDMI_TG_VACT_SZ_L);
398 DUMPREG(HDMI_TG_VACT_SZ_H);
399 DUMPREG(HDMI_TG_FIELD_CHG_L);
400 DUMPREG(HDMI_TG_FIELD_CHG_H);
401 DUMPREG(HDMI_TG_VACT_ST2_L);
402 DUMPREG(HDMI_TG_VACT_ST2_H);
403 DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L);
404 DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H);
405 DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L);
406 DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H);
407 DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L);
408 DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H);
409 DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L);
410 DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H);
411#undef DUMPREG
412}
413
414static int hdmi_conf_index(struct drm_display_mode *mode)
415{
416 int i;
417
418 for (i = 0; i < ARRAY_SIZE(hdmi_confs); ++i)
419 if (hdmi_confs[i].width == mode->hdisplay &&
420 hdmi_confs[i].height == mode->vdisplay &&
421 hdmi_confs[i].vrefresh == mode->vrefresh &&
422 hdmi_confs[i].interlace ==
423 ((mode->flags & DRM_MODE_FLAG_INTERLACE) ?
424 true : false))
425 return i;
426
427 return -1;
428}
429
430static bool hdmi_is_connected(void *ctx)
431{
432 struct hdmi_context *hdata = (struct hdmi_context *)ctx;
433 u32 val = hdmi_reg_read(hdata, HDMI_HPD_STATUS);
434
435 if (val)
436 return true;
437
438 return false;
439}
440
441static int hdmi_get_edid(void *ctx, struct drm_connector *connector,
442 u8 *edid, int len)
443{
444 struct edid *raw_edid;
445 struct hdmi_context *hdata = (struct hdmi_context *)ctx;
446
447 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
448
449 if (!hdata->ddc_port)
450 return -ENODEV;
451
452 raw_edid = drm_get_edid(connector, hdata->ddc_port->adapter);
453 if (raw_edid) {
454 memcpy(edid, raw_edid, min((1 + raw_edid->extensions)
455 * EDID_LENGTH, len));
456 DRM_DEBUG_KMS("width[%d] x height[%d]\n",
457 raw_edid->width_cm, raw_edid->height_cm);
458 } else {
459 return -ENODEV;
460 }
461
462 return 0;
463}
464
465static int hdmi_check_timing(void *ctx, void *timing)
466{
467 struct fb_videomode *check_timing = timing;
468 int i;
469
470 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
471
472 DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres,
473 check_timing->yres, check_timing->refresh,
474 check_timing->vmode);
475
476 for (i = 0; i < ARRAY_SIZE(hdmi_confs); ++i)
477 if (hdmi_confs[i].width == check_timing->xres &&
478 hdmi_confs[i].height == check_timing->yres &&
479 hdmi_confs[i].vrefresh == check_timing->refresh &&
480 hdmi_confs[i].interlace ==
481 ((check_timing->vmode & FB_VMODE_INTERLACED) ?
482 true : false))
483 return 0;
484
485 return -EINVAL;
486}
487
488static int hdmi_display_power_on(void *ctx, int mode)
489{
490 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
491
492 switch (mode) {
493 case DRM_MODE_DPMS_ON:
494 DRM_DEBUG_KMS("hdmi [on]\n");
495 break;
496 case DRM_MODE_DPMS_STANDBY:
497 break;
498 case DRM_MODE_DPMS_SUSPEND:
499 break;
500 case DRM_MODE_DPMS_OFF:
501 DRM_DEBUG_KMS("hdmi [off]\n");
502 break;
503 default:
504 break;
505 }
506
507 return 0;
508}
509
510static struct exynos_hdmi_display_ops display_ops = {
511 .is_connected = hdmi_is_connected,
512 .get_edid = hdmi_get_edid,
513 .check_timing = hdmi_check_timing,
514 .power_on = hdmi_display_power_on,
515};
516
517static void hdmi_conf_reset(struct hdmi_context *hdata)
518{
519 /* disable hpd handle for drm */
520 hdata->hpd_handle = false;
521
522 /* resetting HDMI core */
523 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT);
524 mdelay(10);
525 hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT);
526 mdelay(10);
527
528 /* enable hpd handle for drm */
529 hdata->hpd_handle = true;
530}
531
532static void hdmi_conf_init(struct hdmi_context *hdata)
533{
534 /* disable hpd handle for drm */
535 hdata->hpd_handle = false;
536
537 /* enable HPD interrupts */
538 hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
539 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
540 mdelay(10);
541 hdmi_reg_writemask(hdata, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL |
542 HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
543
544 /* choose HDMI mode */
545 hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
546 HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
547 /* disable bluescreen */
548 hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
549 /* choose bluescreen (fecal) color */
550 hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_0, 0x12);
551 hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_1, 0x34);
552 hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_2, 0x56);
553 /* enable AVI packet every vsync, fixes purple line problem */
554 hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02);
555 /* force RGB, look to CEA-861-D, table 7 for more detail */
556 hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(0), 0 << 5);
557 hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
558
559 hdmi_reg_writeb(hdata, HDMI_SPD_CON, 0x02);
560 hdmi_reg_writeb(hdata, HDMI_AUI_CON, 0x02);
561 hdmi_reg_writeb(hdata, HDMI_ACR_CON, 0x04);
562
563 /* enable hpd handle for drm */
564 hdata->hpd_handle = true;
565}
566
567static void hdmi_timing_apply(struct hdmi_context *hdata,
568 const struct hdmi_preset_conf *conf)
569{
570 const struct hdmi_core_regs *core = &conf->core;
571 const struct hdmi_tg_regs *tg = &conf->tg;
572 int tries;
573
574 /* setting core registers */
575 hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]);
576 hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]);
577 hdmi_reg_writeb(hdata, HDMI_V_BLANK_0, core->v_blank[0]);
578 hdmi_reg_writeb(hdata, HDMI_V_BLANK_1, core->v_blank[1]);
579 hdmi_reg_writeb(hdata, HDMI_V_BLANK_2, core->v_blank[2]);
580 hdmi_reg_writeb(hdata, HDMI_H_V_LINE_0, core->h_v_line[0]);
581 hdmi_reg_writeb(hdata, HDMI_H_V_LINE_1, core->h_v_line[1]);
582 hdmi_reg_writeb(hdata, HDMI_H_V_LINE_2, core->h_v_line[2]);
583 hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]);
584 hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]);
585 hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_0, core->v_blank_f[0]);
586 hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_1, core->v_blank_f[1]);
587 hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_2, core->v_blank_f[2]);
588 hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]);
589 hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]);
590 hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]);
591 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]);
592 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]);
593 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]);
594 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]);
595 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]);
596 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]);
597 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]);
598 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]);
599 hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]);
600 /* Timing generator registers */
601 hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l);
602 hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h);
603 hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st_l);
604 hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st_h);
605 hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz_l);
606 hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz_h);
607 hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz_l);
608 hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz_h);
609 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync_l);
610 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync_h);
611 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2_l);
612 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2_h);
613 hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st_l);
614 hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st_h);
615 hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz_l);
616 hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz_h);
617 hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg_l);
618 hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h);
619 hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l);
620 hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h);
621 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l);
622 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h);
623 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l);
624 hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h);
625 hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l);
626 hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h);
627 hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l);
628 hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h);
629
630 /* waiting for HDMIPHY's PLL to get to steady state */
631 for (tries = 100; tries; --tries) {
632 u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
633 if (val & HDMI_PHY_STATUS_READY)
634 break;
635 mdelay(1);
636 }
637 /* steady state not achieved */
638 if (tries == 0) {
639 DRM_ERROR("hdmiphy's pll could not reach steady state.\n");
640 hdmi_regs_dump(hdata, "timing apply");
641 }
642
643 clk_disable(hdata->res.sclk_hdmi);
644 clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy);
645 clk_enable(hdata->res.sclk_hdmi);
646
647 /* enable HDMI and timing generator */
648 hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN);
649 if (core->int_pro_mode[0])
650 hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN |
651 HDMI_FIELD_EN);
652 else
653 hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
654}
655
656static void hdmiphy_conf_reset(struct hdmi_context *hdata)
657{
658 u8 buffer[2];
659
660 clk_disable(hdata->res.sclk_hdmi);
661 clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel);
662 clk_enable(hdata->res.sclk_hdmi);
663
664 /* operation mode */
665 buffer[0] = 0x1f;
666 buffer[1] = 0x00;
667
668 if (hdata->hdmiphy_port)
669 i2c_master_send(hdata->hdmiphy_port, buffer, 2);
670
671 /* reset hdmiphy */
672 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
673 mdelay(10);
674 hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
675 mdelay(10);
676}
677
678static void hdmiphy_conf_apply(struct hdmi_context *hdata)
679{
680 u8 buffer[32];
681 u8 operation[2];
682 u8 read_buffer[32] = {0, };
683 int ret;
684 int i;
685
686 if (!hdata->hdmiphy_port) {
687 DRM_ERROR("hdmiphy is not attached\n");
688 return;
689 }
690
691 /* pixel clock */
692 memcpy(buffer, hdmi_confs[hdata->cur_conf].hdmiphy_data, 32);
693 ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
694 if (ret != 32) {
695 DRM_ERROR("failed to configure HDMIPHY via I2C\n");
696 return;
697 }
698
699 mdelay(10);
700
701 /* operation mode */
702 operation[0] = 0x1f;
703 operation[1] = 0x80;
704
705 ret = i2c_master_send(hdata->hdmiphy_port, operation, 2);
706 if (ret != 2) {
707 DRM_ERROR("failed to enable hdmiphy\n");
708 return;
709 }
710
711 ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32);
712 if (ret < 0) {
713 DRM_ERROR("failed to read hdmiphy config\n");
714 return;
715 }
716
717 for (i = 0; i < ret; i++)
718 DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - "
719 "recv [0x%02x]\n", i, buffer[i], read_buffer[i]);
720}
721
722static void hdmi_conf_apply(struct hdmi_context *hdata)
723{
724 const struct hdmi_preset_conf *conf =
725 hdmi_confs[hdata->cur_conf].conf;
726
727 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
728
729 hdmiphy_conf_reset(hdata);
730 hdmiphy_conf_apply(hdata);
731
732 hdmi_conf_reset(hdata);
733 hdmi_conf_init(hdata);
734
735 /* setting core registers */
736 hdmi_timing_apply(hdata, conf);
737
738 hdmi_regs_dump(hdata, "start");
739}
740
741static void hdmi_mode_set(void *ctx, void *mode)
742{
743 struct hdmi_context *hdata = (struct hdmi_context *)ctx;
744 int conf_idx;
745
746 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
747
748 conf_idx = hdmi_conf_index(mode);
749 if (conf_idx >= 0 && conf_idx < ARRAY_SIZE(hdmi_confs))
750 hdata->cur_conf = conf_idx;
751 else
752 DRM_DEBUG_KMS("not supported mode\n");
753}
754
755static void hdmi_commit(void *ctx)
756{
757 struct hdmi_context *hdata = (struct hdmi_context *)ctx;
758
759 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
760
761 hdmi_conf_apply(hdata);
762
763 hdata->enabled = true;
764}
765
766static void hdmi_disable(void *ctx)
767{
768 struct hdmi_context *hdata = (struct hdmi_context *)ctx;
769
770 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
771
772 if (hdata->enabled) {
773 hdmiphy_conf_reset(hdata);
774 hdmi_conf_reset(hdata);
775 }
776}
777
778static struct exynos_hdmi_manager_ops manager_ops = {
779 .mode_set = hdmi_mode_set,
780 .commit = hdmi_commit,
781 .disable = hdmi_disable,
782};
783
784/*
785 * Handle hotplug events outside the interrupt handler proper.
786 */
787static void hdmi_hotplug_func(struct work_struct *work)
788{
789 struct hdmi_context *hdata =
790 container_of(work, struct hdmi_context, hotplug_work);
791 struct exynos_drm_hdmi_context *ctx =
792 (struct exynos_drm_hdmi_context *)hdata->parent_ctx;
793
794 drm_helper_hpd_irq_event(ctx->drm_dev);
795}
796
797static irqreturn_t hdmi_irq_handler(int irq, void *arg)
798{
799 struct exynos_drm_hdmi_context *ctx = arg;
800 struct hdmi_context *hdata = (struct hdmi_context *)ctx->ctx;
801 u32 intc_flag;
802
803 intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG);
804 /* clearing flags for HPD plug/unplug */
805 if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) {
806 DRM_DEBUG_KMS("unplugged, handling:%d\n", hdata->hpd_handle);
807 hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
808 HDMI_INTC_FLAG_HPD_UNPLUG);
809 }
810 if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) {
811 DRM_DEBUG_KMS("plugged, handling:%d\n", hdata->hpd_handle);
812 hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
813 HDMI_INTC_FLAG_HPD_PLUG);
814 }
815
816 if (ctx->drm_dev && hdata->hpd_handle)
817 queue_work(hdata->wq, &hdata->hotplug_work);
818
819 return IRQ_HANDLED;
820}
821
822static int __devinit hdmi_resources_init(struct hdmi_context *hdata)
823{
824 struct device *dev = hdata->dev;
825 struct hdmi_resources *res = &hdata->res;
826 static char *supply[] = {
827 "hdmi-en",
828 "vdd",
829 "vdd_osc",
830 "vdd_pll",
831 };
832 int i, ret;
833
834 DRM_DEBUG_KMS("HDMI resource init\n");
835
836 memset(res, 0, sizeof *res);
837
838 /* get clocks, power */
839 res->hdmi = clk_get(dev, "hdmi");
840 if (IS_ERR_OR_NULL(res->hdmi)) {
841 DRM_ERROR("failed to get clock 'hdmi'\n");
842 goto fail;
843 }
844 res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
845 if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
846 DRM_ERROR("failed to get clock 'sclk_hdmi'\n");
847 goto fail;
848 }
849 res->sclk_pixel = clk_get(dev, "sclk_pixel");
850 if (IS_ERR_OR_NULL(res->sclk_pixel)) {
851 DRM_ERROR("failed to get clock 'sclk_pixel'\n");
852 goto fail;
853 }
854 res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy");
855 if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) {
856 DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n");
857 goto fail;
858 }
859 res->hdmiphy = clk_get(dev, "hdmiphy");
860 if (IS_ERR_OR_NULL(res->hdmiphy)) {
861 DRM_ERROR("failed to get clock 'hdmiphy'\n");
862 goto fail;
863 }
864
865 clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
866
867 res->regul_bulk = kzalloc(ARRAY_SIZE(supply) *
868 sizeof res->regul_bulk[0], GFP_KERNEL);
869 if (!res->regul_bulk) {
870 DRM_ERROR("failed to get memory for regulators\n");
871 goto fail;
872 }
873 for (i = 0; i < ARRAY_SIZE(supply); ++i) {
874 res->regul_bulk[i].supply = supply[i];
875 res->regul_bulk[i].consumer = NULL;
876 }
877 ret = regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk);
878 if (ret) {
879 DRM_ERROR("failed to get regulators\n");
880 goto fail;
881 }
882 res->regul_count = ARRAY_SIZE(supply);
883
884 return 0;
885fail:
886 DRM_ERROR("HDMI resource init - failed\n");
887 return -ENODEV;
888}
889
890static int hdmi_resources_cleanup(struct hdmi_context *hdata)
891{
892 struct hdmi_resources *res = &hdata->res;
893
894 regulator_bulk_free(res->regul_count, res->regul_bulk);
895 /* kfree is NULL-safe */
896 kfree(res->regul_bulk);
897 if (!IS_ERR_OR_NULL(res->hdmiphy))
898 clk_put(res->hdmiphy);
899 if (!IS_ERR_OR_NULL(res->sclk_hdmiphy))
900 clk_put(res->sclk_hdmiphy);
901 if (!IS_ERR_OR_NULL(res->sclk_pixel))
902 clk_put(res->sclk_pixel);
903 if (!IS_ERR_OR_NULL(res->sclk_hdmi))
904 clk_put(res->sclk_hdmi);
905 if (!IS_ERR_OR_NULL(res->hdmi))
906 clk_put(res->hdmi);
907 memset(res, 0, sizeof *res);
908
909 return 0;
910}
911
912static void hdmi_resource_poweron(struct hdmi_context *hdata)
913{
914 struct hdmi_resources *res = &hdata->res;
915
916 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
917
918 /* turn HDMI power on */
919 regulator_bulk_enable(res->regul_count, res->regul_bulk);
920 /* power-on hdmi physical interface */
921 clk_enable(res->hdmiphy);
922 /* turn clocks on */
923 clk_enable(res->hdmi);
924 clk_enable(res->sclk_hdmi);
925
926 hdmiphy_conf_reset(hdata);
927 hdmi_conf_reset(hdata);
928 hdmi_conf_init(hdata);
929
930}
931
932static void hdmi_resource_poweroff(struct hdmi_context *hdata)
933{
934 struct hdmi_resources *res = &hdata->res;
935
936 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
937
938 /* turn clocks off */
939 clk_disable(res->sclk_hdmi);
940 clk_disable(res->hdmi);
941 /* power-off hdmiphy */
942 clk_disable(res->hdmiphy);
943 /* turn HDMI power off */
944 regulator_bulk_disable(res->regul_count, res->regul_bulk);
945}
946
947static int hdmi_runtime_suspend(struct device *dev)
948{
949 struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
950
951 DRM_DEBUG_KMS("%s\n", __func__);
952
953 hdmi_resource_poweroff((struct hdmi_context *)ctx->ctx);
954
955 return 0;
956}
957
958static int hdmi_runtime_resume(struct device *dev)
959{
960 struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
961
962 DRM_DEBUG_KMS("%s\n", __func__);
963
964 hdmi_resource_poweron((struct hdmi_context *)ctx->ctx);
965
966 return 0;
967}
968
969static const struct dev_pm_ops hdmi_pm_ops = {
970 .runtime_suspend = hdmi_runtime_suspend,
971 .runtime_resume = hdmi_runtime_resume,
972};
973
974static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy;
975
976void hdmi_attach_ddc_client(struct i2c_client *ddc)
977{
978 if (ddc)
979 hdmi_ddc = ddc;
980}
981EXPORT_SYMBOL(hdmi_attach_ddc_client);
982
983void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy)
984{
985 if (hdmiphy)
986 hdmi_hdmiphy = hdmiphy;
987}
988EXPORT_SYMBOL(hdmi_attach_hdmiphy_client);
989
990static int __devinit hdmi_probe(struct platform_device *pdev)
991{
992 struct device *dev = &pdev->dev;
993 struct exynos_drm_hdmi_context *drm_hdmi_ctx;
994 struct hdmi_context *hdata;
995 struct exynos_drm_hdmi_pdata *pdata;
996 struct resource *res;
997 int ret;
998
999 DRM_DEBUG_KMS("[%d]\n", __LINE__);
1000
1001 pdata = pdev->dev.platform_data;
1002 if (!pdata) {
1003 DRM_ERROR("no platform data specified\n");
1004 return -EINVAL;
1005 }
1006
1007 drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL);
1008 if (!drm_hdmi_ctx) {
1009 DRM_ERROR("failed to allocate common hdmi context.\n");
1010 return -ENOMEM;
1011 }
1012
1013 hdata = kzalloc(sizeof(struct hdmi_context), GFP_KERNEL);
1014 if (!hdata) {
1015 DRM_ERROR("out of memory\n");
1016 kfree(drm_hdmi_ctx);
1017 return -ENOMEM;
1018 }
1019
1020 drm_hdmi_ctx->ctx = (void *)hdata;
1021 hdata->parent_ctx = (void *)drm_hdmi_ctx;
1022
1023 platform_set_drvdata(pdev, drm_hdmi_ctx);
1024
1025 hdata->default_win = pdata->default_win;
1026 hdata->default_timing = &pdata->timing;
1027 hdata->default_bpp = pdata->bpp;
1028 hdata->dev = dev;
1029
1030 ret = hdmi_resources_init(hdata);
1031 if (ret) {
1032 ret = -EINVAL;
1033 goto err_data;
1034 }
1035
1036 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1037 if (!res) {
1038 DRM_ERROR("failed to find registers\n");
1039 ret = -ENOENT;
1040 goto err_resource;
1041 }
1042
1043 hdata->regs_res = request_mem_region(res->start, resource_size(res),
1044 dev_name(dev));
1045 if (!hdata->regs_res) {
1046 DRM_ERROR("failed to claim register region\n");
1047 ret = -ENOENT;
1048 goto err_resource;
1049 }
1050
1051 hdata->regs = ioremap(res->start, resource_size(res));
1052 if (!hdata->regs) {
1053 DRM_ERROR("failed to map registers\n");
1054 ret = -ENXIO;
1055 goto err_req_region;
1056 }
1057
1058 /* DDC i2c driver */
1059 if (i2c_add_driver(&ddc_driver)) {
1060 DRM_ERROR("failed to register ddc i2c driver\n");
1061 ret = -ENOENT;
1062 goto err_iomap;
1063 }
1064
1065 hdata->ddc_port = hdmi_ddc;
1066
1067 /* hdmiphy i2c driver */
1068 if (i2c_add_driver(&hdmiphy_driver)) {
1069 DRM_ERROR("failed to register hdmiphy i2c driver\n");
1070 ret = -ENOENT;
1071 goto err_ddc;
1072 }
1073
1074 hdata->hdmiphy_port = hdmi_hdmiphy;
1075
1076 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
1077 if (res == NULL) {
1078 DRM_ERROR("get interrupt resource failed.\n");
1079 ret = -ENXIO;
1080 goto err_hdmiphy;
1081 }
1082
1083 /* create workqueue and hotplug work */
1084 hdata->wq = alloc_workqueue("exynos-drm-hdmi",
1085 WQ_UNBOUND | WQ_NON_REENTRANT, 1);
1086 if (hdata->wq == NULL) {
1087 DRM_ERROR("Failed to create workqueue.\n");
1088 ret = -ENOMEM;
1089 goto err_hdmiphy;
1090 }
1091 INIT_WORK(&hdata->hotplug_work, hdmi_hotplug_func);
1092
1093 /* register hpd interrupt */
1094 ret = request_irq(res->start, hdmi_irq_handler, 0, "drm_hdmi",
1095 drm_hdmi_ctx);
1096 if (ret) {
1097 DRM_ERROR("request interrupt failed.\n");
1098 goto err_workqueue;
1099 }
1100 hdata->irq = res->start;
1101
1102 /* register specific callbacks to common hdmi. */
1103 exynos_drm_display_ops_register(&display_ops);
1104 exynos_drm_manager_ops_register(&manager_ops);
1105
1106 hdmi_resource_poweron(hdata);
1107
1108 return 0;
1109
1110err_workqueue:
1111 destroy_workqueue(hdata->wq);
1112err_hdmiphy:
1113 i2c_del_driver(&hdmiphy_driver);
1114err_ddc:
1115 i2c_del_driver(&ddc_driver);
1116err_iomap:
1117 iounmap(hdata->regs);
1118err_req_region:
1119 release_resource(hdata->regs_res);
1120 kfree(hdata->regs_res);
1121err_resource:
1122 hdmi_resources_cleanup(hdata);
1123err_data:
1124 kfree(hdata);
1125 kfree(drm_hdmi_ctx);
1126 return ret;
1127}
1128
1129static int __devexit hdmi_remove(struct platform_device *pdev)
1130{
1131 struct exynos_drm_hdmi_context *ctx = platform_get_drvdata(pdev);
1132 struct hdmi_context *hdata = (struct hdmi_context *)ctx->ctx;
1133
1134 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
1135
1136 hdmi_resource_poweroff(hdata);
1137
1138 disable_irq(hdata->irq);
1139 free_irq(hdata->irq, hdata);
1140
1141 cancel_work_sync(&hdata->hotplug_work);
1142 destroy_workqueue(hdata->wq);
1143
1144 hdmi_resources_cleanup(hdata);
1145
1146 iounmap(hdata->regs);
1147
1148 release_resource(hdata->regs_res);
1149 kfree(hdata->regs_res);
1150
1151 /* hdmiphy i2c driver */
1152 i2c_del_driver(&hdmiphy_driver);
1153 /* DDC i2c driver */
1154 i2c_del_driver(&ddc_driver);
1155
1156 kfree(hdata);
1157
1158 return 0;
1159}
1160
1161struct platform_driver hdmi_driver = {
1162 .probe = hdmi_probe,
1163 .remove = __devexit_p(hdmi_remove),
1164 .driver = {
1165 .name = "exynos4-hdmi",
1166 .owner = THIS_MODULE,
1167 .pm = &hdmi_pm_ops,
1168 },
1169};
1170EXPORT_SYMBOL(hdmi_driver);
1171
1172MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>");
1173MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
1174MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
1175MODULE_DESCRIPTION("Samsung DRM HDMI core Driver");
1176MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h
new file mode 100644
index 000000000000..31d6cf84c1aa
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.h
@@ -0,0 +1,87 @@
1/*
2 *
3 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
4 * Authors:
5 * Inki Dae <inki.dae@samsung.com>
6 * Seung-Woo Kim <sw0312.kim@samsung.com>
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the next
16 * paragraph) shall be included in all copies or substantial portions of the
17 * Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 * OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28#ifndef _EXYNOS_HDMI_H_
29#define _EXYNOS_HDMI_H_
30
31struct hdmi_conf {
32 int width;
33 int height;
34 int vrefresh;
35 bool interlace;
36 const u8 *hdmiphy_data;
37 const struct hdmi_preset_conf *conf;
38};
39
40struct hdmi_resources {
41 struct clk *hdmi;
42 struct clk *sclk_hdmi;
43 struct clk *sclk_pixel;
44 struct clk *sclk_hdmiphy;
45 struct clk *hdmiphy;
46 struct regulator_bulk_data *regul_bulk;
47 int regul_count;
48};
49
50struct hdmi_context {
51 struct device *dev;
52 struct drm_device *drm_dev;
53 struct fb_videomode *default_timing;
54 unsigned int default_win;
55 unsigned int default_bpp;
56 bool hpd_handle;
57 bool enabled;
58
59 struct resource *regs_res;
60 /** base address of HDMI registers */
61 void __iomem *regs;
62 /** HDMI hotplug interrupt */
63 unsigned int irq;
64 /** workqueue for delayed work */
65 struct workqueue_struct *wq;
66 /** hotplug handling work */
67 struct work_struct hotplug_work;
68
69 struct i2c_client *ddc_port;
70 struct i2c_client *hdmiphy_port;
71
72 /** current hdmiphy conf index */
73 int cur_conf;
74 /** other resources */
75 struct hdmi_resources res;
76
77 void *parent_ctx;
78};
79
80
81void hdmi_attach_ddc_client(struct i2c_client *ddc);
82void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy);
83
84extern struct i2c_driver hdmiphy_driver;
85extern struct i2c_driver ddc_driver;
86
87#endif
diff --git a/drivers/gpu/drm/exynos/exynos_hdmiphy.c b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
new file mode 100644
index 000000000000..9fe2995ab9f9
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
@@ -0,0 +1,58 @@
1/*
2 * Copyright (C) 2011 Samsung Electronics Co.Ltd
3 * Authors:
4 * Seung-Woo Kim <sw0312.kim@samsung.com>
5 * Inki Dae <inki.dae@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/i2c.h>
18#include <linux/module.h>
19
20#include "exynos_drm_drv.h"
21#include "exynos_hdmi.h"
22
23
24static int hdmiphy_probe(struct i2c_client *client,
25 const struct i2c_device_id *id)
26{
27 hdmi_attach_hdmiphy_client(client);
28
29 dev_info(&client->adapter->dev, "attached s5p_hdmiphy "
30 "into i2c adapter successfully\n");
31
32 return 0;
33}
34
35static int hdmiphy_remove(struct i2c_client *client)
36{
37 dev_info(&client->adapter->dev, "detached s5p_hdmiphy "
38 "from i2c adapter successfully\n");
39
40 return 0;
41}
42
43static const struct i2c_device_id hdmiphy_id[] = {
44 { "s5p_hdmiphy", 0 },
45 { },
46};
47
48struct i2c_driver hdmiphy_driver = {
49 .driver = {
50 .name = "s5p-hdmiphy",
51 .owner = THIS_MODULE,
52 },
53 .id_table = hdmiphy_id,
54 .probe = hdmiphy_probe,
55 .remove = __devexit_p(hdmiphy_remove),
56 .command = NULL,
57};
58EXPORT_SYMBOL(hdmiphy_driver);
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
new file mode 100644
index 000000000000..ac24cff39775
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -0,0 +1,1070 @@
1/*
2 * Copyright (C) 2011 Samsung Electronics Co.Ltd
3 * Authors:
4 * Seung-Woo Kim <sw0312.kim@samsung.com>
5 * Inki Dae <inki.dae@samsung.com>
6 * Joonyoung Shim <jy0922.shim@samsung.com>
7 *
8 * Based on drivers/media/video/s5p-tv/mixer_reg.c
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version.
14 *
15 */
16
17#include "drmP.h"
18
19#include "regs-mixer.h"
20#include "regs-vp.h"
21
22#include <linux/kernel.h>
23#include <linux/spinlock.h>
24#include <linux/wait.h>
25#include <linux/i2c.h>
26#include <linux/module.h>
27#include <linux/platform_device.h>
28#include <linux/interrupt.h>
29#include <linux/irq.h>
30#include <linux/delay.h>
31#include <linux/pm_runtime.h>
32#include <linux/clk.h>
33#include <linux/regulator/consumer.h>
34
35#include <drm/exynos_drm.h>
36
37#include "exynos_drm_drv.h"
38#include "exynos_drm_hdmi.h"
39#include "exynos_hdmi.h"
40#include "exynos_mixer.h"
41
42#define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev))
43
44static const u8 filter_y_horiz_tap8[] = {
45 0, -1, -1, -1, -1, -1, -1, -1,
46 -1, -1, -1, -1, -1, 0, 0, 0,
47 0, 2, 4, 5, 6, 6, 6, 6,
48 6, 5, 5, 4, 3, 2, 1, 1,
49 0, -6, -12, -16, -18, -20, -21, -20,
50 -20, -18, -16, -13, -10, -8, -5, -2,
51 127, 126, 125, 121, 114, 107, 99, 89,
52 79, 68, 57, 46, 35, 25, 16, 8,
53};
54
55static const u8 filter_y_vert_tap4[] = {
56 0, -3, -6, -8, -8, -8, -8, -7,
57 -6, -5, -4, -3, -2, -1, -1, 0,
58 127, 126, 124, 118, 111, 102, 92, 81,
59 70, 59, 48, 37, 27, 19, 11, 5,
60 0, 5, 11, 19, 27, 37, 48, 59,
61 70, 81, 92, 102, 111, 118, 124, 126,
62 0, 0, -1, -1, -2, -3, -4, -5,
63 -6, -7, -8, -8, -8, -8, -6, -3,
64};
65
66static const u8 filter_cr_horiz_tap4[] = {
67 0, -3, -6, -8, -8, -8, -8, -7,
68 -6, -5, -4, -3, -2, -1, -1, 0,
69 127, 126, 124, 118, 111, 102, 92, 81,
70 70, 59, 48, 37, 27, 19, 11, 5,
71};
72
73static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
74{
75 return readl(res->vp_regs + reg_id);
76}
77
78static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id,
79 u32 val)
80{
81 writel(val, res->vp_regs + reg_id);
82}
83
84static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id,
85 u32 val, u32 mask)
86{
87 u32 old = vp_reg_read(res, reg_id);
88
89 val = (val & mask) | (old & ~mask);
90 writel(val, res->vp_regs + reg_id);
91}
92
93static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id)
94{
95 return readl(res->mixer_regs + reg_id);
96}
97
98static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id,
99 u32 val)
100{
101 writel(val, res->mixer_regs + reg_id);
102}
103
104static inline void mixer_reg_writemask(struct mixer_resources *res,
105 u32 reg_id, u32 val, u32 mask)
106{
107 u32 old = mixer_reg_read(res, reg_id);
108
109 val = (val & mask) | (old & ~mask);
110 writel(val, res->mixer_regs + reg_id);
111}
112
113static void mixer_regs_dump(struct mixer_context *ctx)
114{
115#define DUMPREG(reg_id) \
116do { \
117 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
118 (u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \
119} while (0)
120
121 DUMPREG(MXR_STATUS);
122 DUMPREG(MXR_CFG);
123 DUMPREG(MXR_INT_EN);
124 DUMPREG(MXR_INT_STATUS);
125
126 DUMPREG(MXR_LAYER_CFG);
127 DUMPREG(MXR_VIDEO_CFG);
128
129 DUMPREG(MXR_GRAPHIC0_CFG);
130 DUMPREG(MXR_GRAPHIC0_BASE);
131 DUMPREG(MXR_GRAPHIC0_SPAN);
132 DUMPREG(MXR_GRAPHIC0_WH);
133 DUMPREG(MXR_GRAPHIC0_SXY);
134 DUMPREG(MXR_GRAPHIC0_DXY);
135
136 DUMPREG(MXR_GRAPHIC1_CFG);
137 DUMPREG(MXR_GRAPHIC1_BASE);
138 DUMPREG(MXR_GRAPHIC1_SPAN);
139 DUMPREG(MXR_GRAPHIC1_WH);
140 DUMPREG(MXR_GRAPHIC1_SXY);
141 DUMPREG(MXR_GRAPHIC1_DXY);
142#undef DUMPREG
143}
144
145static void vp_regs_dump(struct mixer_context *ctx)
146{
147#define DUMPREG(reg_id) \
148do { \
149 DRM_DEBUG_KMS(#reg_id " = %08x\n", \
150 (u32) readl(ctx->mixer_res.vp_regs + reg_id)); \
151} while (0)
152
153 DUMPREG(VP_ENABLE);
154 DUMPREG(VP_SRESET);
155 DUMPREG(VP_SHADOW_UPDATE);
156 DUMPREG(VP_FIELD_ID);
157 DUMPREG(VP_MODE);
158 DUMPREG(VP_IMG_SIZE_Y);
159 DUMPREG(VP_IMG_SIZE_C);
160 DUMPREG(VP_PER_RATE_CTRL);
161 DUMPREG(VP_TOP_Y_PTR);
162 DUMPREG(VP_BOT_Y_PTR);
163 DUMPREG(VP_TOP_C_PTR);
164 DUMPREG(VP_BOT_C_PTR);
165 DUMPREG(VP_ENDIAN_MODE);
166 DUMPREG(VP_SRC_H_POSITION);
167 DUMPREG(VP_SRC_V_POSITION);
168 DUMPREG(VP_SRC_WIDTH);
169 DUMPREG(VP_SRC_HEIGHT);
170 DUMPREG(VP_DST_H_POSITION);
171 DUMPREG(VP_DST_V_POSITION);
172 DUMPREG(VP_DST_WIDTH);
173 DUMPREG(VP_DST_HEIGHT);
174 DUMPREG(VP_H_RATIO);
175 DUMPREG(VP_V_RATIO);
176
177#undef DUMPREG
178}
179
180static inline void vp_filter_set(struct mixer_resources *res,
181 int reg_id, const u8 *data, unsigned int size)
182{
183 /* assure 4-byte align */
184 BUG_ON(size & 3);
185 for (; size; size -= 4, reg_id += 4, data += 4) {
186 u32 val = (data[0] << 24) | (data[1] << 16) |
187 (data[2] << 8) | data[3];
188 vp_reg_write(res, reg_id, val);
189 }
190}
191
192static void vp_default_filter(struct mixer_resources *res)
193{
194 vp_filter_set(res, VP_POLY8_Y0_LL,
195 filter_y_horiz_tap8, sizeof filter_y_horiz_tap8);
196 vp_filter_set(res, VP_POLY4_Y0_LL,
197 filter_y_vert_tap4, sizeof filter_y_vert_tap4);
198 vp_filter_set(res, VP_POLY4_C0_LL,
199 filter_cr_horiz_tap4, sizeof filter_cr_horiz_tap4);
200}
201
202static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
203{
204 struct mixer_resources *res = &ctx->mixer_res;
205
206 /* block update on vsync */
207 mixer_reg_writemask(res, MXR_STATUS, enable ?
208 MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
209
210 vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
211 VP_SHADOW_UPDATE_ENABLE : 0);
212}
213
214static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
215{
216 struct mixer_resources *res = &ctx->mixer_res;
217 u32 val;
218
219 /* choosing between interlace and progressive mode */
220 val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
221 MXR_CFG_SCAN_PROGRASSIVE);
222
223 /* choosing between porper HD and SD mode */
224 if (height == 480)
225 val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
226 else if (height == 576)
227 val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
228 else if (height == 720)
229 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
230 else if (height == 1080)
231 val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
232 else
233 val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
234
235 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
236}
237
238static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
239{
240 struct mixer_resources *res = &ctx->mixer_res;
241 u32 val;
242
243 if (height == 480) {
244 val = MXR_CFG_RGB601_0_255;
245 } else if (height == 576) {
246 val = MXR_CFG_RGB601_0_255;
247 } else if (height == 720) {
248 val = MXR_CFG_RGB709_16_235;
249 mixer_reg_write(res, MXR_CM_COEFF_Y,
250 (1 << 30) | (94 << 20) | (314 << 10) |
251 (32 << 0));
252 mixer_reg_write(res, MXR_CM_COEFF_CB,
253 (972 << 20) | (851 << 10) | (225 << 0));
254 mixer_reg_write(res, MXR_CM_COEFF_CR,
255 (225 << 20) | (820 << 10) | (1004 << 0));
256 } else if (height == 1080) {
257 val = MXR_CFG_RGB709_16_235;
258 mixer_reg_write(res, MXR_CM_COEFF_Y,
259 (1 << 30) | (94 << 20) | (314 << 10) |
260 (32 << 0));
261 mixer_reg_write(res, MXR_CM_COEFF_CB,
262 (972 << 20) | (851 << 10) | (225 << 0));
263 mixer_reg_write(res, MXR_CM_COEFF_CR,
264 (225 << 20) | (820 << 10) | (1004 << 0));
265 } else {
266 val = MXR_CFG_RGB709_16_235;
267 mixer_reg_write(res, MXR_CM_COEFF_Y,
268 (1 << 30) | (94 << 20) | (314 << 10) |
269 (32 << 0));
270 mixer_reg_write(res, MXR_CM_COEFF_CB,
271 (972 << 20) | (851 << 10) | (225 << 0));
272 mixer_reg_write(res, MXR_CM_COEFF_CR,
273 (225 << 20) | (820 << 10) | (1004 << 0));
274 }
275
276 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
277}
278
279static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable)
280{
281 struct mixer_resources *res = &ctx->mixer_res;
282 u32 val = enable ? ~0 : 0;
283
284 switch (win) {
285 case 0:
286 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
287 break;
288 case 1:
289 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
290 break;
291 case 2:
292 vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
293 mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_VP_ENABLE);
294 break;
295 }
296}
297
298static void mixer_run(struct mixer_context *ctx)
299{
300 struct mixer_resources *res = &ctx->mixer_res;
301
302 mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
303
304 mixer_regs_dump(ctx);
305}
306
307static void vp_video_buffer(struct mixer_context *ctx, int win)
308{
309 struct mixer_resources *res = &ctx->mixer_res;
310 unsigned long flags;
311 struct hdmi_win_data *win_data;
312 unsigned int full_width, full_height, width, height;
313 unsigned int x_ratio, y_ratio;
314 unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
315 unsigned int mode_width, mode_height;
316 unsigned int buf_num;
317 dma_addr_t luma_addr[2], chroma_addr[2];
318 bool tiled_mode = false;
319 bool crcb_mode = false;
320 u32 val;
321
322 win_data = &ctx->win_data[win];
323
324 switch (win_data->pixel_format) {
325 case DRM_FORMAT_NV12MT:
326 tiled_mode = true;
327 case DRM_FORMAT_NV12M:
328 crcb_mode = false;
329 buf_num = 2;
330 break;
331 /* TODO: single buffer format NV12, NV21 */
332 default:
333 /* ignore pixel format at disable time */
334 if (!win_data->dma_addr)
335 break;
336
337 DRM_ERROR("pixel format for vp is wrong [%d].\n",
338 win_data->pixel_format);
339 return;
340 }
341
342 full_width = win_data->fb_width;
343 full_height = win_data->fb_height;
344 width = win_data->crtc_width;
345 height = win_data->crtc_height;
346 mode_width = win_data->mode_width;
347 mode_height = win_data->mode_height;
348
349 /* scaling feature: (src << 16) / dst */
350 x_ratio = (width << 16) / width;
351 y_ratio = (height << 16) / height;
352
353 src_x_offset = win_data->fb_x;
354 src_y_offset = win_data->fb_y;
355 dst_x_offset = win_data->crtc_x;
356 dst_y_offset = win_data->crtc_y;
357
358 if (buf_num == 2) {
359 luma_addr[0] = win_data->dma_addr;
360 chroma_addr[0] = win_data->chroma_dma_addr;
361 } else {
362 luma_addr[0] = win_data->dma_addr;
363 chroma_addr[0] = win_data->dma_addr
364 + (full_width * full_height);
365 }
366
367 if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) {
368 ctx->interlace = true;
369 if (tiled_mode) {
370 luma_addr[1] = luma_addr[0] + 0x40;
371 chroma_addr[1] = chroma_addr[0] + 0x40;
372 } else {
373 luma_addr[1] = luma_addr[0] + full_width;
374 chroma_addr[1] = chroma_addr[0] + full_width;
375 }
376 } else {
377 ctx->interlace = false;
378 luma_addr[1] = 0;
379 chroma_addr[1] = 0;
380 }
381
382 spin_lock_irqsave(&res->reg_slock, flags);
383 mixer_vsync_set_update(ctx, false);
384
385 /* interlace or progressive scan mode */
386 val = (ctx->interlace ? ~0 : 0);
387 vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
388
389 /* setup format */
390 val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
391 val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
392 vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
393
394 /* setting size of input image */
395 vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(full_width) |
396 VP_IMG_VSIZE(full_height));
397 /* chroma height has to reduced by 2 to avoid chroma distorions */
398 vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(full_width) |
399 VP_IMG_VSIZE(full_height / 2));
400
401 vp_reg_write(res, VP_SRC_WIDTH, width);
402 vp_reg_write(res, VP_SRC_HEIGHT, height);
403 vp_reg_write(res, VP_SRC_H_POSITION,
404 VP_SRC_H_POSITION_VAL(src_x_offset));
405 vp_reg_write(res, VP_SRC_V_POSITION, src_y_offset);
406
407 vp_reg_write(res, VP_DST_WIDTH, width);
408 vp_reg_write(res, VP_DST_H_POSITION, dst_x_offset);
409 if (ctx->interlace) {
410 vp_reg_write(res, VP_DST_HEIGHT, height / 2);
411 vp_reg_write(res, VP_DST_V_POSITION, dst_y_offset / 2);
412 } else {
413 vp_reg_write(res, VP_DST_HEIGHT, height);
414 vp_reg_write(res, VP_DST_V_POSITION, dst_y_offset);
415 }
416
417 vp_reg_write(res, VP_H_RATIO, x_ratio);
418 vp_reg_write(res, VP_V_RATIO, y_ratio);
419
420 vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
421
422 /* set buffer address to vp */
423 vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]);
424 vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]);
425 vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
426 vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
427
428 mixer_cfg_scan(ctx, mode_height);
429 mixer_cfg_rgb_fmt(ctx, mode_height);
430 mixer_cfg_layer(ctx, win, true);
431 mixer_run(ctx);
432
433 mixer_vsync_set_update(ctx, true);
434 spin_unlock_irqrestore(&res->reg_slock, flags);
435
436 vp_regs_dump(ctx);
437}
438
439static void mixer_graph_buffer(struct mixer_context *ctx, int win)
440{
441 struct mixer_resources *res = &ctx->mixer_res;
442 unsigned long flags;
443 struct hdmi_win_data *win_data;
444 unsigned int full_width, width, height;
445 unsigned int x_ratio, y_ratio;
446 unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
447 unsigned int mode_width, mode_height;
448 dma_addr_t dma_addr;
449 unsigned int fmt;
450 u32 val;
451
452 win_data = &ctx->win_data[win];
453
454 #define RGB565 4
455 #define ARGB1555 5
456 #define ARGB4444 6
457 #define ARGB8888 7
458
459 switch (win_data->bpp) {
460 case 16:
461 fmt = ARGB4444;
462 break;
463 case 32:
464 fmt = ARGB8888;
465 break;
466 default:
467 fmt = ARGB8888;
468 }
469
470 dma_addr = win_data->dma_addr;
471 full_width = win_data->fb_width;
472 width = win_data->crtc_width;
473 height = win_data->crtc_height;
474 mode_width = win_data->mode_width;
475 mode_height = win_data->mode_height;
476
477 /* 2x scaling feature */
478 x_ratio = 0;
479 y_ratio = 0;
480
481 src_x_offset = win_data->fb_x;
482 src_y_offset = win_data->fb_y;
483 dst_x_offset = win_data->crtc_x;
484 dst_y_offset = win_data->crtc_y;
485
486 /* converting dma address base and source offset */
487 dma_addr = dma_addr
488 + (src_x_offset * win_data->bpp >> 3)
489 + (src_y_offset * full_width * win_data->bpp >> 3);
490 src_x_offset = 0;
491 src_y_offset = 0;
492
493 if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE)
494 ctx->interlace = true;
495 else
496 ctx->interlace = false;
497
498 spin_lock_irqsave(&res->reg_slock, flags);
499 mixer_vsync_set_update(ctx, false);
500
501 /* setup format */
502 mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
503 MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
504
505 /* setup geometry */
506 mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), full_width);
507
508 val = MXR_GRP_WH_WIDTH(width);
509 val |= MXR_GRP_WH_HEIGHT(height);
510 val |= MXR_GRP_WH_H_SCALE(x_ratio);
511 val |= MXR_GRP_WH_V_SCALE(y_ratio);
512 mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
513
514 /* setup offsets in source image */
515 val = MXR_GRP_SXY_SX(src_x_offset);
516 val |= MXR_GRP_SXY_SY(src_y_offset);
517 mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
518
519 /* setup offsets in display image */
520 val = MXR_GRP_DXY_DX(dst_x_offset);
521 val |= MXR_GRP_DXY_DY(dst_y_offset);
522 mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val);
523
524 /* set buffer address to mixer */
525 mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
526
527 mixer_cfg_scan(ctx, mode_height);
528 mixer_cfg_rgb_fmt(ctx, mode_height);
529 mixer_cfg_layer(ctx, win, true);
530 mixer_run(ctx);
531
532 mixer_vsync_set_update(ctx, true);
533 spin_unlock_irqrestore(&res->reg_slock, flags);
534}
535
536static void vp_win_reset(struct mixer_context *ctx)
537{
538 struct mixer_resources *res = &ctx->mixer_res;
539 int tries = 100;
540
541 vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
542 for (tries = 100; tries; --tries) {
543 /* waiting until VP_SRESET_PROCESSING is 0 */
544 if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
545 break;
546 mdelay(10);
547 }
548 WARN(tries == 0, "failed to reset Video Processor\n");
549}
550
551static int mixer_enable_vblank(void *ctx, int pipe)
552{
553 struct mixer_context *mixer_ctx = ctx;
554 struct mixer_resources *res = &mixer_ctx->mixer_res;
555
556 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
557
558 mixer_ctx->pipe = pipe;
559
560 /* enable vsync interrupt */
561 mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC,
562 MXR_INT_EN_VSYNC);
563
564 return 0;
565}
566
567static void mixer_disable_vblank(void *ctx)
568{
569 struct mixer_context *mixer_ctx = ctx;
570 struct mixer_resources *res = &mixer_ctx->mixer_res;
571
572 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
573
574 /* disable vsync interrupt */
575 mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
576}
577
578static void mixer_win_mode_set(void *ctx,
579 struct exynos_drm_overlay *overlay)
580{
581 struct mixer_context *mixer_ctx = ctx;
582 struct hdmi_win_data *win_data;
583 int win;
584
585 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
586
587 if (!overlay) {
588 DRM_ERROR("overlay is NULL\n");
589 return;
590 }
591
592 DRM_DEBUG_KMS("set [%d]x[%d] at (%d,%d) to [%d]x[%d] at (%d,%d)\n",
593 overlay->fb_width, overlay->fb_height,
594 overlay->fb_x, overlay->fb_y,
595 overlay->crtc_width, overlay->crtc_height,
596 overlay->crtc_x, overlay->crtc_y);
597
598 win = overlay->zpos;
599 if (win == DEFAULT_ZPOS)
600 win = mixer_ctx->default_win;
601
602 if (win < 0 || win > HDMI_OVERLAY_NUMBER) {
603 DRM_ERROR("overlay plane[%d] is wrong\n", win);
604 return;
605 }
606
607 win_data = &mixer_ctx->win_data[win];
608
609 win_data->dma_addr = overlay->dma_addr[0];
610 win_data->vaddr = overlay->vaddr[0];
611 win_data->chroma_dma_addr = overlay->dma_addr[1];
612 win_data->chroma_vaddr = overlay->vaddr[1];
613 win_data->pixel_format = overlay->pixel_format;
614 win_data->bpp = overlay->bpp;
615
616 win_data->crtc_x = overlay->crtc_x;
617 win_data->crtc_y = overlay->crtc_y;
618 win_data->crtc_width = overlay->crtc_width;
619 win_data->crtc_height = overlay->crtc_height;
620
621 win_data->fb_x = overlay->fb_x;
622 win_data->fb_y = overlay->fb_y;
623 win_data->fb_width = overlay->fb_width;
624 win_data->fb_height = overlay->fb_height;
625
626 win_data->mode_width = overlay->mode_width;
627 win_data->mode_height = overlay->mode_height;
628
629 win_data->scan_flags = overlay->scan_flag;
630}
631
632static void mixer_win_commit(void *ctx, int zpos)
633{
634 struct mixer_context *mixer_ctx = ctx;
635 int win = zpos;
636
637 DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);
638
639 if (win == DEFAULT_ZPOS)
640 win = mixer_ctx->default_win;
641
642 if (win < 0 || win > HDMI_OVERLAY_NUMBER) {
643 DRM_ERROR("overlay plane[%d] is wrong\n", win);
644 return;
645 }
646
647 if (win > 1)
648 vp_video_buffer(mixer_ctx, win);
649 else
650 mixer_graph_buffer(mixer_ctx, win);
651}
652
653static void mixer_win_disable(void *ctx, int zpos)
654{
655 struct mixer_context *mixer_ctx = ctx;
656 struct mixer_resources *res = &mixer_ctx->mixer_res;
657 unsigned long flags;
658 int win = zpos;
659
660 DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);
661
662 if (win == DEFAULT_ZPOS)
663 win = mixer_ctx->default_win;
664
665 if (win < 0 || win > HDMI_OVERLAY_NUMBER) {
666 DRM_ERROR("overlay plane[%d] is wrong\n", win);
667 return;
668 }
669
670 spin_lock_irqsave(&res->reg_slock, flags);
671 mixer_vsync_set_update(mixer_ctx, false);
672
673 mixer_cfg_layer(mixer_ctx, win, false);
674
675 mixer_vsync_set_update(mixer_ctx, true);
676 spin_unlock_irqrestore(&res->reg_slock, flags);
677}
678
679static struct exynos_hdmi_overlay_ops overlay_ops = {
680 .enable_vblank = mixer_enable_vblank,
681 .disable_vblank = mixer_disable_vblank,
682 .win_mode_set = mixer_win_mode_set,
683 .win_commit = mixer_win_commit,
684 .win_disable = mixer_win_disable,
685};
686
687/* for pageflip event */
688static void mixer_finish_pageflip(struct drm_device *drm_dev, int crtc)
689{
690 struct exynos_drm_private *dev_priv = drm_dev->dev_private;
691 struct drm_pending_vblank_event *e, *t;
692 struct timeval now;
693 unsigned long flags;
694 bool is_checked = false;
695
696 spin_lock_irqsave(&drm_dev->event_lock, flags);
697
698 list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
699 base.link) {
700 /* if event's pipe isn't same as crtc then ignore it. */
701 if (crtc != e->pipe)
702 continue;
703
704 is_checked = true;
705 do_gettimeofday(&now);
706 e->event.sequence = 0;
707 e->event.tv_sec = now.tv_sec;
708 e->event.tv_usec = now.tv_usec;
709
710 list_move_tail(&e->base.link, &e->base.file_priv->event_list);
711 wake_up_interruptible(&e->base.file_priv->event_wait);
712 }
713
714 if (is_checked)
715 drm_vblank_put(drm_dev, crtc);
716
717 spin_unlock_irqrestore(&drm_dev->event_lock, flags);
718}
719
720static irqreturn_t mixer_irq_handler(int irq, void *arg)
721{
722 struct exynos_drm_hdmi_context *drm_hdmi_ctx = arg;
723 struct mixer_context *ctx =
724 (struct mixer_context *)drm_hdmi_ctx->ctx;
725 struct mixer_resources *res = &ctx->mixer_res;
726 u32 val, val_base;
727
728 spin_lock(&res->reg_slock);
729
730 /* read interrupt status for handling and clearing flags for VSYNC */
731 val = mixer_reg_read(res, MXR_INT_STATUS);
732
733 /* handling VSYNC */
734 if (val & MXR_INT_STATUS_VSYNC) {
735 /* interlace scan need to check shadow register */
736 if (ctx->interlace) {
737 val_base = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
738 if (ctx->win_data[0].dma_addr != val_base)
739 goto out;
740
741 val_base = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
742 if (ctx->win_data[1].dma_addr != val_base)
743 goto out;
744 }
745
746 drm_handle_vblank(drm_hdmi_ctx->drm_dev, ctx->pipe);
747 mixer_finish_pageflip(drm_hdmi_ctx->drm_dev, ctx->pipe);
748 }
749
750out:
751 /* clear interrupts */
752 if (~val & MXR_INT_EN_VSYNC) {
753 /* vsync interrupt use different bit for read and clear */
754 val &= ~MXR_INT_EN_VSYNC;
755 val |= MXR_INT_CLEAR_VSYNC;
756 }
757 mixer_reg_write(res, MXR_INT_STATUS, val);
758
759 spin_unlock(&res->reg_slock);
760
761 return IRQ_HANDLED;
762}
763
764static void mixer_win_reset(struct mixer_context *ctx)
765{
766 struct mixer_resources *res = &ctx->mixer_res;
767 unsigned long flags;
768 u32 val; /* value stored to register */
769
770 spin_lock_irqsave(&res->reg_slock, flags);
771 mixer_vsync_set_update(ctx, false);
772
773 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
774
775 /* set output in RGB888 mode */
776 mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
777
778 /* 16 beat burst in DMA */
779 mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
780 MXR_STATUS_BURST_MASK);
781
782 /* setting default layer priority: layer1 > video > layer0
783 * because typical usage scenario would be
784 * layer0 - framebuffer
785 * video - video overlay
786 * layer1 - OSD
787 */
788 val = MXR_LAYER_CFG_GRP0_VAL(1);
789 val |= MXR_LAYER_CFG_VP_VAL(2);
790 val |= MXR_LAYER_CFG_GRP1_VAL(3);
791 mixer_reg_write(res, MXR_LAYER_CFG, val);
792
793 /* setting background color */
794 mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
795 mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
796 mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
797
798 /* setting graphical layers */
799
800 val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
801 val |= MXR_GRP_CFG_WIN_BLEND_EN;
802 val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
803
804 /* the same configuration for both layers */
805 mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
806
807 val |= MXR_GRP_CFG_BLEND_PRE_MUL;
808 val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
809 mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
810
811 /* configuration of Video Processor Registers */
812 vp_win_reset(ctx);
813 vp_default_filter(res);
814
815 /* disable all layers */
816 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
817 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
818 mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
819
820 mixer_vsync_set_update(ctx, true);
821 spin_unlock_irqrestore(&res->reg_slock, flags);
822}
823
824static void mixer_resource_poweron(struct mixer_context *ctx)
825{
826 struct mixer_resources *res = &ctx->mixer_res;
827
828 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
829
830 clk_enable(res->mixer);
831 clk_enable(res->vp);
832 clk_enable(res->sclk_mixer);
833
834 mixer_win_reset(ctx);
835}
836
837static void mixer_resource_poweroff(struct mixer_context *ctx)
838{
839 struct mixer_resources *res = &ctx->mixer_res;
840
841 DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
842
843 clk_disable(res->mixer);
844 clk_disable(res->vp);
845 clk_disable(res->sclk_mixer);
846}
847
848static int mixer_runtime_resume(struct device *dev)
849{
850 struct exynos_drm_hdmi_context *ctx = get_mixer_context(dev);
851
852 DRM_DEBUG_KMS("resume - start\n");
853
854 mixer_resource_poweron((struct mixer_context *)ctx->ctx);
855
856 return 0;
857}
858
859static int mixer_runtime_suspend(struct device *dev)
860{
861 struct exynos_drm_hdmi_context *ctx = get_mixer_context(dev);
862
863 DRM_DEBUG_KMS("suspend - start\n");
864
865 mixer_resource_poweroff((struct mixer_context *)ctx->ctx);
866
867 return 0;
868}
869
870static const struct dev_pm_ops mixer_pm_ops = {
871 .runtime_suspend = mixer_runtime_suspend,
872 .runtime_resume = mixer_runtime_resume,
873};
874
875static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
876 struct platform_device *pdev)
877{
878 struct mixer_context *mixer_ctx =
879 (struct mixer_context *)ctx->ctx;
880 struct device *dev = &pdev->dev;
881 struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
882 struct resource *res;
883 int ret;
884
885 mixer_res->dev = dev;
886 spin_lock_init(&mixer_res->reg_slock);
887
888 mixer_res->mixer = clk_get(dev, "mixer");
889 if (IS_ERR_OR_NULL(mixer_res->mixer)) {
890 dev_err(dev, "failed to get clock 'mixer'\n");
891 ret = -ENODEV;
892 goto fail;
893 }
894 mixer_res->vp = clk_get(dev, "vp");
895 if (IS_ERR_OR_NULL(mixer_res->vp)) {
896 dev_err(dev, "failed to get clock 'vp'\n");
897 ret = -ENODEV;
898 goto fail;
899 }
900 mixer_res->sclk_mixer = clk_get(dev, "sclk_mixer");
901 if (IS_ERR_OR_NULL(mixer_res->sclk_mixer)) {
902 dev_err(dev, "failed to get clock 'sclk_mixer'\n");
903 ret = -ENODEV;
904 goto fail;
905 }
906 mixer_res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
907 if (IS_ERR_OR_NULL(mixer_res->sclk_hdmi)) {
908 dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
909 ret = -ENODEV;
910 goto fail;
911 }
912 mixer_res->sclk_dac = clk_get(dev, "sclk_dac");
913 if (IS_ERR_OR_NULL(mixer_res->sclk_dac)) {
914 dev_err(dev, "failed to get clock 'sclk_dac'\n");
915 ret = -ENODEV;
916 goto fail;
917 }
918 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mxr");
919 if (res == NULL) {
920 dev_err(dev, "get memory resource failed.\n");
921 ret = -ENXIO;
922 goto fail;
923 }
924
925 clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
926
927 mixer_res->mixer_regs = ioremap(res->start, resource_size(res));
928 if (mixer_res->mixer_regs == NULL) {
929 dev_err(dev, "register mapping failed.\n");
930 ret = -ENXIO;
931 goto fail;
932 }
933
934 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vp");
935 if (res == NULL) {
936 dev_err(dev, "get memory resource failed.\n");
937 ret = -ENXIO;
938 goto fail_mixer_regs;
939 }
940
941 mixer_res->vp_regs = ioremap(res->start, resource_size(res));
942 if (mixer_res->vp_regs == NULL) {
943 dev_err(dev, "register mapping failed.\n");
944 ret = -ENXIO;
945 goto fail_mixer_regs;
946 }
947
948 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq");
949 if (res == NULL) {
950 dev_err(dev, "get interrupt resource failed.\n");
951 ret = -ENXIO;
952 goto fail_vp_regs;
953 }
954
955 ret = request_irq(res->start, mixer_irq_handler, 0, "drm_mixer", ctx);
956 if (ret) {
957 dev_err(dev, "request interrupt failed.\n");
958 goto fail_vp_regs;
959 }
960 mixer_res->irq = res->start;
961
962 return 0;
963
964fail_vp_regs:
965 iounmap(mixer_res->vp_regs);
966
967fail_mixer_regs:
968 iounmap(mixer_res->mixer_regs);
969
970fail:
971 if (!IS_ERR_OR_NULL(mixer_res->sclk_dac))
972 clk_put(mixer_res->sclk_dac);
973 if (!IS_ERR_OR_NULL(mixer_res->sclk_hdmi))
974 clk_put(mixer_res->sclk_hdmi);
975 if (!IS_ERR_OR_NULL(mixer_res->sclk_mixer))
976 clk_put(mixer_res->sclk_mixer);
977 if (!IS_ERR_OR_NULL(mixer_res->vp))
978 clk_put(mixer_res->vp);
979 if (!IS_ERR_OR_NULL(mixer_res->mixer))
980 clk_put(mixer_res->mixer);
981 mixer_res->dev = NULL;
982 return ret;
983}
984
985static void mixer_resources_cleanup(struct mixer_context *ctx)
986{
987 struct mixer_resources *res = &ctx->mixer_res;
988
989 disable_irq(res->irq);
990 free_irq(res->irq, ctx);
991
992 iounmap(res->vp_regs);
993 iounmap(res->mixer_regs);
994}
995
996static int __devinit mixer_probe(struct platform_device *pdev)
997{
998 struct device *dev = &pdev->dev;
999 struct exynos_drm_hdmi_context *drm_hdmi_ctx;
1000 struct mixer_context *ctx;
1001 int ret;
1002
1003 dev_info(dev, "probe start\n");
1004
1005 drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL);
1006 if (!drm_hdmi_ctx) {
1007 DRM_ERROR("failed to allocate common hdmi context.\n");
1008 return -ENOMEM;
1009 }
1010
1011 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
1012 if (!ctx) {
1013 DRM_ERROR("failed to alloc mixer context.\n");
1014 kfree(drm_hdmi_ctx);
1015 return -ENOMEM;
1016 }
1017
1018 drm_hdmi_ctx->ctx = (void *)ctx;
1019
1020 platform_set_drvdata(pdev, drm_hdmi_ctx);
1021
1022 /* acquire resources: regs, irqs, clocks */
1023 ret = mixer_resources_init(drm_hdmi_ctx, pdev);
1024 if (ret)
1025 goto fail;
1026
1027 /* register specific callback point to common hdmi. */
1028 exynos_drm_overlay_ops_register(&overlay_ops);
1029
1030 mixer_resource_poweron(ctx);
1031
1032 return 0;
1033
1034
1035fail:
1036 dev_info(dev, "probe failed\n");
1037 return ret;
1038}
1039
1040static int mixer_remove(struct platform_device *pdev)
1041{
1042 struct device *dev = &pdev->dev;
1043 struct exynos_drm_hdmi_context *drm_hdmi_ctx =
1044 platform_get_drvdata(pdev);
1045 struct mixer_context *ctx = (struct mixer_context *)drm_hdmi_ctx->ctx;
1046
1047 dev_info(dev, "remove sucessful\n");
1048
1049 mixer_resource_poweroff(ctx);
1050 mixer_resources_cleanup(ctx);
1051
1052 return 0;
1053}
1054
1055struct platform_driver mixer_driver = {
1056 .driver = {
1057 .name = "s5p-mixer",
1058 .owner = THIS_MODULE,
1059 .pm = &mixer_pm_ops,
1060 },
1061 .probe = mixer_probe,
1062 .remove = __devexit_p(mixer_remove),
1063};
1064EXPORT_SYMBOL(mixer_driver);
1065
1066MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>");
1067MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
1068MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
1069MODULE_DESCRIPTION("Samsung DRM HDMI mixer Driver");
1070MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.h b/drivers/gpu/drm/exynos/exynos_mixer.h
new file mode 100644
index 000000000000..cebacfefc077
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_mixer.h
@@ -0,0 +1,92 @@
1/*
2 *
3 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
4 * Authors:
5 * Seung-Woo Kim <sw0312.kim@samsung.com>
6 * Inki Dae <inki.dae@samsung.com>
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the next
16 * paragraph) shall be included in all copies or substantial portions of the
17 * Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 * OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28#ifndef _EXYNOS_MIXER_H_
29#define _EXYNOS_MIXER_H_
30
31#define HDMI_OVERLAY_NUMBER 3
32
33struct hdmi_win_data {
34 dma_addr_t dma_addr;
35 void __iomem *vaddr;
36 dma_addr_t chroma_dma_addr;
37 void __iomem *chroma_vaddr;
38 uint32_t pixel_format;
39 unsigned int bpp;
40 unsigned int crtc_x;
41 unsigned int crtc_y;
42 unsigned int crtc_width;
43 unsigned int crtc_height;
44 unsigned int fb_x;
45 unsigned int fb_y;
46 unsigned int fb_width;
47 unsigned int fb_height;
48 unsigned int mode_width;
49 unsigned int mode_height;
50 unsigned int scan_flags;
51};
52
53struct mixer_resources {
54 struct device *dev;
55 /** interrupt index */
56 int irq;
57 /** pointer to Mixer registers */
58 void __iomem *mixer_regs;
59 /** pointer to Video Processor registers */
60 void __iomem *vp_regs;
61 /** spinlock for protection of registers */
62 spinlock_t reg_slock;
63 /** other resources */
64 struct clk *mixer;
65 struct clk *vp;
66 struct clk *sclk_mixer;
67 struct clk *sclk_hdmi;
68 struct clk *sclk_dac;
69};
70
71struct mixer_context {
72 unsigned int default_win;
73 struct fb_videomode *default_timing;
74 unsigned int default_bpp;
75
76 /** mixer interrupt */
77 unsigned int irq;
78 /** current crtc pipe for vblank */
79 int pipe;
80 /** interlace scan mode */
81 bool interlace;
82 /** vp enabled status */
83 bool vp_enabled;
84
85 /** mixer and vp resources */
86 struct mixer_resources mixer_res;
87
88 /** overlay window data */
89 struct hdmi_win_data win_data[HDMI_OVERLAY_NUMBER];
90};
91
92#endif
diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h
new file mode 100644
index 000000000000..72e6b52be740
--- /dev/null
+++ b/drivers/gpu/drm/exynos/regs-hdmi.h
@@ -0,0 +1,147 @@
1/*
2 *
3 * Cloned from drivers/media/video/s5p-tv/regs-hdmi.h
4 *
5 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
6 * http://www.samsung.com/
7 *
8 * HDMI register header file for Samsung TVOUT driver
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13*/
14
15#ifndef SAMSUNG_REGS_HDMI_H
16#define SAMSUNG_REGS_HDMI_H
17
18/*
19 * Register part
20*/
21
22#define HDMI_CTRL_BASE(x) ((x) + 0x00000000)
23#define HDMI_CORE_BASE(x) ((x) + 0x00010000)
24#define HDMI_TG_BASE(x) ((x) + 0x00050000)
25
26/* Control registers */
27#define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000)
28#define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004)
29#define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C)
30#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0014)
31#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0018)
32#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x001C)
33#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0020)
34
35/* Core registers */
36#define HDMI_CON_0 HDMI_CORE_BASE(0x0000)
37#define HDMI_CON_1 HDMI_CORE_BASE(0x0004)
38#define HDMI_CON_2 HDMI_CORE_BASE(0x0008)
39#define HDMI_SYS_STATUS HDMI_CORE_BASE(0x0010)
40#define HDMI_PHY_STATUS HDMI_CORE_BASE(0x0014)
41#define HDMI_STATUS_EN HDMI_CORE_BASE(0x0020)
42#define HDMI_HPD HDMI_CORE_BASE(0x0030)
43#define HDMI_MODE_SEL HDMI_CORE_BASE(0x0040)
44#define HDMI_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050)
45#define HDMI_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054)
46#define HDMI_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058)
47#define HDMI_H_BLANK_0 HDMI_CORE_BASE(0x00A0)
48#define HDMI_H_BLANK_1 HDMI_CORE_BASE(0x00A4)
49#define HDMI_V_BLANK_0 HDMI_CORE_BASE(0x00B0)
50#define HDMI_V_BLANK_1 HDMI_CORE_BASE(0x00B4)
51#define HDMI_V_BLANK_2 HDMI_CORE_BASE(0x00B8)
52#define HDMI_H_V_LINE_0 HDMI_CORE_BASE(0x00C0)
53#define HDMI_H_V_LINE_1 HDMI_CORE_BASE(0x00C4)
54#define HDMI_H_V_LINE_2 HDMI_CORE_BASE(0x00C8)
55#define HDMI_VSYNC_POL HDMI_CORE_BASE(0x00E4)
56#define HDMI_INT_PRO_MODE HDMI_CORE_BASE(0x00E8)
57#define HDMI_V_BLANK_F_0 HDMI_CORE_BASE(0x0110)
58#define HDMI_V_BLANK_F_1 HDMI_CORE_BASE(0x0114)
59#define HDMI_V_BLANK_F_2 HDMI_CORE_BASE(0x0118)
60#define HDMI_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120)
61#define HDMI_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124)
62#define HDMI_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128)
63#define HDMI_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130)
64#define HDMI_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134)
65#define HDMI_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138)
66#define HDMI_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140)
67#define HDMI_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144)
68#define HDMI_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148)
69#define HDMI_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150)
70#define HDMI_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154)
71#define HDMI_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158)
72#define HDMI_ACR_CON HDMI_CORE_BASE(0x0180)
73#define HDMI_AVI_CON HDMI_CORE_BASE(0x0300)
74#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n))
75#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x05C0)
76#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4)
77#define HDMI_HPD_GEN HDMI_CORE_BASE(0x05C8)
78#define HDMI_AUI_CON HDMI_CORE_BASE(0x0360)
79#define HDMI_SPD_CON HDMI_CORE_BASE(0x0400)
80
81/* Timing generator registers */
82#define HDMI_TG_CMD HDMI_TG_BASE(0x0000)
83#define HDMI_TG_H_FSZ_L HDMI_TG_BASE(0x0018)
84#define HDMI_TG_H_FSZ_H HDMI_TG_BASE(0x001C)
85#define HDMI_TG_HACT_ST_L HDMI_TG_BASE(0x0020)
86#define HDMI_TG_HACT_ST_H HDMI_TG_BASE(0x0024)
87#define HDMI_TG_HACT_SZ_L HDMI_TG_BASE(0x0028)
88#define HDMI_TG_HACT_SZ_H HDMI_TG_BASE(0x002C)
89#define HDMI_TG_V_FSZ_L HDMI_TG_BASE(0x0030)
90#define HDMI_TG_V_FSZ_H HDMI_TG_BASE(0x0034)
91#define HDMI_TG_VSYNC_L HDMI_TG_BASE(0x0038)
92#define HDMI_TG_VSYNC_H HDMI_TG_BASE(0x003C)
93#define HDMI_TG_VSYNC2_L HDMI_TG_BASE(0x0040)
94#define HDMI_TG_VSYNC2_H HDMI_TG_BASE(0x0044)
95#define HDMI_TG_VACT_ST_L HDMI_TG_BASE(0x0048)
96#define HDMI_TG_VACT_ST_H HDMI_TG_BASE(0x004C)
97#define HDMI_TG_VACT_SZ_L HDMI_TG_BASE(0x0050)
98#define HDMI_TG_VACT_SZ_H HDMI_TG_BASE(0x0054)
99#define HDMI_TG_FIELD_CHG_L HDMI_TG_BASE(0x0058)
100#define HDMI_TG_FIELD_CHG_H HDMI_TG_BASE(0x005C)
101#define HDMI_TG_VACT_ST2_L HDMI_TG_BASE(0x0060)
102#define HDMI_TG_VACT_ST2_H HDMI_TG_BASE(0x0064)
103#define HDMI_TG_VSYNC_TOP_HDMI_L HDMI_TG_BASE(0x0078)
104#define HDMI_TG_VSYNC_TOP_HDMI_H HDMI_TG_BASE(0x007C)
105#define HDMI_TG_VSYNC_BOT_HDMI_L HDMI_TG_BASE(0x0080)
106#define HDMI_TG_VSYNC_BOT_HDMI_H HDMI_TG_BASE(0x0084)
107#define HDMI_TG_FIELD_TOP_HDMI_L HDMI_TG_BASE(0x0088)
108#define HDMI_TG_FIELD_TOP_HDMI_H HDMI_TG_BASE(0x008C)
109#define HDMI_TG_FIELD_BOT_HDMI_L HDMI_TG_BASE(0x0090)
110#define HDMI_TG_FIELD_BOT_HDMI_H HDMI_TG_BASE(0x0094)
111
112/*
113 * Bit definition part
114 */
115
116/* HDMI_INTC_CON */
117#define HDMI_INTC_EN_GLOBAL (1 << 6)
118#define HDMI_INTC_EN_HPD_PLUG (1 << 3)
119#define HDMI_INTC_EN_HPD_UNPLUG (1 << 2)
120
121/* HDMI_INTC_FLAG */
122#define HDMI_INTC_FLAG_HPD_PLUG (1 << 3)
123#define HDMI_INTC_FLAG_HPD_UNPLUG (1 << 2)
124
125/* HDMI_PHY_RSTOUT */
126#define HDMI_PHY_SW_RSTOUT (1 << 0)
127
128/* HDMI_CORE_RSTOUT */
129#define HDMI_CORE_SW_RSTOUT (1 << 0)
130
131/* HDMI_CON_0 */
132#define HDMI_BLUE_SCR_EN (1 << 5)
133#define HDMI_EN (1 << 0)
134
135/* HDMI_PHY_STATUS */
136#define HDMI_PHY_STATUS_READY (1 << 0)
137
138/* HDMI_MODE_SEL */
139#define HDMI_MODE_HDMI_EN (1 << 1)
140#define HDMI_MODE_DVI_EN (1 << 0)
141#define HDMI_MODE_MASK (3 << 0)
142
143/* HDMI_TG_CMD */
144#define HDMI_TG_EN (1 << 0)
145#define HDMI_FIELD_EN (1 << 1)
146
147#endif /* SAMSUNG_REGS_HDMI_H */
diff --git a/drivers/gpu/drm/exynos/regs-mixer.h b/drivers/gpu/drm/exynos/regs-mixer.h
new file mode 100644
index 000000000000..fd2f4d14cf6d
--- /dev/null
+++ b/drivers/gpu/drm/exynos/regs-mixer.h
@@ -0,0 +1,141 @@
1/*
2 *
3 * Cloned from drivers/media/video/s5p-tv/regs-mixer.h
4 *
5 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
6 * http://www.samsung.com/
7 *
8 * Mixer register header file for Samsung Mixer driver
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13*/
14#ifndef SAMSUNG_REGS_MIXER_H
15#define SAMSUNG_REGS_MIXER_H
16
17/*
18 * Register part
19 */
20#define MXR_STATUS 0x0000
21#define MXR_CFG 0x0004
22#define MXR_INT_EN 0x0008
23#define MXR_INT_STATUS 0x000C
24#define MXR_LAYER_CFG 0x0010
25#define MXR_VIDEO_CFG 0x0014
26#define MXR_GRAPHIC0_CFG 0x0020
27#define MXR_GRAPHIC0_BASE 0x0024
28#define MXR_GRAPHIC0_SPAN 0x0028
29#define MXR_GRAPHIC0_SXY 0x002C
30#define MXR_GRAPHIC0_WH 0x0030
31#define MXR_GRAPHIC0_DXY 0x0034
32#define MXR_GRAPHIC0_BLANK 0x0038
33#define MXR_GRAPHIC1_CFG 0x0040
34#define MXR_GRAPHIC1_BASE 0x0044
35#define MXR_GRAPHIC1_SPAN 0x0048
36#define MXR_GRAPHIC1_SXY 0x004C
37#define MXR_GRAPHIC1_WH 0x0050
38#define MXR_GRAPHIC1_DXY 0x0054
39#define MXR_GRAPHIC1_BLANK 0x0058
40#define MXR_BG_CFG 0x0060
41#define MXR_BG_COLOR0 0x0064
42#define MXR_BG_COLOR1 0x0068
43#define MXR_BG_COLOR2 0x006C
44#define MXR_CM_COEFF_Y 0x0080
45#define MXR_CM_COEFF_CB 0x0084
46#define MXR_CM_COEFF_CR 0x0088
47#define MXR_GRAPHIC0_BASE_S 0x2024
48#define MXR_GRAPHIC1_BASE_S 0x2044
49
50/* for parametrized access to layer registers */
51#define MXR_GRAPHIC_CFG(i) (0x0020 + (i) * 0x20)
52#define MXR_GRAPHIC_BASE(i) (0x0024 + (i) * 0x20)
53#define MXR_GRAPHIC_SPAN(i) (0x0028 + (i) * 0x20)
54#define MXR_GRAPHIC_SXY(i) (0x002C + (i) * 0x20)
55#define MXR_GRAPHIC_WH(i) (0x0030 + (i) * 0x20)
56#define MXR_GRAPHIC_DXY(i) (0x0034 + (i) * 0x20)
57#define MXR_GRAPHIC_BLANK(i) (0x0038 + (i) * 0x20)
58#define MXR_GRAPHIC_BASE_S(i) (0x2024 + (i) * 0x20)
59
60/*
61 * Bit definition part
62 */
63
64/* generates mask for range of bits */
65#define MXR_MASK(high_bit, low_bit) \
66 (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit))
67
68#define MXR_MASK_VAL(val, high_bit, low_bit) \
69 (((val) << (low_bit)) & MXR_MASK(high_bit, low_bit))
70
71/* bits for MXR_STATUS */
72#define MXR_STATUS_16_BURST (1 << 7)
73#define MXR_STATUS_BURST_MASK (1 << 7)
74#define MXR_STATUS_BIG_ENDIAN (1 << 3)
75#define MXR_STATUS_ENDIAN_MASK (1 << 3)
76#define MXR_STATUS_SYNC_ENABLE (1 << 2)
77#define MXR_STATUS_REG_RUN (1 << 0)
78
79/* bits for MXR_CFG */
80#define MXR_CFG_RGB601_0_255 (0 << 9)
81#define MXR_CFG_RGB601_16_235 (1 << 9)
82#define MXR_CFG_RGB709_0_255 (2 << 9)
83#define MXR_CFG_RGB709_16_235 (3 << 9)
84#define MXR_CFG_RGB_FMT_MASK 0x600
85#define MXR_CFG_OUT_YUV444 (0 << 8)
86#define MXR_CFG_OUT_RGB888 (1 << 8)
87#define MXR_CFG_OUT_MASK (1 << 8)
88#define MXR_CFG_DST_SDO (0 << 7)
89#define MXR_CFG_DST_HDMI (1 << 7)
90#define MXR_CFG_DST_MASK (1 << 7)
91#define MXR_CFG_SCAN_HD_720 (0 << 6)
92#define MXR_CFG_SCAN_HD_1080 (1 << 6)
93#define MXR_CFG_GRP1_ENABLE (1 << 5)
94#define MXR_CFG_GRP0_ENABLE (1 << 4)
95#define MXR_CFG_VP_ENABLE (1 << 3)
96#define MXR_CFG_SCAN_INTERLACE (0 << 2)
97#define MXR_CFG_SCAN_PROGRASSIVE (1 << 2)
98#define MXR_CFG_SCAN_NTSC (0 << 1)
99#define MXR_CFG_SCAN_PAL (1 << 1)
100#define MXR_CFG_SCAN_SD (0 << 0)
101#define MXR_CFG_SCAN_HD (1 << 0)
102#define MXR_CFG_SCAN_MASK 0x47
103
104/* bits for MXR_GRAPHICn_CFG */
105#define MXR_GRP_CFG_COLOR_KEY_DISABLE (1 << 21)
106#define MXR_GRP_CFG_BLEND_PRE_MUL (1 << 20)
107#define MXR_GRP_CFG_WIN_BLEND_EN (1 << 17)
108#define MXR_GRP_CFG_PIXEL_BLEND_EN (1 << 16)
109#define MXR_GRP_CFG_FORMAT_VAL(x) MXR_MASK_VAL(x, 11, 8)
110#define MXR_GRP_CFG_FORMAT_MASK MXR_GRP_CFG_FORMAT_VAL(~0)
111#define MXR_GRP_CFG_ALPHA_VAL(x) MXR_MASK_VAL(x, 7, 0)
112
113/* bits for MXR_GRAPHICn_WH */
114#define MXR_GRP_WH_H_SCALE(x) MXR_MASK_VAL(x, 28, 28)
115#define MXR_GRP_WH_V_SCALE(x) MXR_MASK_VAL(x, 12, 12)
116#define MXR_GRP_WH_WIDTH(x) MXR_MASK_VAL(x, 26, 16)
117#define MXR_GRP_WH_HEIGHT(x) MXR_MASK_VAL(x, 10, 0)
118
119/* bits for MXR_GRAPHICn_SXY */
120#define MXR_GRP_SXY_SX(x) MXR_MASK_VAL(x, 26, 16)
121#define MXR_GRP_SXY_SY(x) MXR_MASK_VAL(x, 10, 0)
122
123/* bits for MXR_GRAPHICn_DXY */
124#define MXR_GRP_DXY_DX(x) MXR_MASK_VAL(x, 26, 16)
125#define MXR_GRP_DXY_DY(x) MXR_MASK_VAL(x, 10, 0)
126
127/* bits for MXR_INT_EN */
128#define MXR_INT_EN_VSYNC (1 << 11)
129#define MXR_INT_EN_ALL (0x0f << 8)
130
131/* bit for MXR_INT_STATUS */
132#define MXR_INT_CLEAR_VSYNC (1 << 11)
133#define MXR_INT_STATUS_VSYNC (1 << 0)
134
135/* bit for MXR_LAYER_CFG */
136#define MXR_LAYER_CFG_GRP1_VAL(x) MXR_MASK_VAL(x, 11, 8)
137#define MXR_LAYER_CFG_GRP0_VAL(x) MXR_MASK_VAL(x, 7, 4)
138#define MXR_LAYER_CFG_VP_VAL(x) MXR_MASK_VAL(x, 3, 0)
139
140#endif /* SAMSUNG_REGS_MIXER_H */
141
diff --git a/drivers/gpu/drm/exynos/regs-vp.h b/drivers/gpu/drm/exynos/regs-vp.h
new file mode 100644
index 000000000000..10b737af0a72
--- /dev/null
+++ b/drivers/gpu/drm/exynos/regs-vp.h
@@ -0,0 +1,91 @@
1/*
2 *
3 * Cloned from drivers/media/video/s5p-tv/regs-vp.h
4 *
5 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
6 * http://www.samsung.com/
7 *
8 * Video processor register header file for Samsung Mixer driver
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 */
14
15#ifndef SAMSUNG_REGS_VP_H
16#define SAMSUNG_REGS_VP_H
17
18/*
19 * Register part
20 */
21
22#define VP_ENABLE 0x0000
23#define VP_SRESET 0x0004
24#define VP_SHADOW_UPDATE 0x0008
25#define VP_FIELD_ID 0x000C
26#define VP_MODE 0x0010
27#define VP_IMG_SIZE_Y 0x0014
28#define VP_IMG_SIZE_C 0x0018
29#define VP_PER_RATE_CTRL 0x001C
30#define VP_TOP_Y_PTR 0x0028
31#define VP_BOT_Y_PTR 0x002C
32#define VP_TOP_C_PTR 0x0030
33#define VP_BOT_C_PTR 0x0034
34#define VP_ENDIAN_MODE 0x03CC
35#define VP_SRC_H_POSITION 0x0044
36#define VP_SRC_V_POSITION 0x0048
37#define VP_SRC_WIDTH 0x004C
38#define VP_SRC_HEIGHT 0x0050
39#define VP_DST_H_POSITION 0x0054
40#define VP_DST_V_POSITION 0x0058
41#define VP_DST_WIDTH 0x005C
42#define VP_DST_HEIGHT 0x0060
43#define VP_H_RATIO 0x0064
44#define VP_V_RATIO 0x0068
45#define VP_POLY8_Y0_LL 0x006C
46#define VP_POLY4_Y0_LL 0x00EC
47#define VP_POLY4_C0_LL 0x012C
48
49/*
50 * Bit definition part
51 */
52
53/* generates mask for range of bits */
54
55#define VP_MASK(high_bit, low_bit) \
56 (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit))
57
58#define VP_MASK_VAL(val, high_bit, low_bit) \
59 (((val) << (low_bit)) & VP_MASK(high_bit, low_bit))
60
61 /* VP_ENABLE */
62#define VP_ENABLE_ON (1 << 0)
63
64/* VP_SRESET */
65#define VP_SRESET_PROCESSING (1 << 0)
66
67/* VP_SHADOW_UPDATE */
68#define VP_SHADOW_UPDATE_ENABLE (1 << 0)
69
70/* VP_MODE */
71#define VP_MODE_NV12 (0 << 6)
72#define VP_MODE_NV21 (1 << 6)
73#define VP_MODE_LINE_SKIP (1 << 5)
74#define VP_MODE_MEM_LINEAR (0 << 4)
75#define VP_MODE_MEM_TILED (1 << 4)
76#define VP_MODE_FMT_MASK (5 << 4)
77#define VP_MODE_FIELD_ID_AUTO_TOGGLING (1 << 2)
78#define VP_MODE_2D_IPC (1 << 1)
79
80/* VP_IMG_SIZE_Y */
81/* VP_IMG_SIZE_C */
82#define VP_IMG_HSIZE(x) VP_MASK_VAL(x, 29, 16)
83#define VP_IMG_VSIZE(x) VP_MASK_VAL(x, 13, 0)
84
85/* VP_SRC_H_POSITION */
86#define VP_SRC_H_POSITION_VAL(x) VP_MASK_VAL(x, 14, 4)
87
88/* VP_ENDIAN_MODE */
89#define VP_ENDIAN_MODE_LITTLE (1 << 0)
90
91#endif /* SAMSUNG_REGS_VP_H */
diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h
index 7a2262a7689f..5e120f1c5cd9 100644
--- a/include/drm/exynos_drm.h
+++ b/include/drm/exynos_drm.h
@@ -112,4 +112,31 @@ struct exynos_drm_fimd_pdata {
112 unsigned int bpp; 112 unsigned int bpp;
113}; 113};
114 114
115/**
116 * Platform Specific Structure for DRM based HDMI.
117 *
118 * @hdmi_dev: device point to specific hdmi driver.
119 * @mixer_dev: device point to specific mixer driver.
120 *
121 * this structure is used for common hdmi driver and each device object
122 * would be used to access specific device driver(hdmi or mixer driver)
123 */
124struct exynos_drm_common_hdmi_pd {
125 struct device *hdmi_dev;
126 struct device *mixer_dev;
127};
128
129/**
130 * Platform Specific Structure for DRM based HDMI core.
131 *
132 * @timing: default video mode for initializing
133 * @default_win: default window layer number to be used for UI.
134 * @bpp: default bit per pixel.
135 */
136struct exynos_drm_hdmi_pdata {
137 struct fb_videomode timing;
138 unsigned int default_win;
139 unsigned int bpp;
140};
141
115#endif 142#endif