diff options
Diffstat (limited to 'drivers/gpu/drm/exynos')
-rw-r--r-- | drivers/gpu/drm/exynos/Kconfig | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_ddc.c | 58 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_hdmi.c | 439 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_hdmi.h | 73 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_hdmi.c | 1176 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_hdmi.h | 87 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_hdmiphy.c | 58 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_mixer.c | 1070 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_mixer.h | 92 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/regs-hdmi.h | 147 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/regs-mixer.h | 141 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/regs-vp.h | 91 |
13 files changed, 3441 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 | |||
22 | config 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 | ||
11 | obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o | 11 | obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o |
12 | obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o | 12 | obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o |
13 | obj-$(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 | |||
24 | static 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 | |||
35 | static 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 | |||
43 | static struct i2c_device_id ddc_idtable[] = { | ||
44 | {"s5p_ddc", 0}, | ||
45 | { }, | ||
46 | }; | ||
47 | |||
48 | struct 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 | }; | ||
58 | EXPORT_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. */ | ||
33 | static struct exynos_hdmi_display_ops *hdmi_display_ops; | ||
34 | static struct exynos_hdmi_manager_ops *hdmi_manager_ops; | ||
35 | static struct exynos_hdmi_overlay_ops *hdmi_overlay_ops; | ||
36 | |||
37 | struct 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 | |||
44 | void 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 | } | ||
52 | EXPORT_SYMBOL(exynos_drm_display_ops_register); | ||
53 | |||
54 | void 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 | } | ||
62 | EXPORT_SYMBOL(exynos_drm_manager_ops_register); | ||
63 | |||
64 | void 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 | } | ||
72 | EXPORT_SYMBOL(exynos_drm_overlay_ops_register); | ||
73 | |||
74 | static 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 | |||
86 | static 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 | |||
100 | static 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 | |||
113 | static 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 | |||
125 | static 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 | |||
133 | static 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 | |||
148 | static 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 | |||
158 | static 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 | |||
168 | static 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 | |||
178 | static 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 | |||
199 | static 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 | |||
207 | static 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 | |||
218 | static 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 | |||
228 | static 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 | |||
238 | static 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 | |||
245 | static 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 | |||
309 | err_mixerdrv: | ||
310 | platform_driver_unregister(&mixer_driver); | ||
311 | err_hdmidrv: | ||
312 | platform_driver_unregister(&hdmi_driver); | ||
313 | return ret; | ||
314 | } | ||
315 | |||
316 | static 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 | |||
324 | static 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 | |||
339 | static 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 | |||
372 | static int hdmi_runtime_suspend(struct device *dev) | ||
373 | { | ||
374 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
375 | |||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | static int hdmi_runtime_resume(struct device *dev) | ||
380 | { | ||
381 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
382 | |||
383 | return 0; | ||
384 | } | ||
385 | |||
386 | static const struct dev_pm_ops hdmi_pm_ops = { | ||
387 | .runtime_suspend = hdmi_runtime_suspend, | ||
388 | .runtime_resume = hdmi_runtime_resume, | ||
389 | }; | ||
390 | |||
391 | static 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 | |||
403 | static 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 | |||
413 | static 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 | |||
428 | static void __exit exynos_drm_hdmi_exit(void) | ||
429 | { | ||
430 | platform_driver_unregister(&exynos_drm_common_hdmi_driver); | ||
431 | } | ||
432 | |||
433 | module_init(exynos_drm_hdmi_init); | ||
434 | module_exit(exynos_drm_hdmi_exit); | ||
435 | |||
436 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
437 | MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>"); | ||
438 | MODULE_DESCRIPTION("Samsung SoC DRM HDMI Driver"); | ||
439 | MODULE_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 | */ | ||
36 | struct exynos_drm_hdmi_context { | ||
37 | struct drm_device *drm_dev; | ||
38 | void *ctx; | ||
39 | }; | ||
40 | |||
41 | struct 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 | |||
49 | struct exynos_hdmi_manager_ops { | ||
50 | void (*mode_set)(void *ctx, void *mode); | ||
51 | void (*commit)(void *ctx); | ||
52 | void (*disable)(void *ctx); | ||
53 | }; | ||
54 | |||
55 | struct 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 | |||
63 | extern struct platform_driver hdmi_driver; | ||
64 | extern struct platform_driver mixer_driver; | ||
65 | |||
66 | void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops | ||
67 | *display_ops); | ||
68 | void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops | ||
69 | *manager_ops); | ||
70 | void 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 | |||
46 | static 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 | |||
53 | static 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 | |||
60 | static 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 | |||
67 | static 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 | |||
74 | static 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 | |||
81 | struct 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 | |||
113 | struct 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 | |||
126 | struct hdmi_preset_conf { | ||
127 | struct hdmi_core_regs core; | ||
128 | struct hdmi_tg_regs tg; | ||
129 | }; | ||
130 | |||
131 | static 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 | |||
157 | static 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 | |||
185 | static 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 | |||
213 | static 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 | |||
241 | static 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 | |||
269 | static 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 | |||
297 | static 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 | |||
308 | static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id) | ||
309 | { | ||
310 | return readl(hdata->regs + reg_id); | ||
311 | } | ||
312 | |||
313 | static 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 | |||
319 | static 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 | |||
327 | static 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 | |||
414 | static 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 | |||
430 | static 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 | |||
441 | static 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 | |||
465 | static 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 | |||
488 | static 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 | |||
510 | static 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 | |||
517 | static 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 | |||
532 | static 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 | |||
567 | static 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 | |||
656 | static 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 | |||
678 | static 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 | |||
722 | static 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 | |||
741 | static 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 | |||
755 | static 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 | |||
766 | static 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 | |||
778 | static 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 | */ | ||
787 | static 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 | |||
797 | static 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 | |||
822 | static 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; | ||
885 | fail: | ||
886 | DRM_ERROR("HDMI resource init - failed\n"); | ||
887 | return -ENODEV; | ||
888 | } | ||
889 | |||
890 | static 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 | |||
912 | static 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 | |||
932 | static 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 | |||
947 | static 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 | |||
958 | static 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 | |||
969 | static const struct dev_pm_ops hdmi_pm_ops = { | ||
970 | .runtime_suspend = hdmi_runtime_suspend, | ||
971 | .runtime_resume = hdmi_runtime_resume, | ||
972 | }; | ||
973 | |||
974 | static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy; | ||
975 | |||
976 | void hdmi_attach_ddc_client(struct i2c_client *ddc) | ||
977 | { | ||
978 | if (ddc) | ||
979 | hdmi_ddc = ddc; | ||
980 | } | ||
981 | EXPORT_SYMBOL(hdmi_attach_ddc_client); | ||
982 | |||
983 | void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy) | ||
984 | { | ||
985 | if (hdmiphy) | ||
986 | hdmi_hdmiphy = hdmiphy; | ||
987 | } | ||
988 | EXPORT_SYMBOL(hdmi_attach_hdmiphy_client); | ||
989 | |||
990 | static 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 | |||
1110 | err_workqueue: | ||
1111 | destroy_workqueue(hdata->wq); | ||
1112 | err_hdmiphy: | ||
1113 | i2c_del_driver(&hdmiphy_driver); | ||
1114 | err_ddc: | ||
1115 | i2c_del_driver(&ddc_driver); | ||
1116 | err_iomap: | ||
1117 | iounmap(hdata->regs); | ||
1118 | err_req_region: | ||
1119 | release_resource(hdata->regs_res); | ||
1120 | kfree(hdata->regs_res); | ||
1121 | err_resource: | ||
1122 | hdmi_resources_cleanup(hdata); | ||
1123 | err_data: | ||
1124 | kfree(hdata); | ||
1125 | kfree(drm_hdmi_ctx); | ||
1126 | return ret; | ||
1127 | } | ||
1128 | |||
1129 | static 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 | |||
1161 | struct 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 | }; | ||
1170 | EXPORT_SYMBOL(hdmi_driver); | ||
1171 | |||
1172 | MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>"); | ||
1173 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
1174 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | ||
1175 | MODULE_DESCRIPTION("Samsung DRM HDMI core Driver"); | ||
1176 | MODULE_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 | |||
31 | struct 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 | |||
40 | struct 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 | |||
50 | struct 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 | |||
81 | void hdmi_attach_ddc_client(struct i2c_client *ddc); | ||
82 | void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy); | ||
83 | |||
84 | extern struct i2c_driver hdmiphy_driver; | ||
85 | extern 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 | |||
24 | static 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 | |||
35 | static 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 | |||
43 | static const struct i2c_device_id hdmiphy_id[] = { | ||
44 | { "s5p_hdmiphy", 0 }, | ||
45 | { }, | ||
46 | }; | ||
47 | |||
48 | struct 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 | }; | ||
58 | EXPORT_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 | |||
44 | static 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 | |||
55 | static 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 | |||
66 | static 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 | |||
73 | static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id) | ||
74 | { | ||
75 | return readl(res->vp_regs + reg_id); | ||
76 | } | ||
77 | |||
78 | static 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 | |||
84 | static 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 | |||
93 | static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id) | ||
94 | { | ||
95 | return readl(res->mixer_regs + reg_id); | ||
96 | } | ||
97 | |||
98 | static 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 | |||
104 | static 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 | |||
113 | static void mixer_regs_dump(struct mixer_context *ctx) | ||
114 | { | ||
115 | #define DUMPREG(reg_id) \ | ||
116 | do { \ | ||
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 | |||
145 | static void vp_regs_dump(struct mixer_context *ctx) | ||
146 | { | ||
147 | #define DUMPREG(reg_id) \ | ||
148 | do { \ | ||
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 | |||
180 | static 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 | |||
192 | static 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 | |||
202 | static 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 | |||
214 | static 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 | |||
238 | static 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 | |||
279 | static 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 | |||
298 | static 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 | |||
307 | static 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 | |||
439 | static 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 | |||
536 | static 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 | |||
551 | static 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 | |||
567 | static 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 | |||
578 | static 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 | |||
632 | static 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 | |||
653 | static 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 | |||
679 | static 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 */ | ||
688 | static 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 | |||
720 | static 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 | |||
750 | out: | ||
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 | |||
764 | static 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 | |||
824 | static 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 | |||
837 | static 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 | |||
848 | static 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 | |||
859 | static 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 | |||
870 | static const struct dev_pm_ops mixer_pm_ops = { | ||
871 | .runtime_suspend = mixer_runtime_suspend, | ||
872 | .runtime_resume = mixer_runtime_resume, | ||
873 | }; | ||
874 | |||
875 | static 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 | |||
964 | fail_vp_regs: | ||
965 | iounmap(mixer_res->vp_regs); | ||
966 | |||
967 | fail_mixer_regs: | ||
968 | iounmap(mixer_res->mixer_regs); | ||
969 | |||
970 | fail: | ||
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 | |||
985 | static 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 | |||
996 | static 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 | |||
1035 | fail: | ||
1036 | dev_info(dev, "probe failed\n"); | ||
1037 | return ret; | ||
1038 | } | ||
1039 | |||
1040 | static 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 | |||
1055 | struct 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 | }; | ||
1064 | EXPORT_SYMBOL(mixer_driver); | ||
1065 | |||
1066 | MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>"); | ||
1067 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
1068 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | ||
1069 | MODULE_DESCRIPTION("Samsung DRM HDMI mixer Driver"); | ||
1070 | MODULE_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 | |||
33 | struct 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 | |||
53 | struct 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 | |||
71 | struct 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 */ | ||