diff options
Diffstat (limited to 'drivers/gpu/drm/imx')
-rw-r--r-- | drivers/gpu/drm/imx/Kconfig | 53 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/Makefile | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/imx-drm-core.c | 704 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/imx-drm.h | 56 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/imx-hdmi.c | 1766 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/imx-hdmi.h | 1032 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/imx-ldb.c | 615 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/imx-tve.c | 735 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/ipuv3-crtc.c | 518 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/ipuv3-plane.c | 363 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/ipuv3-plane.h | 55 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/parallel-display.c | 295 |
12 files changed, 6204 insertions, 0 deletions
diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig new file mode 100644 index 000000000000..82fb758a29bc --- /dev/null +++ b/drivers/gpu/drm/imx/Kconfig | |||
@@ -0,0 +1,53 @@ | |||
1 | config DRM_IMX | ||
2 | tristate "DRM Support for Freescale i.MX" | ||
3 | select DRM_KMS_HELPER | ||
4 | select DRM_KMS_FB_HELPER | ||
5 | select VIDEOMODE_HELPERS | ||
6 | select DRM_GEM_CMA_HELPER | ||
7 | select DRM_KMS_CMA_HELPER | ||
8 | depends on DRM && (ARCH_MXC || ARCH_MULTIPLATFORM) | ||
9 | help | ||
10 | enable i.MX graphics support | ||
11 | |||
12 | config DRM_IMX_FB_HELPER | ||
13 | tristate "provide legacy framebuffer /dev/fb0" | ||
14 | select DRM_KMS_CMA_HELPER | ||
15 | depends on DRM_IMX | ||
16 | help | ||
17 | The DRM framework can provide a legacy /dev/fb0 framebuffer | ||
18 | for your device. This is necessary to get a framebuffer console | ||
19 | and also for applications using the legacy framebuffer API | ||
20 | |||
21 | config DRM_IMX_PARALLEL_DISPLAY | ||
22 | tristate "Support for parallel displays" | ||
23 | select DRM_PANEL | ||
24 | depends on DRM_IMX | ||
25 | select VIDEOMODE_HELPERS | ||
26 | |||
27 | config DRM_IMX_TVE | ||
28 | tristate "Support for TV and VGA displays" | ||
29 | depends on DRM_IMX | ||
30 | select REGMAP_MMIO | ||
31 | help | ||
32 | Choose this to enable the internal Television Encoder (TVe) | ||
33 | found on i.MX53 processors. | ||
34 | |||
35 | config DRM_IMX_LDB | ||
36 | tristate "Support for LVDS displays" | ||
37 | depends on DRM_IMX && MFD_SYSCON | ||
38 | help | ||
39 | Choose this to enable the internal LVDS Display Bridge (LDB) | ||
40 | found on i.MX53 and i.MX6 processors. | ||
41 | |||
42 | config DRM_IMX_IPUV3 | ||
43 | tristate "DRM Support for i.MX IPUv3" | ||
44 | depends on DRM_IMX | ||
45 | depends on IMX_IPUV3_CORE | ||
46 | help | ||
47 | Choose this if you have a i.MX5 or i.MX6 processor. | ||
48 | |||
49 | config DRM_IMX_HDMI | ||
50 | tristate "Freescale i.MX DRM HDMI" | ||
51 | depends on DRM_IMX | ||
52 | help | ||
53 | Choose this if you want to use HDMI on i.MX6. | ||
diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile new file mode 100644 index 000000000000..582c438d8cbd --- /dev/null +++ b/drivers/gpu/drm/imx/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | |||
2 | imxdrm-objs := imx-drm-core.o | ||
3 | |||
4 | obj-$(CONFIG_DRM_IMX) += imxdrm.o | ||
5 | |||
6 | obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += parallel-display.o | ||
7 | obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o | ||
8 | obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o | ||
9 | |||
10 | imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o | ||
11 | obj-$(CONFIG_DRM_IMX_IPUV3) += imx-ipuv3-crtc.o | ||
12 | obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o | ||
diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c new file mode 100644 index 000000000000..e48b2211d2d6 --- /dev/null +++ b/drivers/gpu/drm/imx/imx-drm-core.c | |||
@@ -0,0 +1,704 @@ | |||
1 | /* | ||
2 | * Freescale i.MX drm driver | ||
3 | * | ||
4 | * Copyright (C) 2011 Sascha Hauer, Pengutronix | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version 2 | ||
9 | * of the License, or (at your option) any later version. | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | #include <linux/component.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/fb.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/of_graph.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <drm/drmP.h> | ||
23 | #include <drm/drm_fb_helper.h> | ||
24 | #include <drm/drm_crtc_helper.h> | ||
25 | #include <drm/drm_gem_cma_helper.h> | ||
26 | #include <drm/drm_fb_cma_helper.h> | ||
27 | #include <drm/drm_plane_helper.h> | ||
28 | |||
29 | #include "imx-drm.h" | ||
30 | |||
31 | #define MAX_CRTC 4 | ||
32 | |||
33 | struct imx_drm_crtc; | ||
34 | |||
35 | struct imx_drm_component { | ||
36 | struct device_node *of_node; | ||
37 | struct list_head list; | ||
38 | }; | ||
39 | |||
40 | struct imx_drm_device { | ||
41 | struct drm_device *drm; | ||
42 | struct imx_drm_crtc *crtc[MAX_CRTC]; | ||
43 | int pipes; | ||
44 | struct drm_fbdev_cma *fbhelper; | ||
45 | }; | ||
46 | |||
47 | struct imx_drm_crtc { | ||
48 | struct drm_crtc *crtc; | ||
49 | int pipe; | ||
50 | struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; | ||
51 | struct device_node *port; | ||
52 | }; | ||
53 | |||
54 | static int legacyfb_depth = 16; | ||
55 | module_param(legacyfb_depth, int, 0444); | ||
56 | |||
57 | int imx_drm_crtc_id(struct imx_drm_crtc *crtc) | ||
58 | { | ||
59 | return crtc->pipe; | ||
60 | } | ||
61 | EXPORT_SYMBOL_GPL(imx_drm_crtc_id); | ||
62 | |||
63 | static void imx_drm_driver_lastclose(struct drm_device *drm) | ||
64 | { | ||
65 | #if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) | ||
66 | struct imx_drm_device *imxdrm = drm->dev_private; | ||
67 | |||
68 | if (imxdrm->fbhelper) | ||
69 | drm_fbdev_cma_restore_mode(imxdrm->fbhelper); | ||
70 | #endif | ||
71 | } | ||
72 | |||
73 | static int imx_drm_driver_unload(struct drm_device *drm) | ||
74 | { | ||
75 | #if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) | ||
76 | struct imx_drm_device *imxdrm = drm->dev_private; | ||
77 | #endif | ||
78 | |||
79 | drm_kms_helper_poll_fini(drm); | ||
80 | |||
81 | #if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) | ||
82 | if (imxdrm->fbhelper) | ||
83 | drm_fbdev_cma_fini(imxdrm->fbhelper); | ||
84 | #endif | ||
85 | |||
86 | component_unbind_all(drm->dev, drm); | ||
87 | |||
88 | drm_vblank_cleanup(drm); | ||
89 | drm_mode_config_cleanup(drm); | ||
90 | |||
91 | platform_set_drvdata(drm->platformdev, NULL); | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static struct imx_drm_crtc *imx_drm_find_crtc(struct drm_crtc *crtc) | ||
97 | { | ||
98 | struct imx_drm_device *imxdrm = crtc->dev->dev_private; | ||
99 | unsigned i; | ||
100 | |||
101 | for (i = 0; i < MAX_CRTC; i++) | ||
102 | if (imxdrm->crtc[i] && imxdrm->crtc[i]->crtc == crtc) | ||
103 | return imxdrm->crtc[i]; | ||
104 | |||
105 | return NULL; | ||
106 | } | ||
107 | |||
108 | int imx_drm_panel_format_pins(struct drm_encoder *encoder, | ||
109 | u32 interface_pix_fmt, int hsync_pin, int vsync_pin) | ||
110 | { | ||
111 | struct imx_drm_crtc_helper_funcs *helper; | ||
112 | struct imx_drm_crtc *imx_crtc; | ||
113 | |||
114 | imx_crtc = imx_drm_find_crtc(encoder->crtc); | ||
115 | if (!imx_crtc) | ||
116 | return -EINVAL; | ||
117 | |||
118 | helper = &imx_crtc->imx_drm_helper_funcs; | ||
119 | if (helper->set_interface_pix_fmt) | ||
120 | return helper->set_interface_pix_fmt(encoder->crtc, | ||
121 | encoder->encoder_type, interface_pix_fmt, | ||
122 | hsync_pin, vsync_pin); | ||
123 | return 0; | ||
124 | } | ||
125 | EXPORT_SYMBOL_GPL(imx_drm_panel_format_pins); | ||
126 | |||
127 | int imx_drm_panel_format(struct drm_encoder *encoder, u32 interface_pix_fmt) | ||
128 | { | ||
129 | return imx_drm_panel_format_pins(encoder, interface_pix_fmt, 2, 3); | ||
130 | } | ||
131 | EXPORT_SYMBOL_GPL(imx_drm_panel_format); | ||
132 | |||
133 | int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc) | ||
134 | { | ||
135 | return drm_vblank_get(imx_drm_crtc->crtc->dev, imx_drm_crtc->pipe); | ||
136 | } | ||
137 | EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_get); | ||
138 | |||
139 | void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc) | ||
140 | { | ||
141 | drm_vblank_put(imx_drm_crtc->crtc->dev, imx_drm_crtc->pipe); | ||
142 | } | ||
143 | EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_put); | ||
144 | |||
145 | void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc) | ||
146 | { | ||
147 | drm_handle_vblank(imx_drm_crtc->crtc->dev, imx_drm_crtc->pipe); | ||
148 | } | ||
149 | EXPORT_SYMBOL_GPL(imx_drm_handle_vblank); | ||
150 | |||
151 | static int imx_drm_enable_vblank(struct drm_device *drm, int crtc) | ||
152 | { | ||
153 | struct imx_drm_device *imxdrm = drm->dev_private; | ||
154 | struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[crtc]; | ||
155 | int ret; | ||
156 | |||
157 | if (!imx_drm_crtc) | ||
158 | return -EINVAL; | ||
159 | |||
160 | if (!imx_drm_crtc->imx_drm_helper_funcs.enable_vblank) | ||
161 | return -ENOSYS; | ||
162 | |||
163 | ret = imx_drm_crtc->imx_drm_helper_funcs.enable_vblank( | ||
164 | imx_drm_crtc->crtc); | ||
165 | |||
166 | return ret; | ||
167 | } | ||
168 | |||
169 | static void imx_drm_disable_vblank(struct drm_device *drm, int crtc) | ||
170 | { | ||
171 | struct imx_drm_device *imxdrm = drm->dev_private; | ||
172 | struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[crtc]; | ||
173 | |||
174 | if (!imx_drm_crtc) | ||
175 | return; | ||
176 | |||
177 | if (!imx_drm_crtc->imx_drm_helper_funcs.disable_vblank) | ||
178 | return; | ||
179 | |||
180 | imx_drm_crtc->imx_drm_helper_funcs.disable_vblank(imx_drm_crtc->crtc); | ||
181 | } | ||
182 | |||
183 | static void imx_drm_driver_preclose(struct drm_device *drm, | ||
184 | struct drm_file *file) | ||
185 | { | ||
186 | int i; | ||
187 | |||
188 | if (!file->is_master) | ||
189 | return; | ||
190 | |||
191 | for (i = 0; i < MAX_CRTC; i++) | ||
192 | imx_drm_disable_vblank(drm, i); | ||
193 | } | ||
194 | |||
195 | static const struct file_operations imx_drm_driver_fops = { | ||
196 | .owner = THIS_MODULE, | ||
197 | .open = drm_open, | ||
198 | .release = drm_release, | ||
199 | .unlocked_ioctl = drm_ioctl, | ||
200 | .mmap = drm_gem_cma_mmap, | ||
201 | .poll = drm_poll, | ||
202 | .read = drm_read, | ||
203 | .llseek = noop_llseek, | ||
204 | }; | ||
205 | |||
206 | void imx_drm_connector_destroy(struct drm_connector *connector) | ||
207 | { | ||
208 | drm_connector_unregister(connector); | ||
209 | drm_connector_cleanup(connector); | ||
210 | } | ||
211 | EXPORT_SYMBOL_GPL(imx_drm_connector_destroy); | ||
212 | |||
213 | void imx_drm_encoder_destroy(struct drm_encoder *encoder) | ||
214 | { | ||
215 | drm_encoder_cleanup(encoder); | ||
216 | } | ||
217 | EXPORT_SYMBOL_GPL(imx_drm_encoder_destroy); | ||
218 | |||
219 | static void imx_drm_output_poll_changed(struct drm_device *drm) | ||
220 | { | ||
221 | #if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) | ||
222 | struct imx_drm_device *imxdrm = drm->dev_private; | ||
223 | |||
224 | drm_fbdev_cma_hotplug_event(imxdrm->fbhelper); | ||
225 | #endif | ||
226 | } | ||
227 | |||
228 | static struct drm_mode_config_funcs imx_drm_mode_config_funcs = { | ||
229 | .fb_create = drm_fb_cma_create, | ||
230 | .output_poll_changed = imx_drm_output_poll_changed, | ||
231 | }; | ||
232 | |||
233 | /* | ||
234 | * Main DRM initialisation. This binds, initialises and registers | ||
235 | * with DRM the subcomponents of the driver. | ||
236 | */ | ||
237 | static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) | ||
238 | { | ||
239 | struct imx_drm_device *imxdrm; | ||
240 | struct drm_connector *connector; | ||
241 | int ret; | ||
242 | |||
243 | imxdrm = devm_kzalloc(drm->dev, sizeof(*imxdrm), GFP_KERNEL); | ||
244 | if (!imxdrm) | ||
245 | return -ENOMEM; | ||
246 | |||
247 | imxdrm->drm = drm; | ||
248 | |||
249 | drm->dev_private = imxdrm; | ||
250 | |||
251 | /* | ||
252 | * enable drm irq mode. | ||
253 | * - with irq_enabled = true, we can use the vblank feature. | ||
254 | * | ||
255 | * P.S. note that we wouldn't use drm irq handler but | ||
256 | * just specific driver own one instead because | ||
257 | * drm framework supports only one irq handler and | ||
258 | * drivers can well take care of their interrupts | ||
259 | */ | ||
260 | drm->irq_enabled = true; | ||
261 | |||
262 | /* | ||
263 | * set max width and height as default value(4096x4096). | ||
264 | * this value would be used to check framebuffer size limitation | ||
265 | * at drm_mode_addfb(). | ||
266 | */ | ||
267 | drm->mode_config.min_width = 64; | ||
268 | drm->mode_config.min_height = 64; | ||
269 | drm->mode_config.max_width = 4096; | ||
270 | drm->mode_config.max_height = 4096; | ||
271 | drm->mode_config.funcs = &imx_drm_mode_config_funcs; | ||
272 | |||
273 | drm_mode_config_init(drm); | ||
274 | |||
275 | ret = drm_vblank_init(drm, MAX_CRTC); | ||
276 | if (ret) | ||
277 | goto err_kms; | ||
278 | |||
279 | /* | ||
280 | * with vblank_disable_allowed = true, vblank interrupt will be | ||
281 | * disabled by drm timer once a current process gives up ownership | ||
282 | * of vblank event. (after drm_vblank_put function is called) | ||
283 | */ | ||
284 | drm->vblank_disable_allowed = true; | ||
285 | |||
286 | platform_set_drvdata(drm->platformdev, drm); | ||
287 | |||
288 | /* Now try and bind all our sub-components */ | ||
289 | ret = component_bind_all(drm->dev, drm); | ||
290 | if (ret) | ||
291 | goto err_vblank; | ||
292 | |||
293 | /* | ||
294 | * All components are now added, we can publish the connector sysfs | ||
295 | * entries to userspace. This will generate hotplug events and so | ||
296 | * userspace will expect to be able to access DRM at this point. | ||
297 | */ | ||
298 | list_for_each_entry(connector, &drm->mode_config.connector_list, head) { | ||
299 | ret = drm_connector_register(connector); | ||
300 | if (ret) { | ||
301 | dev_err(drm->dev, | ||
302 | "[CONNECTOR:%d:%s] drm_connector_register failed: %d\n", | ||
303 | connector->base.id, | ||
304 | connector->name, ret); | ||
305 | goto err_unbind; | ||
306 | } | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * All components are now initialised, so setup the fb helper. | ||
311 | * The fb helper takes copies of key hardware information, so the | ||
312 | * crtcs/connectors/encoders must not change after this point. | ||
313 | */ | ||
314 | #if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) | ||
315 | if (legacyfb_depth != 16 && legacyfb_depth != 32) { | ||
316 | dev_warn(drm->dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n"); | ||
317 | legacyfb_depth = 16; | ||
318 | } | ||
319 | imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, | ||
320 | drm->mode_config.num_crtc, MAX_CRTC); | ||
321 | if (IS_ERR(imxdrm->fbhelper)) { | ||
322 | ret = PTR_ERR(imxdrm->fbhelper); | ||
323 | imxdrm->fbhelper = NULL; | ||
324 | goto err_unbind; | ||
325 | } | ||
326 | #endif | ||
327 | |||
328 | drm_kms_helper_poll_init(drm); | ||
329 | |||
330 | return 0; | ||
331 | |||
332 | err_unbind: | ||
333 | component_unbind_all(drm->dev, drm); | ||
334 | err_vblank: | ||
335 | drm_vblank_cleanup(drm); | ||
336 | err_kms: | ||
337 | drm_mode_config_cleanup(drm); | ||
338 | |||
339 | return ret; | ||
340 | } | ||
341 | |||
342 | /* | ||
343 | * imx_drm_add_crtc - add a new crtc | ||
344 | */ | ||
345 | int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, | ||
346 | struct imx_drm_crtc **new_crtc, | ||
347 | const struct imx_drm_crtc_helper_funcs *imx_drm_helper_funcs, | ||
348 | struct device_node *port) | ||
349 | { | ||
350 | struct imx_drm_device *imxdrm = drm->dev_private; | ||
351 | struct imx_drm_crtc *imx_drm_crtc; | ||
352 | int ret; | ||
353 | |||
354 | /* | ||
355 | * The vblank arrays are dimensioned by MAX_CRTC - we can't | ||
356 | * pass IDs greater than this to those functions. | ||
357 | */ | ||
358 | if (imxdrm->pipes >= MAX_CRTC) | ||
359 | return -EINVAL; | ||
360 | |||
361 | if (imxdrm->drm->open_count) | ||
362 | return -EBUSY; | ||
363 | |||
364 | imx_drm_crtc = kzalloc(sizeof(*imx_drm_crtc), GFP_KERNEL); | ||
365 | if (!imx_drm_crtc) | ||
366 | return -ENOMEM; | ||
367 | |||
368 | imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs; | ||
369 | imx_drm_crtc->pipe = imxdrm->pipes++; | ||
370 | imx_drm_crtc->port = port; | ||
371 | imx_drm_crtc->crtc = crtc; | ||
372 | |||
373 | imxdrm->crtc[imx_drm_crtc->pipe] = imx_drm_crtc; | ||
374 | |||
375 | *new_crtc = imx_drm_crtc; | ||
376 | |||
377 | ret = drm_mode_crtc_set_gamma_size(imx_drm_crtc->crtc, 256); | ||
378 | if (ret) | ||
379 | goto err_register; | ||
380 | |||
381 | drm_crtc_helper_add(crtc, | ||
382 | imx_drm_crtc->imx_drm_helper_funcs.crtc_helper_funcs); | ||
383 | |||
384 | drm_crtc_init(drm, crtc, | ||
385 | imx_drm_crtc->imx_drm_helper_funcs.crtc_funcs); | ||
386 | |||
387 | return 0; | ||
388 | |||
389 | err_register: | ||
390 | imxdrm->crtc[imx_drm_crtc->pipe] = NULL; | ||
391 | kfree(imx_drm_crtc); | ||
392 | return ret; | ||
393 | } | ||
394 | EXPORT_SYMBOL_GPL(imx_drm_add_crtc); | ||
395 | |||
396 | /* | ||
397 | * imx_drm_remove_crtc - remove a crtc | ||
398 | */ | ||
399 | int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc) | ||
400 | { | ||
401 | struct imx_drm_device *imxdrm = imx_drm_crtc->crtc->dev->dev_private; | ||
402 | |||
403 | drm_crtc_cleanup(imx_drm_crtc->crtc); | ||
404 | |||
405 | imxdrm->crtc[imx_drm_crtc->pipe] = NULL; | ||
406 | |||
407 | kfree(imx_drm_crtc); | ||
408 | |||
409 | return 0; | ||
410 | } | ||
411 | EXPORT_SYMBOL_GPL(imx_drm_remove_crtc); | ||
412 | |||
413 | /* | ||
414 | * Find the DRM CRTC possible mask for the connected endpoint. | ||
415 | * | ||
416 | * The encoder possible masks are defined by their position in the | ||
417 | * mode_config crtc_list. This means that CRTCs must not be added | ||
418 | * or removed once the DRM device has been fully initialised. | ||
419 | */ | ||
420 | static uint32_t imx_drm_find_crtc_mask(struct imx_drm_device *imxdrm, | ||
421 | struct device_node *endpoint) | ||
422 | { | ||
423 | struct device_node *port; | ||
424 | unsigned i; | ||
425 | |||
426 | port = of_graph_get_remote_port(endpoint); | ||
427 | if (!port) | ||
428 | return 0; | ||
429 | of_node_put(port); | ||
430 | |||
431 | for (i = 0; i < MAX_CRTC; i++) { | ||
432 | struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[i]; | ||
433 | |||
434 | if (imx_drm_crtc && imx_drm_crtc->port == port) | ||
435 | return drm_crtc_mask(imx_drm_crtc->crtc); | ||
436 | } | ||
437 | |||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | static struct device_node *imx_drm_of_get_next_endpoint( | ||
442 | const struct device_node *parent, struct device_node *prev) | ||
443 | { | ||
444 | struct device_node *node = of_graph_get_next_endpoint(parent, prev); | ||
445 | |||
446 | of_node_put(prev); | ||
447 | return node; | ||
448 | } | ||
449 | |||
450 | int imx_drm_encoder_parse_of(struct drm_device *drm, | ||
451 | struct drm_encoder *encoder, struct device_node *np) | ||
452 | { | ||
453 | struct imx_drm_device *imxdrm = drm->dev_private; | ||
454 | struct device_node *ep = NULL; | ||
455 | uint32_t crtc_mask = 0; | ||
456 | int i; | ||
457 | |||
458 | for (i = 0; ; i++) { | ||
459 | u32 mask; | ||
460 | |||
461 | ep = imx_drm_of_get_next_endpoint(np, ep); | ||
462 | if (!ep) | ||
463 | break; | ||
464 | |||
465 | mask = imx_drm_find_crtc_mask(imxdrm, ep); | ||
466 | |||
467 | /* | ||
468 | * If we failed to find the CRTC(s) which this encoder is | ||
469 | * supposed to be connected to, it's because the CRTC has | ||
470 | * not been registered yet. Defer probing, and hope that | ||
471 | * the required CRTC is added later. | ||
472 | */ | ||
473 | if (mask == 0) | ||
474 | return -EPROBE_DEFER; | ||
475 | |||
476 | crtc_mask |= mask; | ||
477 | } | ||
478 | |||
479 | of_node_put(ep); | ||
480 | if (i == 0) | ||
481 | return -ENOENT; | ||
482 | |||
483 | encoder->possible_crtcs = crtc_mask; | ||
484 | |||
485 | /* FIXME: this is the mask of outputs which can clone this output. */ | ||
486 | encoder->possible_clones = ~0; | ||
487 | |||
488 | return 0; | ||
489 | } | ||
490 | EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of); | ||
491 | |||
492 | /* | ||
493 | * @node: device tree node containing encoder input ports | ||
494 | * @encoder: drm_encoder | ||
495 | */ | ||
496 | int imx_drm_encoder_get_mux_id(struct device_node *node, | ||
497 | struct drm_encoder *encoder) | ||
498 | { | ||
499 | struct imx_drm_crtc *imx_crtc = imx_drm_find_crtc(encoder->crtc); | ||
500 | struct device_node *ep = NULL; | ||
501 | struct of_endpoint endpoint; | ||
502 | struct device_node *port; | ||
503 | int ret; | ||
504 | |||
505 | if (!node || !imx_crtc) | ||
506 | return -EINVAL; | ||
507 | |||
508 | do { | ||
509 | ep = imx_drm_of_get_next_endpoint(node, ep); | ||
510 | if (!ep) | ||
511 | break; | ||
512 | |||
513 | port = of_graph_get_remote_port(ep); | ||
514 | of_node_put(port); | ||
515 | if (port == imx_crtc->port) { | ||
516 | ret = of_graph_parse_endpoint(ep, &endpoint); | ||
517 | return ret ? ret : endpoint.port; | ||
518 | } | ||
519 | } while (ep); | ||
520 | |||
521 | return -EINVAL; | ||
522 | } | ||
523 | EXPORT_SYMBOL_GPL(imx_drm_encoder_get_mux_id); | ||
524 | |||
525 | static const struct drm_ioctl_desc imx_drm_ioctls[] = { | ||
526 | /* none so far */ | ||
527 | }; | ||
528 | |||
529 | static struct drm_driver imx_drm_driver = { | ||
530 | .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, | ||
531 | .load = imx_drm_driver_load, | ||
532 | .unload = imx_drm_driver_unload, | ||
533 | .lastclose = imx_drm_driver_lastclose, | ||
534 | .preclose = imx_drm_driver_preclose, | ||
535 | .set_busid = drm_platform_set_busid, | ||
536 | .gem_free_object = drm_gem_cma_free_object, | ||
537 | .gem_vm_ops = &drm_gem_cma_vm_ops, | ||
538 | .dumb_create = drm_gem_cma_dumb_create, | ||
539 | .dumb_map_offset = drm_gem_cma_dumb_map_offset, | ||
540 | .dumb_destroy = drm_gem_dumb_destroy, | ||
541 | |||
542 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | ||
543 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | ||
544 | .gem_prime_import = drm_gem_prime_import, | ||
545 | .gem_prime_export = drm_gem_prime_export, | ||
546 | .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, | ||
547 | .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, | ||
548 | .gem_prime_vmap = drm_gem_cma_prime_vmap, | ||
549 | .gem_prime_vunmap = drm_gem_cma_prime_vunmap, | ||
550 | .gem_prime_mmap = drm_gem_cma_prime_mmap, | ||
551 | .get_vblank_counter = drm_vblank_count, | ||
552 | .enable_vblank = imx_drm_enable_vblank, | ||
553 | .disable_vblank = imx_drm_disable_vblank, | ||
554 | .ioctls = imx_drm_ioctls, | ||
555 | .num_ioctls = ARRAY_SIZE(imx_drm_ioctls), | ||
556 | .fops = &imx_drm_driver_fops, | ||
557 | .name = "imx-drm", | ||
558 | .desc = "i.MX DRM graphics", | ||
559 | .date = "20120507", | ||
560 | .major = 1, | ||
561 | .minor = 0, | ||
562 | .patchlevel = 0, | ||
563 | }; | ||
564 | |||
565 | static int compare_of(struct device *dev, void *data) | ||
566 | { | ||
567 | struct device_node *np = data; | ||
568 | |||
569 | /* Special case for LDB, one device for two channels */ | ||
570 | if (of_node_cmp(np->name, "lvds-channel") == 0) { | ||
571 | np = of_get_parent(np); | ||
572 | of_node_put(np); | ||
573 | } | ||
574 | |||
575 | return dev->of_node == np; | ||
576 | } | ||
577 | |||
578 | static int imx_drm_bind(struct device *dev) | ||
579 | { | ||
580 | return drm_platform_init(&imx_drm_driver, to_platform_device(dev)); | ||
581 | } | ||
582 | |||
583 | static void imx_drm_unbind(struct device *dev) | ||
584 | { | ||
585 | drm_put_dev(dev_get_drvdata(dev)); | ||
586 | } | ||
587 | |||
588 | static const struct component_master_ops imx_drm_ops = { | ||
589 | .bind = imx_drm_bind, | ||
590 | .unbind = imx_drm_unbind, | ||
591 | }; | ||
592 | |||
593 | static int imx_drm_platform_probe(struct platform_device *pdev) | ||
594 | { | ||
595 | struct device_node *ep, *port, *remote; | ||
596 | struct component_match *match = NULL; | ||
597 | int ret; | ||
598 | int i; | ||
599 | |||
600 | /* | ||
601 | * Bind the IPU display interface ports first, so that | ||
602 | * imx_drm_encoder_parse_of called from encoder .bind callbacks | ||
603 | * works as expected. | ||
604 | */ | ||
605 | for (i = 0; ; i++) { | ||
606 | port = of_parse_phandle(pdev->dev.of_node, "ports", i); | ||
607 | if (!port) | ||
608 | break; | ||
609 | |||
610 | component_match_add(&pdev->dev, &match, compare_of, port); | ||
611 | } | ||
612 | |||
613 | if (i == 0) { | ||
614 | dev_err(&pdev->dev, "missing 'ports' property\n"); | ||
615 | return -ENODEV; | ||
616 | } | ||
617 | |||
618 | /* Then bind all encoders */ | ||
619 | for (i = 0; ; i++) { | ||
620 | port = of_parse_phandle(pdev->dev.of_node, "ports", i); | ||
621 | if (!port) | ||
622 | break; | ||
623 | |||
624 | for_each_child_of_node(port, ep) { | ||
625 | remote = of_graph_get_remote_port_parent(ep); | ||
626 | if (!remote || !of_device_is_available(remote)) { | ||
627 | of_node_put(remote); | ||
628 | continue; | ||
629 | } else if (!of_device_is_available(remote->parent)) { | ||
630 | dev_warn(&pdev->dev, "parent device of %s is not available\n", | ||
631 | remote->full_name); | ||
632 | of_node_put(remote); | ||
633 | continue; | ||
634 | } | ||
635 | |||
636 | component_match_add(&pdev->dev, &match, compare_of, remote); | ||
637 | of_node_put(remote); | ||
638 | } | ||
639 | of_node_put(port); | ||
640 | } | ||
641 | |||
642 | ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); | ||
643 | if (ret) | ||
644 | return ret; | ||
645 | |||
646 | return component_master_add_with_match(&pdev->dev, &imx_drm_ops, match); | ||
647 | } | ||
648 | |||
649 | static int imx_drm_platform_remove(struct platform_device *pdev) | ||
650 | { | ||
651 | component_master_del(&pdev->dev, &imx_drm_ops); | ||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | #ifdef CONFIG_PM_SLEEP | ||
656 | static int imx_drm_suspend(struct device *dev) | ||
657 | { | ||
658 | struct drm_device *drm_dev = dev_get_drvdata(dev); | ||
659 | |||
660 | /* The drm_dev is NULL before .load hook is called */ | ||
661 | if (drm_dev == NULL) | ||
662 | return 0; | ||
663 | |||
664 | drm_kms_helper_poll_disable(drm_dev); | ||
665 | |||
666 | return 0; | ||
667 | } | ||
668 | |||
669 | static int imx_drm_resume(struct device *dev) | ||
670 | { | ||
671 | struct drm_device *drm_dev = dev_get_drvdata(dev); | ||
672 | |||
673 | if (drm_dev == NULL) | ||
674 | return 0; | ||
675 | |||
676 | drm_helper_resume_force_mode(drm_dev); | ||
677 | drm_kms_helper_poll_enable(drm_dev); | ||
678 | |||
679 | return 0; | ||
680 | } | ||
681 | #endif | ||
682 | |||
683 | static SIMPLE_DEV_PM_OPS(imx_drm_pm_ops, imx_drm_suspend, imx_drm_resume); | ||
684 | |||
685 | static const struct of_device_id imx_drm_dt_ids[] = { | ||
686 | { .compatible = "fsl,imx-display-subsystem", }, | ||
687 | { /* sentinel */ }, | ||
688 | }; | ||
689 | MODULE_DEVICE_TABLE(of, imx_drm_dt_ids); | ||
690 | |||
691 | static struct platform_driver imx_drm_pdrv = { | ||
692 | .probe = imx_drm_platform_probe, | ||
693 | .remove = imx_drm_platform_remove, | ||
694 | .driver = { | ||
695 | .name = "imx-drm", | ||
696 | .pm = &imx_drm_pm_ops, | ||
697 | .of_match_table = imx_drm_dt_ids, | ||
698 | }, | ||
699 | }; | ||
700 | module_platform_driver(imx_drm_pdrv); | ||
701 | |||
702 | MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); | ||
703 | MODULE_DESCRIPTION("i.MX drm driver core"); | ||
704 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/imx/imx-drm.h b/drivers/gpu/drm/imx/imx-drm.h new file mode 100644 index 000000000000..7453ae00c412 --- /dev/null +++ b/drivers/gpu/drm/imx/imx-drm.h | |||
@@ -0,0 +1,56 @@ | |||
1 | #ifndef _IMX_DRM_H_ | ||
2 | #define _IMX_DRM_H_ | ||
3 | |||
4 | struct device_node; | ||
5 | struct drm_crtc; | ||
6 | struct drm_connector; | ||
7 | struct drm_device; | ||
8 | struct drm_display_mode; | ||
9 | struct drm_encoder; | ||
10 | struct drm_fbdev_cma; | ||
11 | struct drm_framebuffer; | ||
12 | struct imx_drm_crtc; | ||
13 | struct platform_device; | ||
14 | |||
15 | int imx_drm_crtc_id(struct imx_drm_crtc *crtc); | ||
16 | |||
17 | struct imx_drm_crtc_helper_funcs { | ||
18 | int (*enable_vblank)(struct drm_crtc *crtc); | ||
19 | void (*disable_vblank)(struct drm_crtc *crtc); | ||
20 | int (*set_interface_pix_fmt)(struct drm_crtc *crtc, u32 encoder_type, | ||
21 | u32 pix_fmt, int hsync_pin, int vsync_pin); | ||
22 | const struct drm_crtc_helper_funcs *crtc_helper_funcs; | ||
23 | const struct drm_crtc_funcs *crtc_funcs; | ||
24 | }; | ||
25 | |||
26 | int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, | ||
27 | struct imx_drm_crtc **new_crtc, | ||
28 | const struct imx_drm_crtc_helper_funcs *imx_helper_funcs, | ||
29 | struct device_node *port); | ||
30 | int imx_drm_remove_crtc(struct imx_drm_crtc *); | ||
31 | int imx_drm_init_drm(struct platform_device *pdev, | ||
32 | int preferred_bpp); | ||
33 | int imx_drm_exit_drm(void); | ||
34 | |||
35 | int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc); | ||
36 | void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc); | ||
37 | void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc); | ||
38 | |||
39 | void imx_drm_mode_config_init(struct drm_device *drm); | ||
40 | |||
41 | struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb); | ||
42 | |||
43 | int imx_drm_panel_format_pins(struct drm_encoder *encoder, | ||
44 | u32 interface_pix_fmt, int hsync_pin, int vsync_pin); | ||
45 | int imx_drm_panel_format(struct drm_encoder *encoder, | ||
46 | u32 interface_pix_fmt); | ||
47 | |||
48 | int imx_drm_encoder_get_mux_id(struct device_node *node, | ||
49 | struct drm_encoder *encoder); | ||
50 | int imx_drm_encoder_parse_of(struct drm_device *drm, | ||
51 | struct drm_encoder *encoder, struct device_node *np); | ||
52 | |||
53 | void imx_drm_connector_destroy(struct drm_connector *connector); | ||
54 | void imx_drm_encoder_destroy(struct drm_encoder *encoder); | ||
55 | |||
56 | #endif /* _IMX_DRM_H_ */ | ||
diff --git a/drivers/gpu/drm/imx/imx-hdmi.c b/drivers/gpu/drm/imx/imx-hdmi.c new file mode 100644 index 000000000000..ddc53e039530 --- /dev/null +++ b/drivers/gpu/drm/imx/imx-hdmi.c | |||
@@ -0,0 +1,1766 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * SH-Mobile High-Definition Multimedia Interface (HDMI) driver | ||
10 | * for SLISHDMI13T and SLIPHDMIT IP cores | ||
11 | * | ||
12 | * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> | ||
13 | */ | ||
14 | |||
15 | #include <linux/component.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/err.h> | ||
19 | #include <linux/clk.h> | ||
20 | #include <linux/hdmi.h> | ||
21 | #include <linux/regmap.h> | ||
22 | #include <linux/mfd/syscon.h> | ||
23 | #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> | ||
24 | #include <linux/of_device.h> | ||
25 | |||
26 | #include <drm/drmP.h> | ||
27 | #include <drm/drm_crtc_helper.h> | ||
28 | #include <drm/drm_edid.h> | ||
29 | #include <drm/drm_encoder_slave.h> | ||
30 | #include <video/imx-ipu-v3.h> | ||
31 | |||
32 | #include "imx-hdmi.h" | ||
33 | #include "imx-drm.h" | ||
34 | |||
35 | #define HDMI_EDID_LEN 512 | ||
36 | |||
37 | #define RGB 0 | ||
38 | #define YCBCR444 1 | ||
39 | #define YCBCR422_16BITS 2 | ||
40 | #define YCBCR422_8BITS 3 | ||
41 | #define XVYCC444 4 | ||
42 | |||
43 | enum hdmi_datamap { | ||
44 | RGB444_8B = 0x01, | ||
45 | RGB444_10B = 0x03, | ||
46 | RGB444_12B = 0x05, | ||
47 | RGB444_16B = 0x07, | ||
48 | YCbCr444_8B = 0x09, | ||
49 | YCbCr444_10B = 0x0B, | ||
50 | YCbCr444_12B = 0x0D, | ||
51 | YCbCr444_16B = 0x0F, | ||
52 | YCbCr422_8B = 0x16, | ||
53 | YCbCr422_10B = 0x14, | ||
54 | YCbCr422_12B = 0x12, | ||
55 | }; | ||
56 | |||
57 | enum imx_hdmi_devtype { | ||
58 | IMX6Q_HDMI, | ||
59 | IMX6DL_HDMI, | ||
60 | }; | ||
61 | |||
62 | static const u16 csc_coeff_default[3][4] = { | ||
63 | { 0x2000, 0x0000, 0x0000, 0x0000 }, | ||
64 | { 0x0000, 0x2000, 0x0000, 0x0000 }, | ||
65 | { 0x0000, 0x0000, 0x2000, 0x0000 } | ||
66 | }; | ||
67 | |||
68 | static const u16 csc_coeff_rgb_out_eitu601[3][4] = { | ||
69 | { 0x2000, 0x6926, 0x74fd, 0x010e }, | ||
70 | { 0x2000, 0x2cdd, 0x0000, 0x7e9a }, | ||
71 | { 0x2000, 0x0000, 0x38b4, 0x7e3b } | ||
72 | }; | ||
73 | |||
74 | static const u16 csc_coeff_rgb_out_eitu709[3][4] = { | ||
75 | { 0x2000, 0x7106, 0x7a02, 0x00a7 }, | ||
76 | { 0x2000, 0x3264, 0x0000, 0x7e6d }, | ||
77 | { 0x2000, 0x0000, 0x3b61, 0x7e25 } | ||
78 | }; | ||
79 | |||
80 | static const u16 csc_coeff_rgb_in_eitu601[3][4] = { | ||
81 | { 0x2591, 0x1322, 0x074b, 0x0000 }, | ||
82 | { 0x6535, 0x2000, 0x7acc, 0x0200 }, | ||
83 | { 0x6acd, 0x7534, 0x2000, 0x0200 } | ||
84 | }; | ||
85 | |||
86 | static const u16 csc_coeff_rgb_in_eitu709[3][4] = { | ||
87 | { 0x2dc5, 0x0d9b, 0x049e, 0x0000 }, | ||
88 | { 0x62f0, 0x2000, 0x7d11, 0x0200 }, | ||
89 | { 0x6756, 0x78ab, 0x2000, 0x0200 } | ||
90 | }; | ||
91 | |||
92 | struct hdmi_vmode { | ||
93 | bool mdvi; | ||
94 | bool mhsyncpolarity; | ||
95 | bool mvsyncpolarity; | ||
96 | bool minterlaced; | ||
97 | bool mdataenablepolarity; | ||
98 | |||
99 | unsigned int mpixelclock; | ||
100 | unsigned int mpixelrepetitioninput; | ||
101 | unsigned int mpixelrepetitionoutput; | ||
102 | }; | ||
103 | |||
104 | struct hdmi_data_info { | ||
105 | unsigned int enc_in_format; | ||
106 | unsigned int enc_out_format; | ||
107 | unsigned int enc_color_depth; | ||
108 | unsigned int colorimetry; | ||
109 | unsigned int pix_repet_factor; | ||
110 | unsigned int hdcp_enable; | ||
111 | struct hdmi_vmode video_mode; | ||
112 | }; | ||
113 | |||
114 | struct imx_hdmi { | ||
115 | struct drm_connector connector; | ||
116 | struct drm_encoder encoder; | ||
117 | |||
118 | enum imx_hdmi_devtype dev_type; | ||
119 | struct device *dev; | ||
120 | struct clk *isfr_clk; | ||
121 | struct clk *iahb_clk; | ||
122 | |||
123 | struct hdmi_data_info hdmi_data; | ||
124 | int vic; | ||
125 | |||
126 | u8 edid[HDMI_EDID_LEN]; | ||
127 | bool cable_plugin; | ||
128 | |||
129 | bool phy_enabled; | ||
130 | struct drm_display_mode previous_mode; | ||
131 | |||
132 | struct regmap *regmap; | ||
133 | struct i2c_adapter *ddc; | ||
134 | void __iomem *regs; | ||
135 | |||
136 | unsigned int sample_rate; | ||
137 | int ratio; | ||
138 | }; | ||
139 | |||
140 | static void imx_hdmi_set_ipu_di_mux(struct imx_hdmi *hdmi, int ipu_di) | ||
141 | { | ||
142 | regmap_update_bits(hdmi->regmap, IOMUXC_GPR3, | ||
143 | IMX6Q_GPR3_HDMI_MUX_CTL_MASK, | ||
144 | ipu_di << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT); | ||
145 | } | ||
146 | |||
147 | static inline void hdmi_writeb(struct imx_hdmi *hdmi, u8 val, int offset) | ||
148 | { | ||
149 | writeb(val, hdmi->regs + offset); | ||
150 | } | ||
151 | |||
152 | static inline u8 hdmi_readb(struct imx_hdmi *hdmi, int offset) | ||
153 | { | ||
154 | return readb(hdmi->regs + offset); | ||
155 | } | ||
156 | |||
157 | static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg) | ||
158 | { | ||
159 | u8 val = hdmi_readb(hdmi, reg) & ~mask; | ||
160 | |||
161 | val |= data & mask; | ||
162 | hdmi_writeb(hdmi, val, reg); | ||
163 | } | ||
164 | |||
165 | static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u8 data, unsigned int reg, | ||
166 | u8 shift, u8 mask) | ||
167 | { | ||
168 | hdmi_modb(hdmi, data << shift, mask, reg); | ||
169 | } | ||
170 | |||
171 | static void hdmi_set_clock_regenerator_n(struct imx_hdmi *hdmi, | ||
172 | unsigned int value) | ||
173 | { | ||
174 | hdmi_writeb(hdmi, value & 0xff, HDMI_AUD_N1); | ||
175 | hdmi_writeb(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2); | ||
176 | hdmi_writeb(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3); | ||
177 | |||
178 | /* nshift factor = 0 */ | ||
179 | hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3); | ||
180 | } | ||
181 | |||
182 | static void hdmi_regenerate_cts(struct imx_hdmi *hdmi, unsigned int cts) | ||
183 | { | ||
184 | /* Must be set/cleared first */ | ||
185 | hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); | ||
186 | |||
187 | hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1); | ||
188 | hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2); | ||
189 | hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) | | ||
190 | HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); | ||
191 | } | ||
192 | |||
193 | static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk, | ||
194 | unsigned int ratio) | ||
195 | { | ||
196 | unsigned int n = (128 * freq) / 1000; | ||
197 | |||
198 | switch (freq) { | ||
199 | case 32000: | ||
200 | if (pixel_clk == 25170000) | ||
201 | n = (ratio == 150) ? 9152 : 4576; | ||
202 | else if (pixel_clk == 27020000) | ||
203 | n = (ratio == 150) ? 8192 : 4096; | ||
204 | else if (pixel_clk == 74170000 || pixel_clk == 148350000) | ||
205 | n = 11648; | ||
206 | else | ||
207 | n = 4096; | ||
208 | break; | ||
209 | |||
210 | case 44100: | ||
211 | if (pixel_clk == 25170000) | ||
212 | n = 7007; | ||
213 | else if (pixel_clk == 74170000) | ||
214 | n = 17836; | ||
215 | else if (pixel_clk == 148350000) | ||
216 | n = (ratio == 150) ? 17836 : 8918; | ||
217 | else | ||
218 | n = 6272; | ||
219 | break; | ||
220 | |||
221 | case 48000: | ||
222 | if (pixel_clk == 25170000) | ||
223 | n = (ratio == 150) ? 9152 : 6864; | ||
224 | else if (pixel_clk == 27020000) | ||
225 | n = (ratio == 150) ? 8192 : 6144; | ||
226 | else if (pixel_clk == 74170000) | ||
227 | n = 11648; | ||
228 | else if (pixel_clk == 148350000) | ||
229 | n = (ratio == 150) ? 11648 : 5824; | ||
230 | else | ||
231 | n = 6144; | ||
232 | break; | ||
233 | |||
234 | case 88200: | ||
235 | n = hdmi_compute_n(44100, pixel_clk, ratio) * 2; | ||
236 | break; | ||
237 | |||
238 | case 96000: | ||
239 | n = hdmi_compute_n(48000, pixel_clk, ratio) * 2; | ||
240 | break; | ||
241 | |||
242 | case 176400: | ||
243 | n = hdmi_compute_n(44100, pixel_clk, ratio) * 4; | ||
244 | break; | ||
245 | |||
246 | case 192000: | ||
247 | n = hdmi_compute_n(48000, pixel_clk, ratio) * 4; | ||
248 | break; | ||
249 | |||
250 | default: | ||
251 | break; | ||
252 | } | ||
253 | |||
254 | return n; | ||
255 | } | ||
256 | |||
257 | static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk, | ||
258 | unsigned int ratio) | ||
259 | { | ||
260 | unsigned int cts = 0; | ||
261 | |||
262 | pr_debug("%s: freq: %d pixel_clk: %ld ratio: %d\n", __func__, freq, | ||
263 | pixel_clk, ratio); | ||
264 | |||
265 | switch (freq) { | ||
266 | case 32000: | ||
267 | if (pixel_clk == 297000000) { | ||
268 | cts = 222750; | ||
269 | break; | ||
270 | } | ||
271 | case 48000: | ||
272 | case 96000: | ||
273 | case 192000: | ||
274 | switch (pixel_clk) { | ||
275 | case 25200000: | ||
276 | case 27000000: | ||
277 | case 54000000: | ||
278 | case 74250000: | ||
279 | case 148500000: | ||
280 | cts = pixel_clk / 1000; | ||
281 | break; | ||
282 | case 297000000: | ||
283 | cts = 247500; | ||
284 | break; | ||
285 | /* | ||
286 | * All other TMDS clocks are not supported by | ||
287 | * DWC_hdmi_tx. The TMDS clocks divided or | ||
288 | * multiplied by 1,001 coefficients are not | ||
289 | * supported. | ||
290 | */ | ||
291 | default: | ||
292 | break; | ||
293 | } | ||
294 | break; | ||
295 | case 44100: | ||
296 | case 88200: | ||
297 | case 176400: | ||
298 | switch (pixel_clk) { | ||
299 | case 25200000: | ||
300 | cts = 28000; | ||
301 | break; | ||
302 | case 27000000: | ||
303 | cts = 30000; | ||
304 | break; | ||
305 | case 54000000: | ||
306 | cts = 60000; | ||
307 | break; | ||
308 | case 74250000: | ||
309 | cts = 82500; | ||
310 | break; | ||
311 | case 148500000: | ||
312 | cts = 165000; | ||
313 | break; | ||
314 | case 297000000: | ||
315 | cts = 247500; | ||
316 | break; | ||
317 | default: | ||
318 | break; | ||
319 | } | ||
320 | break; | ||
321 | default: | ||
322 | break; | ||
323 | } | ||
324 | if (ratio == 100) | ||
325 | return cts; | ||
326 | return (cts * ratio) / 100; | ||
327 | } | ||
328 | |||
329 | static void hdmi_set_clk_regenerator(struct imx_hdmi *hdmi, | ||
330 | unsigned long pixel_clk) | ||
331 | { | ||
332 | unsigned int clk_n, clk_cts; | ||
333 | |||
334 | clk_n = hdmi_compute_n(hdmi->sample_rate, pixel_clk, | ||
335 | hdmi->ratio); | ||
336 | clk_cts = hdmi_compute_cts(hdmi->sample_rate, pixel_clk, | ||
337 | hdmi->ratio); | ||
338 | |||
339 | if (!clk_cts) { | ||
340 | dev_dbg(hdmi->dev, "%s: pixel clock not supported: %lu\n", | ||
341 | __func__, pixel_clk); | ||
342 | return; | ||
343 | } | ||
344 | |||
345 | dev_dbg(hdmi->dev, "%s: samplerate=%d ratio=%d pixelclk=%lu N=%d cts=%d\n", | ||
346 | __func__, hdmi->sample_rate, hdmi->ratio, | ||
347 | pixel_clk, clk_n, clk_cts); | ||
348 | |||
349 | hdmi_set_clock_regenerator_n(hdmi, clk_n); | ||
350 | hdmi_regenerate_cts(hdmi, clk_cts); | ||
351 | } | ||
352 | |||
353 | static void hdmi_init_clk_regenerator(struct imx_hdmi *hdmi) | ||
354 | { | ||
355 | hdmi_set_clk_regenerator(hdmi, 74250000); | ||
356 | } | ||
357 | |||
358 | static void hdmi_clk_regenerator_update_pixel_clock(struct imx_hdmi *hdmi) | ||
359 | { | ||
360 | hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock); | ||
361 | } | ||
362 | |||
363 | /* | ||
364 | * this submodule is responsible for the video data synchronization. | ||
365 | * for example, for RGB 4:4:4 input, the data map is defined as | ||
366 | * pin{47~40} <==> R[7:0] | ||
367 | * pin{31~24} <==> G[7:0] | ||
368 | * pin{15~8} <==> B[7:0] | ||
369 | */ | ||
370 | static void hdmi_video_sample(struct imx_hdmi *hdmi) | ||
371 | { | ||
372 | int color_format = 0; | ||
373 | u8 val; | ||
374 | |||
375 | if (hdmi->hdmi_data.enc_in_format == RGB) { | ||
376 | if (hdmi->hdmi_data.enc_color_depth == 8) | ||
377 | color_format = 0x01; | ||
378 | else if (hdmi->hdmi_data.enc_color_depth == 10) | ||
379 | color_format = 0x03; | ||
380 | else if (hdmi->hdmi_data.enc_color_depth == 12) | ||
381 | color_format = 0x05; | ||
382 | else if (hdmi->hdmi_data.enc_color_depth == 16) | ||
383 | color_format = 0x07; | ||
384 | else | ||
385 | return; | ||
386 | } else if (hdmi->hdmi_data.enc_in_format == YCBCR444) { | ||
387 | if (hdmi->hdmi_data.enc_color_depth == 8) | ||
388 | color_format = 0x09; | ||
389 | else if (hdmi->hdmi_data.enc_color_depth == 10) | ||
390 | color_format = 0x0B; | ||
391 | else if (hdmi->hdmi_data.enc_color_depth == 12) | ||
392 | color_format = 0x0D; | ||
393 | else if (hdmi->hdmi_data.enc_color_depth == 16) | ||
394 | color_format = 0x0F; | ||
395 | else | ||
396 | return; | ||
397 | } else if (hdmi->hdmi_data.enc_in_format == YCBCR422_8BITS) { | ||
398 | if (hdmi->hdmi_data.enc_color_depth == 8) | ||
399 | color_format = 0x16; | ||
400 | else if (hdmi->hdmi_data.enc_color_depth == 10) | ||
401 | color_format = 0x14; | ||
402 | else if (hdmi->hdmi_data.enc_color_depth == 12) | ||
403 | color_format = 0x12; | ||
404 | else | ||
405 | return; | ||
406 | } | ||
407 | |||
408 | val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE | | ||
409 | ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) & | ||
410 | HDMI_TX_INVID0_VIDEO_MAPPING_MASK); | ||
411 | hdmi_writeb(hdmi, val, HDMI_TX_INVID0); | ||
412 | |||
413 | /* Enable TX stuffing: When DE is inactive, fix the output data to 0 */ | ||
414 | val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE | | ||
415 | HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE | | ||
416 | HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE; | ||
417 | hdmi_writeb(hdmi, val, HDMI_TX_INSTUFFING); | ||
418 | hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA0); | ||
419 | hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA1); | ||
420 | hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA0); | ||
421 | hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA1); | ||
422 | hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA0); | ||
423 | hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1); | ||
424 | } | ||
425 | |||
426 | static int is_color_space_conversion(struct imx_hdmi *hdmi) | ||
427 | { | ||
428 | return hdmi->hdmi_data.enc_in_format != hdmi->hdmi_data.enc_out_format; | ||
429 | } | ||
430 | |||
431 | static int is_color_space_decimation(struct imx_hdmi *hdmi) | ||
432 | { | ||
433 | if (hdmi->hdmi_data.enc_out_format != YCBCR422_8BITS) | ||
434 | return 0; | ||
435 | if (hdmi->hdmi_data.enc_in_format == RGB || | ||
436 | hdmi->hdmi_data.enc_in_format == YCBCR444) | ||
437 | return 1; | ||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | static int is_color_space_interpolation(struct imx_hdmi *hdmi) | ||
442 | { | ||
443 | if (hdmi->hdmi_data.enc_in_format != YCBCR422_8BITS) | ||
444 | return 0; | ||
445 | if (hdmi->hdmi_data.enc_out_format == RGB || | ||
446 | hdmi->hdmi_data.enc_out_format == YCBCR444) | ||
447 | return 1; | ||
448 | return 0; | ||
449 | } | ||
450 | |||
451 | static void imx_hdmi_update_csc_coeffs(struct imx_hdmi *hdmi) | ||
452 | { | ||
453 | const u16 (*csc_coeff)[3][4] = &csc_coeff_default; | ||
454 | unsigned i; | ||
455 | u32 csc_scale = 1; | ||
456 | |||
457 | if (is_color_space_conversion(hdmi)) { | ||
458 | if (hdmi->hdmi_data.enc_out_format == RGB) { | ||
459 | if (hdmi->hdmi_data.colorimetry == | ||
460 | HDMI_COLORIMETRY_ITU_601) | ||
461 | csc_coeff = &csc_coeff_rgb_out_eitu601; | ||
462 | else | ||
463 | csc_coeff = &csc_coeff_rgb_out_eitu709; | ||
464 | } else if (hdmi->hdmi_data.enc_in_format == RGB) { | ||
465 | if (hdmi->hdmi_data.colorimetry == | ||
466 | HDMI_COLORIMETRY_ITU_601) | ||
467 | csc_coeff = &csc_coeff_rgb_in_eitu601; | ||
468 | else | ||
469 | csc_coeff = &csc_coeff_rgb_in_eitu709; | ||
470 | csc_scale = 0; | ||
471 | } | ||
472 | } | ||
473 | |||
474 | /* The CSC registers are sequential, alternating MSB then LSB */ | ||
475 | for (i = 0; i < ARRAY_SIZE(csc_coeff_default[0]); i++) { | ||
476 | u16 coeff_a = (*csc_coeff)[0][i]; | ||
477 | u16 coeff_b = (*csc_coeff)[1][i]; | ||
478 | u16 coeff_c = (*csc_coeff)[2][i]; | ||
479 | |||
480 | hdmi_writeb(hdmi, coeff_a & 0xff, | ||
481 | HDMI_CSC_COEF_A1_LSB + i * 2); | ||
482 | hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2); | ||
483 | hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2); | ||
484 | hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2); | ||
485 | hdmi_writeb(hdmi, coeff_c & 0xff, | ||
486 | HDMI_CSC_COEF_C1_LSB + i * 2); | ||
487 | hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2); | ||
488 | } | ||
489 | |||
490 | hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK, | ||
491 | HDMI_CSC_SCALE); | ||
492 | } | ||
493 | |||
494 | static void hdmi_video_csc(struct imx_hdmi *hdmi) | ||
495 | { | ||
496 | int color_depth = 0; | ||
497 | int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE; | ||
498 | int decimation = 0; | ||
499 | |||
500 | /* YCC422 interpolation to 444 mode */ | ||
501 | if (is_color_space_interpolation(hdmi)) | ||
502 | interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1; | ||
503 | else if (is_color_space_decimation(hdmi)) | ||
504 | decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3; | ||
505 | |||
506 | if (hdmi->hdmi_data.enc_color_depth == 8) | ||
507 | color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP; | ||
508 | else if (hdmi->hdmi_data.enc_color_depth == 10) | ||
509 | color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP; | ||
510 | else if (hdmi->hdmi_data.enc_color_depth == 12) | ||
511 | color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP; | ||
512 | else if (hdmi->hdmi_data.enc_color_depth == 16) | ||
513 | color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP; | ||
514 | else | ||
515 | return; | ||
516 | |||
517 | /* Configure the CSC registers */ | ||
518 | hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG); | ||
519 | hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK, | ||
520 | HDMI_CSC_SCALE); | ||
521 | |||
522 | imx_hdmi_update_csc_coeffs(hdmi); | ||
523 | } | ||
524 | |||
525 | /* | ||
526 | * HDMI video packetizer is used to packetize the data. | ||
527 | * for example, if input is YCC422 mode or repeater is used, | ||
528 | * data should be repacked this module can be bypassed. | ||
529 | */ | ||
530 | static void hdmi_video_packetize(struct imx_hdmi *hdmi) | ||
531 | { | ||
532 | unsigned int color_depth = 0; | ||
533 | unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit; | ||
534 | unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP; | ||
535 | struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data; | ||
536 | u8 val, vp_conf; | ||
537 | |||
538 | if (hdmi_data->enc_out_format == RGB | ||
539 | || hdmi_data->enc_out_format == YCBCR444) { | ||
540 | if (!hdmi_data->enc_color_depth) | ||
541 | output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; | ||
542 | else if (hdmi_data->enc_color_depth == 8) { | ||
543 | color_depth = 4; | ||
544 | output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; | ||
545 | } else if (hdmi_data->enc_color_depth == 10) | ||
546 | color_depth = 5; | ||
547 | else if (hdmi_data->enc_color_depth == 12) | ||
548 | color_depth = 6; | ||
549 | else if (hdmi_data->enc_color_depth == 16) | ||
550 | color_depth = 7; | ||
551 | else | ||
552 | return; | ||
553 | } else if (hdmi_data->enc_out_format == YCBCR422_8BITS) { | ||
554 | if (!hdmi_data->enc_color_depth || | ||
555 | hdmi_data->enc_color_depth == 8) | ||
556 | remap_size = HDMI_VP_REMAP_YCC422_16bit; | ||
557 | else if (hdmi_data->enc_color_depth == 10) | ||
558 | remap_size = HDMI_VP_REMAP_YCC422_20bit; | ||
559 | else if (hdmi_data->enc_color_depth == 12) | ||
560 | remap_size = HDMI_VP_REMAP_YCC422_24bit; | ||
561 | else | ||
562 | return; | ||
563 | output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422; | ||
564 | } else | ||
565 | return; | ||
566 | |||
567 | /* set the packetizer registers */ | ||
568 | val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) & | ||
569 | HDMI_VP_PR_CD_COLOR_DEPTH_MASK) | | ||
570 | ((hdmi_data->pix_repet_factor << | ||
571 | HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) & | ||
572 | HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK); | ||
573 | hdmi_writeb(hdmi, val, HDMI_VP_PR_CD); | ||
574 | |||
575 | hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE, | ||
576 | HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF); | ||
577 | |||
578 | /* Data from pixel repeater block */ | ||
579 | if (hdmi_data->pix_repet_factor > 1) { | ||
580 | vp_conf = HDMI_VP_CONF_PR_EN_ENABLE | | ||
581 | HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER; | ||
582 | } else { /* data from packetizer block */ | ||
583 | vp_conf = HDMI_VP_CONF_PR_EN_DISABLE | | ||
584 | HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER; | ||
585 | } | ||
586 | |||
587 | hdmi_modb(hdmi, vp_conf, | ||
588 | HDMI_VP_CONF_PR_EN_MASK | | ||
589 | HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF); | ||
590 | |||
591 | hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET, | ||
592 | HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF); | ||
593 | |||
594 | hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP); | ||
595 | |||
596 | if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) { | ||
597 | vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE | | ||
598 | HDMI_VP_CONF_PP_EN_ENABLE | | ||
599 | HDMI_VP_CONF_YCC422_EN_DISABLE; | ||
600 | } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) { | ||
601 | vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE | | ||
602 | HDMI_VP_CONF_PP_EN_DISABLE | | ||
603 | HDMI_VP_CONF_YCC422_EN_ENABLE; | ||
604 | } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) { | ||
605 | vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE | | ||
606 | HDMI_VP_CONF_PP_EN_DISABLE | | ||
607 | HDMI_VP_CONF_YCC422_EN_DISABLE; | ||
608 | } else { | ||
609 | return; | ||
610 | } | ||
611 | |||
612 | hdmi_modb(hdmi, vp_conf, | ||
613 | HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK | | ||
614 | HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF); | ||
615 | |||
616 | hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE | | ||
617 | HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE, | ||
618 | HDMI_VP_STUFF_PP_STUFFING_MASK | | ||
619 | HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF); | ||
620 | |||
621 | hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK, | ||
622 | HDMI_VP_CONF); | ||
623 | } | ||
624 | |||
625 | static inline void hdmi_phy_test_clear(struct imx_hdmi *hdmi, | ||
626 | unsigned char bit) | ||
627 | { | ||
628 | hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET, | ||
629 | HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0); | ||
630 | } | ||
631 | |||
632 | static inline void hdmi_phy_test_enable(struct imx_hdmi *hdmi, | ||
633 | unsigned char bit) | ||
634 | { | ||
635 | hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET, | ||
636 | HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0); | ||
637 | } | ||
638 | |||
639 | static inline void hdmi_phy_test_clock(struct imx_hdmi *hdmi, | ||
640 | unsigned char bit) | ||
641 | { | ||
642 | hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET, | ||
643 | HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0); | ||
644 | } | ||
645 | |||
646 | static inline void hdmi_phy_test_din(struct imx_hdmi *hdmi, | ||
647 | unsigned char bit) | ||
648 | { | ||
649 | hdmi_writeb(hdmi, bit, HDMI_PHY_TST1); | ||
650 | } | ||
651 | |||
652 | static inline void hdmi_phy_test_dout(struct imx_hdmi *hdmi, | ||
653 | unsigned char bit) | ||
654 | { | ||
655 | hdmi_writeb(hdmi, bit, HDMI_PHY_TST2); | ||
656 | } | ||
657 | |||
658 | static bool hdmi_phy_wait_i2c_done(struct imx_hdmi *hdmi, int msec) | ||
659 | { | ||
660 | while ((hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) { | ||
661 | if (msec-- == 0) | ||
662 | return false; | ||
663 | udelay(1000); | ||
664 | } | ||
665 | return true; | ||
666 | } | ||
667 | |||
668 | static void __hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data, | ||
669 | unsigned char addr) | ||
670 | { | ||
671 | hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0); | ||
672 | hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR); | ||
673 | hdmi_writeb(hdmi, (unsigned char)(data >> 8), | ||
674 | HDMI_PHY_I2CM_DATAO_1_ADDR); | ||
675 | hdmi_writeb(hdmi, (unsigned char)(data >> 0), | ||
676 | HDMI_PHY_I2CM_DATAO_0_ADDR); | ||
677 | hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE, | ||
678 | HDMI_PHY_I2CM_OPERATION_ADDR); | ||
679 | hdmi_phy_wait_i2c_done(hdmi, 1000); | ||
680 | } | ||
681 | |||
682 | static int hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data, | ||
683 | unsigned char addr) | ||
684 | { | ||
685 | __hdmi_phy_i2c_write(hdmi, data, addr); | ||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | static void imx_hdmi_phy_enable_power(struct imx_hdmi *hdmi, u8 enable) | ||
690 | { | ||
691 | hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, | ||
692 | HDMI_PHY_CONF0_PDZ_OFFSET, | ||
693 | HDMI_PHY_CONF0_PDZ_MASK); | ||
694 | } | ||
695 | |||
696 | static void imx_hdmi_phy_enable_tmds(struct imx_hdmi *hdmi, u8 enable) | ||
697 | { | ||
698 | hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, | ||
699 | HDMI_PHY_CONF0_ENTMDS_OFFSET, | ||
700 | HDMI_PHY_CONF0_ENTMDS_MASK); | ||
701 | } | ||
702 | |||
703 | static void imx_hdmi_phy_gen2_pddq(struct imx_hdmi *hdmi, u8 enable) | ||
704 | { | ||
705 | hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, | ||
706 | HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET, | ||
707 | HDMI_PHY_CONF0_GEN2_PDDQ_MASK); | ||
708 | } | ||
709 | |||
710 | static void imx_hdmi_phy_gen2_txpwron(struct imx_hdmi *hdmi, u8 enable) | ||
711 | { | ||
712 | hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, | ||
713 | HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET, | ||
714 | HDMI_PHY_CONF0_GEN2_TXPWRON_MASK); | ||
715 | } | ||
716 | |||
717 | static void imx_hdmi_phy_sel_data_en_pol(struct imx_hdmi *hdmi, u8 enable) | ||
718 | { | ||
719 | hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, | ||
720 | HDMI_PHY_CONF0_SELDATAENPOL_OFFSET, | ||
721 | HDMI_PHY_CONF0_SELDATAENPOL_MASK); | ||
722 | } | ||
723 | |||
724 | static void imx_hdmi_phy_sel_interface_control(struct imx_hdmi *hdmi, u8 enable) | ||
725 | { | ||
726 | hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, | ||
727 | HDMI_PHY_CONF0_SELDIPIF_OFFSET, | ||
728 | HDMI_PHY_CONF0_SELDIPIF_MASK); | ||
729 | } | ||
730 | |||
731 | enum { | ||
732 | RES_8, | ||
733 | RES_10, | ||
734 | RES_12, | ||
735 | RES_MAX, | ||
736 | }; | ||
737 | |||
738 | struct mpll_config { | ||
739 | unsigned long mpixelclock; | ||
740 | struct { | ||
741 | u16 cpce; | ||
742 | u16 gmp; | ||
743 | } res[RES_MAX]; | ||
744 | }; | ||
745 | |||
746 | static const struct mpll_config mpll_config[] = { | ||
747 | { | ||
748 | 45250000, { | ||
749 | { 0x01e0, 0x0000 }, | ||
750 | { 0x21e1, 0x0000 }, | ||
751 | { 0x41e2, 0x0000 } | ||
752 | }, | ||
753 | }, { | ||
754 | 92500000, { | ||
755 | { 0x0140, 0x0005 }, | ||
756 | { 0x2141, 0x0005 }, | ||
757 | { 0x4142, 0x0005 }, | ||
758 | }, | ||
759 | }, { | ||
760 | 148500000, { | ||
761 | { 0x00a0, 0x000a }, | ||
762 | { 0x20a1, 0x000a }, | ||
763 | { 0x40a2, 0x000a }, | ||
764 | }, | ||
765 | }, { | ||
766 | ~0UL, { | ||
767 | { 0x00a0, 0x000a }, | ||
768 | { 0x2001, 0x000f }, | ||
769 | { 0x4002, 0x000f }, | ||
770 | }, | ||
771 | } | ||
772 | }; | ||
773 | |||
774 | struct curr_ctrl { | ||
775 | unsigned long mpixelclock; | ||
776 | u16 curr[RES_MAX]; | ||
777 | }; | ||
778 | |||
779 | static const struct curr_ctrl curr_ctrl[] = { | ||
780 | /* pixelclk bpp8 bpp10 bpp12 */ | ||
781 | { | ||
782 | 54000000, { 0x091c, 0x091c, 0x06dc }, | ||
783 | }, { | ||
784 | 58400000, { 0x091c, 0x06dc, 0x06dc }, | ||
785 | }, { | ||
786 | 72000000, { 0x06dc, 0x06dc, 0x091c }, | ||
787 | }, { | ||
788 | 74250000, { 0x06dc, 0x0b5c, 0x091c }, | ||
789 | }, { | ||
790 | 118800000, { 0x091c, 0x091c, 0x06dc }, | ||
791 | }, { | ||
792 | 216000000, { 0x06dc, 0x0b5c, 0x091c }, | ||
793 | } | ||
794 | }; | ||
795 | |||
796 | static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep, | ||
797 | unsigned char res, int cscon) | ||
798 | { | ||
799 | unsigned res_idx, i; | ||
800 | u8 val, msec; | ||
801 | |||
802 | if (prep) | ||
803 | return -EINVAL; | ||
804 | |||
805 | switch (res) { | ||
806 | case 0: /* color resolution 0 is 8 bit colour depth */ | ||
807 | case 8: | ||
808 | res_idx = RES_8; | ||
809 | break; | ||
810 | case 10: | ||
811 | res_idx = RES_10; | ||
812 | break; | ||
813 | case 12: | ||
814 | res_idx = RES_12; | ||
815 | break; | ||
816 | default: | ||
817 | return -EINVAL; | ||
818 | } | ||
819 | |||
820 | /* Enable csc path */ | ||
821 | if (cscon) | ||
822 | val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH; | ||
823 | else | ||
824 | val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS; | ||
825 | |||
826 | hdmi_writeb(hdmi, val, HDMI_MC_FLOWCTRL); | ||
827 | |||
828 | /* gen2 tx power off */ | ||
829 | imx_hdmi_phy_gen2_txpwron(hdmi, 0); | ||
830 | |||
831 | /* gen2 pddq */ | ||
832 | imx_hdmi_phy_gen2_pddq(hdmi, 1); | ||
833 | |||
834 | /* PHY reset */ | ||
835 | hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ); | ||
836 | hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ); | ||
837 | |||
838 | hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST); | ||
839 | |||
840 | hdmi_phy_test_clear(hdmi, 1); | ||
841 | hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2, | ||
842 | HDMI_PHY_I2CM_SLAVE_ADDR); | ||
843 | hdmi_phy_test_clear(hdmi, 0); | ||
844 | |||
845 | /* PLL/MPLL Cfg - always match on final entry */ | ||
846 | for (i = 0; i < ARRAY_SIZE(mpll_config) - 1; i++) | ||
847 | if (hdmi->hdmi_data.video_mode.mpixelclock <= | ||
848 | mpll_config[i].mpixelclock) | ||
849 | break; | ||
850 | |||
851 | hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].cpce, 0x06); | ||
852 | hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].gmp, 0x15); | ||
853 | |||
854 | for (i = 0; i < ARRAY_SIZE(curr_ctrl); i++) | ||
855 | if (hdmi->hdmi_data.video_mode.mpixelclock <= | ||
856 | curr_ctrl[i].mpixelclock) | ||
857 | break; | ||
858 | |||
859 | if (i >= ARRAY_SIZE(curr_ctrl)) { | ||
860 | dev_err(hdmi->dev, | ||
861 | "Pixel clock %d - unsupported by HDMI\n", | ||
862 | hdmi->hdmi_data.video_mode.mpixelclock); | ||
863 | return -EINVAL; | ||
864 | } | ||
865 | |||
866 | /* CURRCTRL */ | ||
867 | hdmi_phy_i2c_write(hdmi, curr_ctrl[i].curr[res_idx], 0x10); | ||
868 | |||
869 | hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */ | ||
870 | hdmi_phy_i2c_write(hdmi, 0x0006, 0x17); | ||
871 | /* RESISTANCE TERM 133Ohm Cfg */ | ||
872 | hdmi_phy_i2c_write(hdmi, 0x0005, 0x19); /* TXTERM */ | ||
873 | /* PREEMP Cgf 0.00 */ | ||
874 | hdmi_phy_i2c_write(hdmi, 0x800d, 0x09); /* CKSYMTXCTRL */ | ||
875 | /* TX/CK LVL 10 */ | ||
876 | hdmi_phy_i2c_write(hdmi, 0x01ad, 0x0E); /* VLEVCTRL */ | ||
877 | /* REMOVE CLK TERM */ | ||
878 | hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */ | ||
879 | |||
880 | imx_hdmi_phy_enable_power(hdmi, 1); | ||
881 | |||
882 | /* toggle TMDS enable */ | ||
883 | imx_hdmi_phy_enable_tmds(hdmi, 0); | ||
884 | imx_hdmi_phy_enable_tmds(hdmi, 1); | ||
885 | |||
886 | /* gen2 tx power on */ | ||
887 | imx_hdmi_phy_gen2_txpwron(hdmi, 1); | ||
888 | imx_hdmi_phy_gen2_pddq(hdmi, 0); | ||
889 | |||
890 | /*Wait for PHY PLL lock */ | ||
891 | msec = 5; | ||
892 | do { | ||
893 | val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK; | ||
894 | if (!val) | ||
895 | break; | ||
896 | |||
897 | if (msec == 0) { | ||
898 | dev_err(hdmi->dev, "PHY PLL not locked\n"); | ||
899 | return -ETIMEDOUT; | ||
900 | } | ||
901 | |||
902 | udelay(1000); | ||
903 | msec--; | ||
904 | } while (1); | ||
905 | |||
906 | return 0; | ||
907 | } | ||
908 | |||
909 | static int imx_hdmi_phy_init(struct imx_hdmi *hdmi) | ||
910 | { | ||
911 | int i, ret; | ||
912 | bool cscon = false; | ||
913 | |||
914 | /*check csc whether needed activated in HDMI mode */ | ||
915 | cscon = (is_color_space_conversion(hdmi) && | ||
916 | !hdmi->hdmi_data.video_mode.mdvi); | ||
917 | |||
918 | /* HDMI Phy spec says to do the phy initialization sequence twice */ | ||
919 | for (i = 0; i < 2; i++) { | ||
920 | imx_hdmi_phy_sel_data_en_pol(hdmi, 1); | ||
921 | imx_hdmi_phy_sel_interface_control(hdmi, 0); | ||
922 | imx_hdmi_phy_enable_tmds(hdmi, 0); | ||
923 | imx_hdmi_phy_enable_power(hdmi, 0); | ||
924 | |||
925 | /* Enable CSC */ | ||
926 | ret = hdmi_phy_configure(hdmi, 0, 8, cscon); | ||
927 | if (ret) | ||
928 | return ret; | ||
929 | } | ||
930 | |||
931 | hdmi->phy_enabled = true; | ||
932 | return 0; | ||
933 | } | ||
934 | |||
935 | static void hdmi_tx_hdcp_config(struct imx_hdmi *hdmi) | ||
936 | { | ||
937 | u8 de; | ||
938 | |||
939 | if (hdmi->hdmi_data.video_mode.mdataenablepolarity) | ||
940 | de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH; | ||
941 | else | ||
942 | de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW; | ||
943 | |||
944 | /* disable rx detect */ | ||
945 | hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE, | ||
946 | HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0); | ||
947 | |||
948 | hdmi_modb(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG); | ||
949 | |||
950 | hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE, | ||
951 | HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1); | ||
952 | } | ||
953 | |||
954 | static void hdmi_config_AVI(struct imx_hdmi *hdmi) | ||
955 | { | ||
956 | u8 val, pix_fmt, under_scan; | ||
957 | u8 act_ratio, coded_ratio, colorimetry, ext_colorimetry; | ||
958 | bool aspect_16_9; | ||
959 | |||
960 | aspect_16_9 = false; /* FIXME */ | ||
961 | |||
962 | /* AVI Data Byte 1 */ | ||
963 | if (hdmi->hdmi_data.enc_out_format == YCBCR444) | ||
964 | pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_YCBCR444; | ||
965 | else if (hdmi->hdmi_data.enc_out_format == YCBCR422_8BITS) | ||
966 | pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_YCBCR422; | ||
967 | else | ||
968 | pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_RGB; | ||
969 | |||
970 | under_scan = HDMI_FC_AVICONF0_SCAN_INFO_NODATA; | ||
971 | |||
972 | /* | ||
973 | * Active format identification data is present in the AVI InfoFrame. | ||
974 | * Under scan info, no bar data | ||
975 | */ | ||
976 | val = pix_fmt | under_scan | | ||
977 | HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT | | ||
978 | HDMI_FC_AVICONF0_BAR_DATA_NO_DATA; | ||
979 | |||
980 | hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0); | ||
981 | |||
982 | /* AVI Data Byte 2 -Set the Aspect Ratio */ | ||
983 | if (aspect_16_9) { | ||
984 | act_ratio = HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_16_9; | ||
985 | coded_ratio = HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_16_9; | ||
986 | } else { | ||
987 | act_ratio = HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_4_3; | ||
988 | coded_ratio = HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_4_3; | ||
989 | } | ||
990 | |||
991 | /* Set up colorimetry */ | ||
992 | if (hdmi->hdmi_data.enc_out_format == XVYCC444) { | ||
993 | colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_EXTENDED_INFO; | ||
994 | if (hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_601) | ||
995 | ext_colorimetry = | ||
996 | HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601; | ||
997 | else /*hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_709*/ | ||
998 | ext_colorimetry = | ||
999 | HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC709; | ||
1000 | } else if (hdmi->hdmi_data.enc_out_format != RGB) { | ||
1001 | if (hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_601) | ||
1002 | colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_SMPTE; | ||
1003 | else /*hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_709*/ | ||
1004 | colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_ITUR; | ||
1005 | ext_colorimetry = HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601; | ||
1006 | } else { /* Carries no data */ | ||
1007 | colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_NO_DATA; | ||
1008 | ext_colorimetry = HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601; | ||
1009 | } | ||
1010 | |||
1011 | val = colorimetry | coded_ratio | act_ratio; | ||
1012 | hdmi_writeb(hdmi, val, HDMI_FC_AVICONF1); | ||
1013 | |||
1014 | /* AVI Data Byte 3 */ | ||
1015 | val = HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA | ext_colorimetry | | ||
1016 | HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT | | ||
1017 | HDMI_FC_AVICONF2_SCALING_NONE; | ||
1018 | hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2); | ||
1019 | |||
1020 | /* AVI Data Byte 4 */ | ||
1021 | hdmi_writeb(hdmi, hdmi->vic, HDMI_FC_AVIVID); | ||
1022 | |||
1023 | /* AVI Data Byte 5- set up input and output pixel repetition */ | ||
1024 | val = (((hdmi->hdmi_data.video_mode.mpixelrepetitioninput + 1) << | ||
1025 | HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET) & | ||
1026 | HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK) | | ||
1027 | ((hdmi->hdmi_data.video_mode.mpixelrepetitionoutput << | ||
1028 | HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) & | ||
1029 | HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK); | ||
1030 | hdmi_writeb(hdmi, val, HDMI_FC_PRCONF); | ||
1031 | |||
1032 | /* IT Content and quantization range = don't care */ | ||
1033 | val = HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS | | ||
1034 | HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED; | ||
1035 | hdmi_writeb(hdmi, val, HDMI_FC_AVICONF3); | ||
1036 | |||
1037 | /* AVI Data Bytes 6-13 */ | ||
1038 | hdmi_writeb(hdmi, 0, HDMI_FC_AVIETB0); | ||
1039 | hdmi_writeb(hdmi, 0, HDMI_FC_AVIETB1); | ||
1040 | hdmi_writeb(hdmi, 0, HDMI_FC_AVISBB0); | ||
1041 | hdmi_writeb(hdmi, 0, HDMI_FC_AVISBB1); | ||
1042 | hdmi_writeb(hdmi, 0, HDMI_FC_AVIELB0); | ||
1043 | hdmi_writeb(hdmi, 0, HDMI_FC_AVIELB1); | ||
1044 | hdmi_writeb(hdmi, 0, HDMI_FC_AVISRB0); | ||
1045 | hdmi_writeb(hdmi, 0, HDMI_FC_AVISRB1); | ||
1046 | } | ||
1047 | |||
1048 | static void hdmi_av_composer(struct imx_hdmi *hdmi, | ||
1049 | const struct drm_display_mode *mode) | ||
1050 | { | ||
1051 | u8 inv_val; | ||
1052 | struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; | ||
1053 | int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len; | ||
1054 | |||
1055 | vmode->mhsyncpolarity = !!(mode->flags & DRM_MODE_FLAG_PHSYNC); | ||
1056 | vmode->mvsyncpolarity = !!(mode->flags & DRM_MODE_FLAG_PVSYNC); | ||
1057 | vmode->minterlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); | ||
1058 | vmode->mpixelclock = mode->clock * 1000; | ||
1059 | |||
1060 | dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock); | ||
1061 | |||
1062 | /* Set up HDMI_FC_INVIDCONF */ | ||
1063 | inv_val = (hdmi->hdmi_data.hdcp_enable ? | ||
1064 | HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE : | ||
1065 | HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE); | ||
1066 | |||
1067 | inv_val |= (vmode->mvsyncpolarity ? | ||
1068 | HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH : | ||
1069 | HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW); | ||
1070 | |||
1071 | inv_val |= (vmode->mhsyncpolarity ? | ||
1072 | HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH : | ||
1073 | HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW); | ||
1074 | |||
1075 | inv_val |= (vmode->mdataenablepolarity ? | ||
1076 | HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH : | ||
1077 | HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW); | ||
1078 | |||
1079 | if (hdmi->vic == 39) | ||
1080 | inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH; | ||
1081 | else | ||
1082 | inv_val |= (vmode->minterlaced ? | ||
1083 | HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH : | ||
1084 | HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW); | ||
1085 | |||
1086 | inv_val |= (vmode->minterlaced ? | ||
1087 | HDMI_FC_INVIDCONF_IN_I_P_INTERLACED : | ||
1088 | HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE); | ||
1089 | |||
1090 | inv_val |= (vmode->mdvi ? | ||
1091 | HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE : | ||
1092 | HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE); | ||
1093 | |||
1094 | hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF); | ||
1095 | |||
1096 | /* Set up horizontal active pixel width */ | ||
1097 | hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1); | ||
1098 | hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0); | ||
1099 | |||
1100 | /* Set up vertical active lines */ | ||
1101 | hdmi_writeb(hdmi, mode->vdisplay >> 8, HDMI_FC_INVACTV1); | ||
1102 | hdmi_writeb(hdmi, mode->vdisplay, HDMI_FC_INVACTV0); | ||
1103 | |||
1104 | /* Set up horizontal blanking pixel region width */ | ||
1105 | hblank = mode->htotal - mode->hdisplay; | ||
1106 | hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1); | ||
1107 | hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0); | ||
1108 | |||
1109 | /* Set up vertical blanking pixel region width */ | ||
1110 | vblank = mode->vtotal - mode->vdisplay; | ||
1111 | hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK); | ||
1112 | |||
1113 | /* Set up HSYNC active edge delay width (in pixel clks) */ | ||
1114 | h_de_hs = mode->hsync_start - mode->hdisplay; | ||
1115 | hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1); | ||
1116 | hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0); | ||
1117 | |||
1118 | /* Set up VSYNC active edge delay (in lines) */ | ||
1119 | v_de_vs = mode->vsync_start - mode->vdisplay; | ||
1120 | hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY); | ||
1121 | |||
1122 | /* Set up HSYNC active pulse width (in pixel clks) */ | ||
1123 | hsync_len = mode->hsync_end - mode->hsync_start; | ||
1124 | hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1); | ||
1125 | hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0); | ||
1126 | |||
1127 | /* Set up VSYNC active edge delay (in lines) */ | ||
1128 | vsync_len = mode->vsync_end - mode->vsync_start; | ||
1129 | hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH); | ||
1130 | } | ||
1131 | |||
1132 | static void imx_hdmi_phy_disable(struct imx_hdmi *hdmi) | ||
1133 | { | ||
1134 | if (!hdmi->phy_enabled) | ||
1135 | return; | ||
1136 | |||
1137 | imx_hdmi_phy_enable_tmds(hdmi, 0); | ||
1138 | imx_hdmi_phy_enable_power(hdmi, 0); | ||
1139 | |||
1140 | hdmi->phy_enabled = false; | ||
1141 | } | ||
1142 | |||
1143 | /* HDMI Initialization Step B.4 */ | ||
1144 | static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi) | ||
1145 | { | ||
1146 | u8 clkdis; | ||
1147 | |||
1148 | /* control period minimum duration */ | ||
1149 | hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR); | ||
1150 | hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR); | ||
1151 | hdmi_writeb(hdmi, 1, HDMI_FC_EXCTRLSPAC); | ||
1152 | |||
1153 | /* Set to fill TMDS data channels */ | ||
1154 | hdmi_writeb(hdmi, 0x0B, HDMI_FC_CH0PREAM); | ||
1155 | hdmi_writeb(hdmi, 0x16, HDMI_FC_CH1PREAM); | ||
1156 | hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM); | ||
1157 | |||
1158 | /* Enable pixel clock and tmds data path */ | ||
1159 | clkdis = 0x7F; | ||
1160 | clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; | ||
1161 | hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); | ||
1162 | |||
1163 | clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE; | ||
1164 | hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); | ||
1165 | |||
1166 | /* Enable csc path */ | ||
1167 | if (is_color_space_conversion(hdmi)) { | ||
1168 | clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; | ||
1169 | hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); | ||
1170 | } | ||
1171 | } | ||
1172 | |||
1173 | static void hdmi_enable_audio_clk(struct imx_hdmi *hdmi) | ||
1174 | { | ||
1175 | hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); | ||
1176 | } | ||
1177 | |||
1178 | /* Workaround to clear the overflow condition */ | ||
1179 | static void imx_hdmi_clear_overflow(struct imx_hdmi *hdmi) | ||
1180 | { | ||
1181 | int count; | ||
1182 | u8 val; | ||
1183 | |||
1184 | /* TMDS software reset */ | ||
1185 | hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ); | ||
1186 | |||
1187 | val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF); | ||
1188 | if (hdmi->dev_type == IMX6DL_HDMI) { | ||
1189 | hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF); | ||
1190 | return; | ||
1191 | } | ||
1192 | |||
1193 | for (count = 0; count < 4; count++) | ||
1194 | hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF); | ||
1195 | } | ||
1196 | |||
1197 | static void hdmi_enable_overflow_interrupts(struct imx_hdmi *hdmi) | ||
1198 | { | ||
1199 | hdmi_writeb(hdmi, 0, HDMI_FC_MASK2); | ||
1200 | hdmi_writeb(hdmi, 0, HDMI_IH_MUTE_FC_STAT2); | ||
1201 | } | ||
1202 | |||
1203 | static void hdmi_disable_overflow_interrupts(struct imx_hdmi *hdmi) | ||
1204 | { | ||
1205 | hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK, | ||
1206 | HDMI_IH_MUTE_FC_STAT2); | ||
1207 | } | ||
1208 | |||
1209 | static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode) | ||
1210 | { | ||
1211 | int ret; | ||
1212 | |||
1213 | hdmi_disable_overflow_interrupts(hdmi); | ||
1214 | |||
1215 | hdmi->vic = drm_match_cea_mode(mode); | ||
1216 | |||
1217 | if (!hdmi->vic) { | ||
1218 | dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n"); | ||
1219 | hdmi->hdmi_data.video_mode.mdvi = true; | ||
1220 | } else { | ||
1221 | dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic); | ||
1222 | hdmi->hdmi_data.video_mode.mdvi = false; | ||
1223 | } | ||
1224 | |||
1225 | if ((hdmi->vic == 6) || (hdmi->vic == 7) || | ||
1226 | (hdmi->vic == 21) || (hdmi->vic == 22) || | ||
1227 | (hdmi->vic == 2) || (hdmi->vic == 3) || | ||
1228 | (hdmi->vic == 17) || (hdmi->vic == 18)) | ||
1229 | hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601; | ||
1230 | else | ||
1231 | hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709; | ||
1232 | |||
1233 | if ((hdmi->vic == 10) || (hdmi->vic == 11) || | ||
1234 | (hdmi->vic == 12) || (hdmi->vic == 13) || | ||
1235 | (hdmi->vic == 14) || (hdmi->vic == 15) || | ||
1236 | (hdmi->vic == 25) || (hdmi->vic == 26) || | ||
1237 | (hdmi->vic == 27) || (hdmi->vic == 28) || | ||
1238 | (hdmi->vic == 29) || (hdmi->vic == 30) || | ||
1239 | (hdmi->vic == 35) || (hdmi->vic == 36) || | ||
1240 | (hdmi->vic == 37) || (hdmi->vic == 38)) | ||
1241 | hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1; | ||
1242 | else | ||
1243 | hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0; | ||
1244 | |||
1245 | hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0; | ||
1246 | |||
1247 | /* TODO: Get input format from IPU (via FB driver interface) */ | ||
1248 | hdmi->hdmi_data.enc_in_format = RGB; | ||
1249 | |||
1250 | hdmi->hdmi_data.enc_out_format = RGB; | ||
1251 | |||
1252 | hdmi->hdmi_data.enc_color_depth = 8; | ||
1253 | hdmi->hdmi_data.pix_repet_factor = 0; | ||
1254 | hdmi->hdmi_data.hdcp_enable = 0; | ||
1255 | hdmi->hdmi_data.video_mode.mdataenablepolarity = true; | ||
1256 | |||
1257 | /* HDMI Initialization Step B.1 */ | ||
1258 | hdmi_av_composer(hdmi, mode); | ||
1259 | |||
1260 | /* HDMI Initializateion Step B.2 */ | ||
1261 | ret = imx_hdmi_phy_init(hdmi); | ||
1262 | if (ret) | ||
1263 | return ret; | ||
1264 | |||
1265 | /* HDMI Initialization Step B.3 */ | ||
1266 | imx_hdmi_enable_video_path(hdmi); | ||
1267 | |||
1268 | /* not for DVI mode */ | ||
1269 | if (hdmi->hdmi_data.video_mode.mdvi) | ||
1270 | dev_dbg(hdmi->dev, "%s DVI mode\n", __func__); | ||
1271 | else { | ||
1272 | dev_dbg(hdmi->dev, "%s CEA mode\n", __func__); | ||
1273 | |||
1274 | /* HDMI Initialization Step E - Configure audio */ | ||
1275 | hdmi_clk_regenerator_update_pixel_clock(hdmi); | ||
1276 | hdmi_enable_audio_clk(hdmi); | ||
1277 | |||
1278 | /* HDMI Initialization Step F - Configure AVI InfoFrame */ | ||
1279 | hdmi_config_AVI(hdmi); | ||
1280 | } | ||
1281 | |||
1282 | hdmi_video_packetize(hdmi); | ||
1283 | hdmi_video_csc(hdmi); | ||
1284 | hdmi_video_sample(hdmi); | ||
1285 | hdmi_tx_hdcp_config(hdmi); | ||
1286 | |||
1287 | imx_hdmi_clear_overflow(hdmi); | ||
1288 | if (hdmi->cable_plugin && !hdmi->hdmi_data.video_mode.mdvi) | ||
1289 | hdmi_enable_overflow_interrupts(hdmi); | ||
1290 | |||
1291 | return 0; | ||
1292 | } | ||
1293 | |||
1294 | /* Wait until we are registered to enable interrupts */ | ||
1295 | static int imx_hdmi_fb_registered(struct imx_hdmi *hdmi) | ||
1296 | { | ||
1297 | hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, | ||
1298 | HDMI_PHY_I2CM_INT_ADDR); | ||
1299 | |||
1300 | hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL | | ||
1301 | HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL, | ||
1302 | HDMI_PHY_I2CM_CTLINT_ADDR); | ||
1303 | |||
1304 | /* enable cable hot plug irq */ | ||
1305 | hdmi_writeb(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0); | ||
1306 | |||
1307 | /* Clear Hotplug interrupts */ | ||
1308 | hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); | ||
1309 | |||
1310 | return 0; | ||
1311 | } | ||
1312 | |||
1313 | static void initialize_hdmi_ih_mutes(struct imx_hdmi *hdmi) | ||
1314 | { | ||
1315 | u8 ih_mute; | ||
1316 | |||
1317 | /* | ||
1318 | * Boot up defaults are: | ||
1319 | * HDMI_IH_MUTE = 0x03 (disabled) | ||
1320 | * HDMI_IH_MUTE_* = 0x00 (enabled) | ||
1321 | * | ||
1322 | * Disable top level interrupt bits in HDMI block | ||
1323 | */ | ||
1324 | ih_mute = hdmi_readb(hdmi, HDMI_IH_MUTE) | | ||
1325 | HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT | | ||
1326 | HDMI_IH_MUTE_MUTE_ALL_INTERRUPT; | ||
1327 | |||
1328 | hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE); | ||
1329 | |||
1330 | /* by default mask all interrupts */ | ||
1331 | hdmi_writeb(hdmi, 0xff, HDMI_VP_MASK); | ||
1332 | hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK0); | ||
1333 | hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK1); | ||
1334 | hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK2); | ||
1335 | hdmi_writeb(hdmi, 0xff, HDMI_PHY_MASK0); | ||
1336 | hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_INT_ADDR); | ||
1337 | hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_CTLINT_ADDR); | ||
1338 | hdmi_writeb(hdmi, 0xff, HDMI_AUD_INT); | ||
1339 | hdmi_writeb(hdmi, 0xff, HDMI_AUD_SPDIFINT); | ||
1340 | hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK); | ||
1341 | hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK); | ||
1342 | hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK); | ||
1343 | hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK); | ||
1344 | hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT); | ||
1345 | hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT); | ||
1346 | |||
1347 | /* Disable interrupts in the IH_MUTE_* registers */ | ||
1348 | hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0); | ||
1349 | hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1); | ||
1350 | hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2); | ||
1351 | hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0); | ||
1352 | hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0); | ||
1353 | hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0); | ||
1354 | hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0); | ||
1355 | hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0); | ||
1356 | hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0); | ||
1357 | hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0); | ||
1358 | |||
1359 | /* Enable top level interrupt bits in HDMI block */ | ||
1360 | ih_mute &= ~(HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT | | ||
1361 | HDMI_IH_MUTE_MUTE_ALL_INTERRUPT); | ||
1362 | hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE); | ||
1363 | } | ||
1364 | |||
1365 | static void imx_hdmi_poweron(struct imx_hdmi *hdmi) | ||
1366 | { | ||
1367 | imx_hdmi_setup(hdmi, &hdmi->previous_mode); | ||
1368 | } | ||
1369 | |||
1370 | static void imx_hdmi_poweroff(struct imx_hdmi *hdmi) | ||
1371 | { | ||
1372 | imx_hdmi_phy_disable(hdmi); | ||
1373 | } | ||
1374 | |||
1375 | static enum drm_connector_status imx_hdmi_connector_detect(struct drm_connector | ||
1376 | *connector, bool force) | ||
1377 | { | ||
1378 | struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi, | ||
1379 | connector); | ||
1380 | |||
1381 | return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ? | ||
1382 | connector_status_connected : connector_status_disconnected; | ||
1383 | } | ||
1384 | |||
1385 | static int imx_hdmi_connector_get_modes(struct drm_connector *connector) | ||
1386 | { | ||
1387 | struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi, | ||
1388 | connector); | ||
1389 | struct edid *edid; | ||
1390 | int ret; | ||
1391 | |||
1392 | if (!hdmi->ddc) | ||
1393 | return 0; | ||
1394 | |||
1395 | edid = drm_get_edid(connector, hdmi->ddc); | ||
1396 | if (edid) { | ||
1397 | dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n", | ||
1398 | edid->width_cm, edid->height_cm); | ||
1399 | |||
1400 | drm_mode_connector_update_edid_property(connector, edid); | ||
1401 | ret = drm_add_edid_modes(connector, edid); | ||
1402 | kfree(edid); | ||
1403 | } else { | ||
1404 | dev_dbg(hdmi->dev, "failed to get edid\n"); | ||
1405 | } | ||
1406 | |||
1407 | return 0; | ||
1408 | } | ||
1409 | |||
1410 | static struct drm_encoder *imx_hdmi_connector_best_encoder(struct drm_connector | ||
1411 | *connector) | ||
1412 | { | ||
1413 | struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi, | ||
1414 | connector); | ||
1415 | |||
1416 | return &hdmi->encoder; | ||
1417 | } | ||
1418 | |||
1419 | static void imx_hdmi_encoder_mode_set(struct drm_encoder *encoder, | ||
1420 | struct drm_display_mode *mode, | ||
1421 | struct drm_display_mode *adjusted_mode) | ||
1422 | { | ||
1423 | struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); | ||
1424 | |||
1425 | imx_hdmi_setup(hdmi, mode); | ||
1426 | |||
1427 | /* Store the display mode for plugin/DKMS poweron events */ | ||
1428 | memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); | ||
1429 | } | ||
1430 | |||
1431 | static bool imx_hdmi_encoder_mode_fixup(struct drm_encoder *encoder, | ||
1432 | const struct drm_display_mode *mode, | ||
1433 | struct drm_display_mode *adjusted_mode) | ||
1434 | { | ||
1435 | return true; | ||
1436 | } | ||
1437 | |||
1438 | static void imx_hdmi_encoder_disable(struct drm_encoder *encoder) | ||
1439 | { | ||
1440 | } | ||
1441 | |||
1442 | static void imx_hdmi_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
1443 | { | ||
1444 | struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); | ||
1445 | |||
1446 | if (mode) | ||
1447 | imx_hdmi_poweroff(hdmi); | ||
1448 | else | ||
1449 | imx_hdmi_poweron(hdmi); | ||
1450 | } | ||
1451 | |||
1452 | static void imx_hdmi_encoder_prepare(struct drm_encoder *encoder) | ||
1453 | { | ||
1454 | struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); | ||
1455 | |||
1456 | imx_hdmi_poweroff(hdmi); | ||
1457 | imx_drm_panel_format(encoder, V4L2_PIX_FMT_RGB24); | ||
1458 | } | ||
1459 | |||
1460 | static void imx_hdmi_encoder_commit(struct drm_encoder *encoder) | ||
1461 | { | ||
1462 | struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); | ||
1463 | int mux = imx_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder); | ||
1464 | |||
1465 | imx_hdmi_set_ipu_di_mux(hdmi, mux); | ||
1466 | |||
1467 | imx_hdmi_poweron(hdmi); | ||
1468 | } | ||
1469 | |||
1470 | static struct drm_encoder_funcs imx_hdmi_encoder_funcs = { | ||
1471 | .destroy = imx_drm_encoder_destroy, | ||
1472 | }; | ||
1473 | |||
1474 | static struct drm_encoder_helper_funcs imx_hdmi_encoder_helper_funcs = { | ||
1475 | .dpms = imx_hdmi_encoder_dpms, | ||
1476 | .prepare = imx_hdmi_encoder_prepare, | ||
1477 | .commit = imx_hdmi_encoder_commit, | ||
1478 | .mode_set = imx_hdmi_encoder_mode_set, | ||
1479 | .mode_fixup = imx_hdmi_encoder_mode_fixup, | ||
1480 | .disable = imx_hdmi_encoder_disable, | ||
1481 | }; | ||
1482 | |||
1483 | static struct drm_connector_funcs imx_hdmi_connector_funcs = { | ||
1484 | .dpms = drm_helper_connector_dpms, | ||
1485 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
1486 | .detect = imx_hdmi_connector_detect, | ||
1487 | .destroy = imx_drm_connector_destroy, | ||
1488 | }; | ||
1489 | |||
1490 | static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = { | ||
1491 | .get_modes = imx_hdmi_connector_get_modes, | ||
1492 | .best_encoder = imx_hdmi_connector_best_encoder, | ||
1493 | }; | ||
1494 | |||
1495 | static irqreturn_t imx_hdmi_hardirq(int irq, void *dev_id) | ||
1496 | { | ||
1497 | struct imx_hdmi *hdmi = dev_id; | ||
1498 | u8 intr_stat; | ||
1499 | |||
1500 | intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0); | ||
1501 | if (intr_stat) | ||
1502 | hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); | ||
1503 | |||
1504 | return intr_stat ? IRQ_WAKE_THREAD : IRQ_NONE; | ||
1505 | } | ||
1506 | |||
1507 | static irqreturn_t imx_hdmi_irq(int irq, void *dev_id) | ||
1508 | { | ||
1509 | struct imx_hdmi *hdmi = dev_id; | ||
1510 | u8 intr_stat; | ||
1511 | u8 phy_int_pol; | ||
1512 | |||
1513 | intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0); | ||
1514 | |||
1515 | phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0); | ||
1516 | |||
1517 | if (intr_stat & HDMI_IH_PHY_STAT0_HPD) { | ||
1518 | if (phy_int_pol & HDMI_PHY_HPD) { | ||
1519 | dev_dbg(hdmi->dev, "EVENT=plugin\n"); | ||
1520 | |||
1521 | hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0); | ||
1522 | |||
1523 | imx_hdmi_poweron(hdmi); | ||
1524 | } else { | ||
1525 | dev_dbg(hdmi->dev, "EVENT=plugout\n"); | ||
1526 | |||
1527 | hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD, | ||
1528 | HDMI_PHY_POL0); | ||
1529 | |||
1530 | imx_hdmi_poweroff(hdmi); | ||
1531 | } | ||
1532 | drm_helper_hpd_irq_event(hdmi->connector.dev); | ||
1533 | } | ||
1534 | |||
1535 | hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0); | ||
1536 | hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); | ||
1537 | |||
1538 | return IRQ_HANDLED; | ||
1539 | } | ||
1540 | |||
1541 | static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi) | ||
1542 | { | ||
1543 | int ret; | ||
1544 | |||
1545 | ret = imx_drm_encoder_parse_of(drm, &hdmi->encoder, | ||
1546 | hdmi->dev->of_node); | ||
1547 | if (ret) | ||
1548 | return ret; | ||
1549 | |||
1550 | hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; | ||
1551 | |||
1552 | drm_encoder_helper_add(&hdmi->encoder, &imx_hdmi_encoder_helper_funcs); | ||
1553 | drm_encoder_init(drm, &hdmi->encoder, &imx_hdmi_encoder_funcs, | ||
1554 | DRM_MODE_ENCODER_TMDS); | ||
1555 | |||
1556 | drm_connector_helper_add(&hdmi->connector, | ||
1557 | &imx_hdmi_connector_helper_funcs); | ||
1558 | drm_connector_init(drm, &hdmi->connector, &imx_hdmi_connector_funcs, | ||
1559 | DRM_MODE_CONNECTOR_HDMIA); | ||
1560 | |||
1561 | hdmi->connector.encoder = &hdmi->encoder; | ||
1562 | |||
1563 | drm_mode_connector_attach_encoder(&hdmi->connector, &hdmi->encoder); | ||
1564 | |||
1565 | return 0; | ||
1566 | } | ||
1567 | |||
1568 | static struct platform_device_id imx_hdmi_devtype[] = { | ||
1569 | { | ||
1570 | .name = "imx6q-hdmi", | ||
1571 | .driver_data = IMX6Q_HDMI, | ||
1572 | }, { | ||
1573 | .name = "imx6dl-hdmi", | ||
1574 | .driver_data = IMX6DL_HDMI, | ||
1575 | }, { /* sentinel */ } | ||
1576 | }; | ||
1577 | MODULE_DEVICE_TABLE(platform, imx_hdmi_devtype); | ||
1578 | |||
1579 | static const struct of_device_id imx_hdmi_dt_ids[] = { | ||
1580 | { .compatible = "fsl,imx6q-hdmi", .data = &imx_hdmi_devtype[IMX6Q_HDMI], }, | ||
1581 | { .compatible = "fsl,imx6dl-hdmi", .data = &imx_hdmi_devtype[IMX6DL_HDMI], }, | ||
1582 | { /* sentinel */ } | ||
1583 | }; | ||
1584 | MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids); | ||
1585 | |||
1586 | static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) | ||
1587 | { | ||
1588 | struct platform_device *pdev = to_platform_device(dev); | ||
1589 | const struct of_device_id *of_id = | ||
1590 | of_match_device(imx_hdmi_dt_ids, dev); | ||
1591 | struct drm_device *drm = data; | ||
1592 | struct device_node *np = dev->of_node; | ||
1593 | struct device_node *ddc_node; | ||
1594 | struct imx_hdmi *hdmi; | ||
1595 | struct resource *iores; | ||
1596 | int ret, irq; | ||
1597 | |||
1598 | hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); | ||
1599 | if (!hdmi) | ||
1600 | return -ENOMEM; | ||
1601 | |||
1602 | hdmi->dev = dev; | ||
1603 | hdmi->sample_rate = 48000; | ||
1604 | hdmi->ratio = 100; | ||
1605 | |||
1606 | if (of_id) { | ||
1607 | const struct platform_device_id *device_id = of_id->data; | ||
1608 | |||
1609 | hdmi->dev_type = device_id->driver_data; | ||
1610 | } | ||
1611 | |||
1612 | ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); | ||
1613 | if (ddc_node) { | ||
1614 | hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node); | ||
1615 | if (!hdmi->ddc) | ||
1616 | dev_dbg(hdmi->dev, "failed to read ddc node\n"); | ||
1617 | |||
1618 | of_node_put(ddc_node); | ||
1619 | } else { | ||
1620 | dev_dbg(hdmi->dev, "no ddc property found\n"); | ||
1621 | } | ||
1622 | |||
1623 | irq = platform_get_irq(pdev, 0); | ||
1624 | if (irq < 0) | ||
1625 | return irq; | ||
1626 | |||
1627 | ret = devm_request_threaded_irq(dev, irq, imx_hdmi_hardirq, | ||
1628 | imx_hdmi_irq, IRQF_SHARED, | ||
1629 | dev_name(dev), hdmi); | ||
1630 | if (ret) | ||
1631 | return ret; | ||
1632 | |||
1633 | iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1634 | hdmi->regs = devm_ioremap_resource(dev, iores); | ||
1635 | if (IS_ERR(hdmi->regs)) | ||
1636 | return PTR_ERR(hdmi->regs); | ||
1637 | |||
1638 | hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr"); | ||
1639 | if (IS_ERR(hdmi->regmap)) | ||
1640 | return PTR_ERR(hdmi->regmap); | ||
1641 | |||
1642 | hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr"); | ||
1643 | if (IS_ERR(hdmi->isfr_clk)) { | ||
1644 | ret = PTR_ERR(hdmi->isfr_clk); | ||
1645 | dev_err(hdmi->dev, | ||
1646 | "Unable to get HDMI isfr clk: %d\n", ret); | ||
1647 | return ret; | ||
1648 | } | ||
1649 | |||
1650 | ret = clk_prepare_enable(hdmi->isfr_clk); | ||
1651 | if (ret) { | ||
1652 | dev_err(hdmi->dev, | ||
1653 | "Cannot enable HDMI isfr clock: %d\n", ret); | ||
1654 | return ret; | ||
1655 | } | ||
1656 | |||
1657 | hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb"); | ||
1658 | if (IS_ERR(hdmi->iahb_clk)) { | ||
1659 | ret = PTR_ERR(hdmi->iahb_clk); | ||
1660 | dev_err(hdmi->dev, | ||
1661 | "Unable to get HDMI iahb clk: %d\n", ret); | ||
1662 | goto err_isfr; | ||
1663 | } | ||
1664 | |||
1665 | ret = clk_prepare_enable(hdmi->iahb_clk); | ||
1666 | if (ret) { | ||
1667 | dev_err(hdmi->dev, | ||
1668 | "Cannot enable HDMI iahb clock: %d\n", ret); | ||
1669 | goto err_isfr; | ||
1670 | } | ||
1671 | |||
1672 | /* Product and revision IDs */ | ||
1673 | dev_info(dev, | ||
1674 | "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n", | ||
1675 | hdmi_readb(hdmi, HDMI_DESIGN_ID), | ||
1676 | hdmi_readb(hdmi, HDMI_REVISION_ID), | ||
1677 | hdmi_readb(hdmi, HDMI_PRODUCT_ID0), | ||
1678 | hdmi_readb(hdmi, HDMI_PRODUCT_ID1)); | ||
1679 | |||
1680 | initialize_hdmi_ih_mutes(hdmi); | ||
1681 | |||
1682 | /* | ||
1683 | * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator | ||
1684 | * N and cts values before enabling phy | ||
1685 | */ | ||
1686 | hdmi_init_clk_regenerator(hdmi); | ||
1687 | |||
1688 | /* | ||
1689 | * Configure registers related to HDMI interrupt | ||
1690 | * generation before registering IRQ. | ||
1691 | */ | ||
1692 | hdmi_writeb(hdmi, HDMI_PHY_HPD, HDMI_PHY_POL0); | ||
1693 | |||
1694 | /* Clear Hotplug interrupts */ | ||
1695 | hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); | ||
1696 | |||
1697 | ret = imx_hdmi_fb_registered(hdmi); | ||
1698 | if (ret) | ||
1699 | goto err_iahb; | ||
1700 | |||
1701 | ret = imx_hdmi_register(drm, hdmi); | ||
1702 | if (ret) | ||
1703 | goto err_iahb; | ||
1704 | |||
1705 | /* Unmute interrupts */ | ||
1706 | hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); | ||
1707 | |||
1708 | dev_set_drvdata(dev, hdmi); | ||
1709 | |||
1710 | return 0; | ||
1711 | |||
1712 | err_iahb: | ||
1713 | clk_disable_unprepare(hdmi->iahb_clk); | ||
1714 | err_isfr: | ||
1715 | clk_disable_unprepare(hdmi->isfr_clk); | ||
1716 | |||
1717 | return ret; | ||
1718 | } | ||
1719 | |||
1720 | static void imx_hdmi_unbind(struct device *dev, struct device *master, | ||
1721 | void *data) | ||
1722 | { | ||
1723 | struct imx_hdmi *hdmi = dev_get_drvdata(dev); | ||
1724 | |||
1725 | /* Disable all interrupts */ | ||
1726 | hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); | ||
1727 | |||
1728 | hdmi->connector.funcs->destroy(&hdmi->connector); | ||
1729 | hdmi->encoder.funcs->destroy(&hdmi->encoder); | ||
1730 | |||
1731 | clk_disable_unprepare(hdmi->iahb_clk); | ||
1732 | clk_disable_unprepare(hdmi->isfr_clk); | ||
1733 | i2c_put_adapter(hdmi->ddc); | ||
1734 | } | ||
1735 | |||
1736 | static const struct component_ops hdmi_ops = { | ||
1737 | .bind = imx_hdmi_bind, | ||
1738 | .unbind = imx_hdmi_unbind, | ||
1739 | }; | ||
1740 | |||
1741 | static int imx_hdmi_platform_probe(struct platform_device *pdev) | ||
1742 | { | ||
1743 | return component_add(&pdev->dev, &hdmi_ops); | ||
1744 | } | ||
1745 | |||
1746 | static int imx_hdmi_platform_remove(struct platform_device *pdev) | ||
1747 | { | ||
1748 | component_del(&pdev->dev, &hdmi_ops); | ||
1749 | return 0; | ||
1750 | } | ||
1751 | |||
1752 | static struct platform_driver imx_hdmi_driver = { | ||
1753 | .probe = imx_hdmi_platform_probe, | ||
1754 | .remove = imx_hdmi_platform_remove, | ||
1755 | .driver = { | ||
1756 | .name = "imx-hdmi", | ||
1757 | .of_match_table = imx_hdmi_dt_ids, | ||
1758 | }, | ||
1759 | }; | ||
1760 | |||
1761 | module_platform_driver(imx_hdmi_driver); | ||
1762 | |||
1763 | MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); | ||
1764 | MODULE_DESCRIPTION("i.MX6 HDMI transmitter driver"); | ||
1765 | MODULE_LICENSE("GPL"); | ||
1766 | MODULE_ALIAS("platform:imx-hdmi"); | ||
diff --git a/drivers/gpu/drm/imx/imx-hdmi.h b/drivers/gpu/drm/imx/imx-hdmi.h new file mode 100644 index 000000000000..39b677689db6 --- /dev/null +++ b/drivers/gpu/drm/imx/imx-hdmi.h | |||
@@ -0,0 +1,1032 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Freescale Semiconductor, Inc. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | */ | ||
9 | |||
10 | #ifndef __IMX_HDMI_H__ | ||
11 | #define __IMX_HDMI_H__ | ||
12 | |||
13 | /* Identification Registers */ | ||
14 | #define HDMI_DESIGN_ID 0x0000 | ||
15 | #define HDMI_REVISION_ID 0x0001 | ||
16 | #define HDMI_PRODUCT_ID0 0x0002 | ||
17 | #define HDMI_PRODUCT_ID1 0x0003 | ||
18 | #define HDMI_CONFIG0_ID 0x0004 | ||
19 | #define HDMI_CONFIG1_ID 0x0005 | ||
20 | #define HDMI_CONFIG2_ID 0x0006 | ||
21 | #define HDMI_CONFIG3_ID 0x0007 | ||
22 | |||
23 | /* Interrupt Registers */ | ||
24 | #define HDMI_IH_FC_STAT0 0x0100 | ||
25 | #define HDMI_IH_FC_STAT1 0x0101 | ||
26 | #define HDMI_IH_FC_STAT2 0x0102 | ||
27 | #define HDMI_IH_AS_STAT0 0x0103 | ||
28 | #define HDMI_IH_PHY_STAT0 0x0104 | ||
29 | #define HDMI_IH_I2CM_STAT0 0x0105 | ||
30 | #define HDMI_IH_CEC_STAT0 0x0106 | ||
31 | #define HDMI_IH_VP_STAT0 0x0107 | ||
32 | #define HDMI_IH_I2CMPHY_STAT0 0x0108 | ||
33 | #define HDMI_IH_AHBDMAAUD_STAT0 0x0109 | ||
34 | |||
35 | #define HDMI_IH_MUTE_FC_STAT0 0x0180 | ||
36 | #define HDMI_IH_MUTE_FC_STAT1 0x0181 | ||
37 | #define HDMI_IH_MUTE_FC_STAT2 0x0182 | ||
38 | #define HDMI_IH_MUTE_AS_STAT0 0x0183 | ||
39 | #define HDMI_IH_MUTE_PHY_STAT0 0x0184 | ||
40 | #define HDMI_IH_MUTE_I2CM_STAT0 0x0185 | ||
41 | #define HDMI_IH_MUTE_CEC_STAT0 0x0186 | ||
42 | #define HDMI_IH_MUTE_VP_STAT0 0x0187 | ||
43 | #define HDMI_IH_MUTE_I2CMPHY_STAT0 0x0188 | ||
44 | #define HDMI_IH_MUTE_AHBDMAAUD_STAT0 0x0189 | ||
45 | #define HDMI_IH_MUTE 0x01FF | ||
46 | |||
47 | /* Video Sample Registers */ | ||
48 | #define HDMI_TX_INVID0 0x0200 | ||
49 | #define HDMI_TX_INSTUFFING 0x0201 | ||
50 | #define HDMI_TX_GYDATA0 0x0202 | ||
51 | #define HDMI_TX_GYDATA1 0x0203 | ||
52 | #define HDMI_TX_RCRDATA0 0x0204 | ||
53 | #define HDMI_TX_RCRDATA1 0x0205 | ||
54 | #define HDMI_TX_BCBDATA0 0x0206 | ||
55 | #define HDMI_TX_BCBDATA1 0x0207 | ||
56 | |||
57 | /* Video Packetizer Registers */ | ||
58 | #define HDMI_VP_STATUS 0x0800 | ||
59 | #define HDMI_VP_PR_CD 0x0801 | ||
60 | #define HDMI_VP_STUFF 0x0802 | ||
61 | #define HDMI_VP_REMAP 0x0803 | ||
62 | #define HDMI_VP_CONF 0x0804 | ||
63 | #define HDMI_VP_STAT 0x0805 | ||
64 | #define HDMI_VP_INT 0x0806 | ||
65 | #define HDMI_VP_MASK 0x0807 | ||
66 | #define HDMI_VP_POL 0x0808 | ||
67 | |||
68 | /* Frame Composer Registers */ | ||
69 | #define HDMI_FC_INVIDCONF 0x1000 | ||
70 | #define HDMI_FC_INHACTV0 0x1001 | ||
71 | #define HDMI_FC_INHACTV1 0x1002 | ||
72 | #define HDMI_FC_INHBLANK0 0x1003 | ||
73 | #define HDMI_FC_INHBLANK1 0x1004 | ||
74 | #define HDMI_FC_INVACTV0 0x1005 | ||
75 | #define HDMI_FC_INVACTV1 0x1006 | ||
76 | #define HDMI_FC_INVBLANK 0x1007 | ||
77 | #define HDMI_FC_HSYNCINDELAY0 0x1008 | ||
78 | #define HDMI_FC_HSYNCINDELAY1 0x1009 | ||
79 | #define HDMI_FC_HSYNCINWIDTH0 0x100A | ||
80 | #define HDMI_FC_HSYNCINWIDTH1 0x100B | ||
81 | #define HDMI_FC_VSYNCINDELAY 0x100C | ||
82 | #define HDMI_FC_VSYNCINWIDTH 0x100D | ||
83 | #define HDMI_FC_INFREQ0 0x100E | ||
84 | #define HDMI_FC_INFREQ1 0x100F | ||
85 | #define HDMI_FC_INFREQ2 0x1010 | ||
86 | #define HDMI_FC_CTRLDUR 0x1011 | ||
87 | #define HDMI_FC_EXCTRLDUR 0x1012 | ||
88 | #define HDMI_FC_EXCTRLSPAC 0x1013 | ||
89 | #define HDMI_FC_CH0PREAM 0x1014 | ||
90 | #define HDMI_FC_CH1PREAM 0x1015 | ||
91 | #define HDMI_FC_CH2PREAM 0x1016 | ||
92 | #define HDMI_FC_AVICONF3 0x1017 | ||
93 | #define HDMI_FC_GCP 0x1018 | ||
94 | #define HDMI_FC_AVICONF0 0x1019 | ||
95 | #define HDMI_FC_AVICONF1 0x101A | ||
96 | #define HDMI_FC_AVICONF2 0x101B | ||
97 | #define HDMI_FC_AVIVID 0x101C | ||
98 | #define HDMI_FC_AVIETB0 0x101D | ||
99 | #define HDMI_FC_AVIETB1 0x101E | ||
100 | #define HDMI_FC_AVISBB0 0x101F | ||
101 | #define HDMI_FC_AVISBB1 0x1020 | ||
102 | #define HDMI_FC_AVIELB0 0x1021 | ||
103 | #define HDMI_FC_AVIELB1 0x1022 | ||
104 | #define HDMI_FC_AVISRB0 0x1023 | ||
105 | #define HDMI_FC_AVISRB1 0x1024 | ||
106 | #define HDMI_FC_AUDICONF0 0x1025 | ||
107 | #define HDMI_FC_AUDICONF1 0x1026 | ||
108 | #define HDMI_FC_AUDICONF2 0x1027 | ||
109 | #define HDMI_FC_AUDICONF3 0x1028 | ||
110 | #define HDMI_FC_VSDIEEEID0 0x1029 | ||
111 | #define HDMI_FC_VSDSIZE 0x102A | ||
112 | #define HDMI_FC_VSDIEEEID1 0x1030 | ||
113 | #define HDMI_FC_VSDIEEEID2 0x1031 | ||
114 | #define HDMI_FC_VSDPAYLOAD0 0x1032 | ||
115 | #define HDMI_FC_VSDPAYLOAD1 0x1033 | ||
116 | #define HDMI_FC_VSDPAYLOAD2 0x1034 | ||
117 | #define HDMI_FC_VSDPAYLOAD3 0x1035 | ||
118 | #define HDMI_FC_VSDPAYLOAD4 0x1036 | ||
119 | #define HDMI_FC_VSDPAYLOAD5 0x1037 | ||
120 | #define HDMI_FC_VSDPAYLOAD6 0x1038 | ||
121 | #define HDMI_FC_VSDPAYLOAD7 0x1039 | ||
122 | #define HDMI_FC_VSDPAYLOAD8 0x103A | ||
123 | #define HDMI_FC_VSDPAYLOAD9 0x103B | ||
124 | #define HDMI_FC_VSDPAYLOAD10 0x103C | ||
125 | #define HDMI_FC_VSDPAYLOAD11 0x103D | ||
126 | #define HDMI_FC_VSDPAYLOAD12 0x103E | ||
127 | #define HDMI_FC_VSDPAYLOAD13 0x103F | ||
128 | #define HDMI_FC_VSDPAYLOAD14 0x1040 | ||
129 | #define HDMI_FC_VSDPAYLOAD15 0x1041 | ||
130 | #define HDMI_FC_VSDPAYLOAD16 0x1042 | ||
131 | #define HDMI_FC_VSDPAYLOAD17 0x1043 | ||
132 | #define HDMI_FC_VSDPAYLOAD18 0x1044 | ||
133 | #define HDMI_FC_VSDPAYLOAD19 0x1045 | ||
134 | #define HDMI_FC_VSDPAYLOAD20 0x1046 | ||
135 | #define HDMI_FC_VSDPAYLOAD21 0x1047 | ||
136 | #define HDMI_FC_VSDPAYLOAD22 0x1048 | ||
137 | #define HDMI_FC_VSDPAYLOAD23 0x1049 | ||
138 | #define HDMI_FC_SPDVENDORNAME0 0x104A | ||
139 | #define HDMI_FC_SPDVENDORNAME1 0x104B | ||
140 | #define HDMI_FC_SPDVENDORNAME2 0x104C | ||
141 | #define HDMI_FC_SPDVENDORNAME3 0x104D | ||
142 | #define HDMI_FC_SPDVENDORNAME4 0x104E | ||
143 | #define HDMI_FC_SPDVENDORNAME5 0x104F | ||
144 | #define HDMI_FC_SPDVENDORNAME6 0x1050 | ||
145 | #define HDMI_FC_SPDVENDORNAME7 0x1051 | ||
146 | #define HDMI_FC_SDPPRODUCTNAME0 0x1052 | ||
147 | #define HDMI_FC_SDPPRODUCTNAME1 0x1053 | ||
148 | #define HDMI_FC_SDPPRODUCTNAME2 0x1054 | ||
149 | #define HDMI_FC_SDPPRODUCTNAME3 0x1055 | ||
150 | #define HDMI_FC_SDPPRODUCTNAME4 0x1056 | ||
151 | #define HDMI_FC_SDPPRODUCTNAME5 0x1057 | ||
152 | #define HDMI_FC_SDPPRODUCTNAME6 0x1058 | ||
153 | #define HDMI_FC_SDPPRODUCTNAME7 0x1059 | ||
154 | #define HDMI_FC_SDPPRODUCTNAME8 0x105A | ||
155 | #define HDMI_FC_SDPPRODUCTNAME9 0x105B | ||
156 | #define HDMI_FC_SDPPRODUCTNAME10 0x105C | ||
157 | #define HDMI_FC_SDPPRODUCTNAME11 0x105D | ||
158 | #define HDMI_FC_SDPPRODUCTNAME12 0x105E | ||
159 | #define HDMI_FC_SDPPRODUCTNAME13 0x105F | ||
160 | #define HDMI_FC_SDPPRODUCTNAME14 0x1060 | ||
161 | #define HDMI_FC_SPDPRODUCTNAME15 0x1061 | ||
162 | #define HDMI_FC_SPDDEVICEINF 0x1062 | ||
163 | #define HDMI_FC_AUDSCONF 0x1063 | ||
164 | #define HDMI_FC_AUDSSTAT 0x1064 | ||
165 | #define HDMI_FC_DATACH0FILL 0x1070 | ||
166 | #define HDMI_FC_DATACH1FILL 0x1071 | ||
167 | #define HDMI_FC_DATACH2FILL 0x1072 | ||
168 | #define HDMI_FC_CTRLQHIGH 0x1073 | ||
169 | #define HDMI_FC_CTRLQLOW 0x1074 | ||
170 | #define HDMI_FC_ACP0 0x1075 | ||
171 | #define HDMI_FC_ACP28 0x1076 | ||
172 | #define HDMI_FC_ACP27 0x1077 | ||
173 | #define HDMI_FC_ACP26 0x1078 | ||
174 | #define HDMI_FC_ACP25 0x1079 | ||
175 | #define HDMI_FC_ACP24 0x107A | ||
176 | #define HDMI_FC_ACP23 0x107B | ||
177 | #define HDMI_FC_ACP22 0x107C | ||
178 | #define HDMI_FC_ACP21 0x107D | ||
179 | #define HDMI_FC_ACP20 0x107E | ||
180 | #define HDMI_FC_ACP19 0x107F | ||
181 | #define HDMI_FC_ACP18 0x1080 | ||
182 | #define HDMI_FC_ACP17 0x1081 | ||
183 | #define HDMI_FC_ACP16 0x1082 | ||
184 | #define HDMI_FC_ACP15 0x1083 | ||
185 | #define HDMI_FC_ACP14 0x1084 | ||
186 | #define HDMI_FC_ACP13 0x1085 | ||
187 | #define HDMI_FC_ACP12 0x1086 | ||
188 | #define HDMI_FC_ACP11 0x1087 | ||
189 | #define HDMI_FC_ACP10 0x1088 | ||
190 | #define HDMI_FC_ACP9 0x1089 | ||
191 | #define HDMI_FC_ACP8 0x108A | ||
192 | #define HDMI_FC_ACP7 0x108B | ||
193 | #define HDMI_FC_ACP6 0x108C | ||
194 | #define HDMI_FC_ACP5 0x108D | ||
195 | #define HDMI_FC_ACP4 0x108E | ||
196 | #define HDMI_FC_ACP3 0x108F | ||
197 | #define HDMI_FC_ACP2 0x1090 | ||
198 | #define HDMI_FC_ACP1 0x1091 | ||
199 | #define HDMI_FC_ISCR1_0 0x1092 | ||
200 | #define HDMI_FC_ISCR1_16 0x1093 | ||
201 | #define HDMI_FC_ISCR1_15 0x1094 | ||
202 | #define HDMI_FC_ISCR1_14 0x1095 | ||
203 | #define HDMI_FC_ISCR1_13 0x1096 | ||
204 | #define HDMI_FC_ISCR1_12 0x1097 | ||
205 | #define HDMI_FC_ISCR1_11 0x1098 | ||
206 | #define HDMI_FC_ISCR1_10 0x1099 | ||
207 | #define HDMI_FC_ISCR1_9 0x109A | ||
208 | #define HDMI_FC_ISCR1_8 0x109B | ||
209 | #define HDMI_FC_ISCR1_7 0x109C | ||
210 | #define HDMI_FC_ISCR1_6 0x109D | ||
211 | #define HDMI_FC_ISCR1_5 0x109E | ||
212 | #define HDMI_FC_ISCR1_4 0x109F | ||
213 | #define HDMI_FC_ISCR1_3 0x10A0 | ||
214 | #define HDMI_FC_ISCR1_2 0x10A1 | ||
215 | #define HDMI_FC_ISCR1_1 0x10A2 | ||
216 | #define HDMI_FC_ISCR2_15 0x10A3 | ||
217 | #define HDMI_FC_ISCR2_14 0x10A4 | ||
218 | #define HDMI_FC_ISCR2_13 0x10A5 | ||
219 | #define HDMI_FC_ISCR2_12 0x10A6 | ||
220 | #define HDMI_FC_ISCR2_11 0x10A7 | ||
221 | #define HDMI_FC_ISCR2_10 0x10A8 | ||
222 | #define HDMI_FC_ISCR2_9 0x10A9 | ||
223 | #define HDMI_FC_ISCR2_8 0x10AA | ||
224 | #define HDMI_FC_ISCR2_7 0x10AB | ||
225 | #define HDMI_FC_ISCR2_6 0x10AC | ||
226 | #define HDMI_FC_ISCR2_5 0x10AD | ||
227 | #define HDMI_FC_ISCR2_4 0x10AE | ||
228 | #define HDMI_FC_ISCR2_3 0x10AF | ||
229 | #define HDMI_FC_ISCR2_2 0x10B0 | ||
230 | #define HDMI_FC_ISCR2_1 0x10B1 | ||
231 | #define HDMI_FC_ISCR2_0 0x10B2 | ||
232 | #define HDMI_FC_DATAUTO0 0x10B3 | ||
233 | #define HDMI_FC_DATAUTO1 0x10B4 | ||
234 | #define HDMI_FC_DATAUTO2 0x10B5 | ||
235 | #define HDMI_FC_DATMAN 0x10B6 | ||
236 | #define HDMI_FC_DATAUTO3 0x10B7 | ||
237 | #define HDMI_FC_RDRB0 0x10B8 | ||
238 | #define HDMI_FC_RDRB1 0x10B9 | ||
239 | #define HDMI_FC_RDRB2 0x10BA | ||
240 | #define HDMI_FC_RDRB3 0x10BB | ||
241 | #define HDMI_FC_RDRB4 0x10BC | ||
242 | #define HDMI_FC_RDRB5 0x10BD | ||
243 | #define HDMI_FC_RDRB6 0x10BE | ||
244 | #define HDMI_FC_RDRB7 0x10BF | ||
245 | #define HDMI_FC_STAT0 0x10D0 | ||
246 | #define HDMI_FC_INT0 0x10D1 | ||
247 | #define HDMI_FC_MASK0 0x10D2 | ||
248 | #define HDMI_FC_POL0 0x10D3 | ||
249 | #define HDMI_FC_STAT1 0x10D4 | ||
250 | #define HDMI_FC_INT1 0x10D5 | ||
251 | #define HDMI_FC_MASK1 0x10D6 | ||
252 | #define HDMI_FC_POL1 0x10D7 | ||
253 | #define HDMI_FC_STAT2 0x10D8 | ||
254 | #define HDMI_FC_INT2 0x10D9 | ||
255 | #define HDMI_FC_MASK2 0x10DA | ||
256 | #define HDMI_FC_POL2 0x10DB | ||
257 | #define HDMI_FC_PRCONF 0x10E0 | ||
258 | |||
259 | #define HDMI_FC_GMD_STAT 0x1100 | ||
260 | #define HDMI_FC_GMD_EN 0x1101 | ||
261 | #define HDMI_FC_GMD_UP 0x1102 | ||
262 | #define HDMI_FC_GMD_CONF 0x1103 | ||
263 | #define HDMI_FC_GMD_HB 0x1104 | ||
264 | #define HDMI_FC_GMD_PB0 0x1105 | ||
265 | #define HDMI_FC_GMD_PB1 0x1106 | ||
266 | #define HDMI_FC_GMD_PB2 0x1107 | ||
267 | #define HDMI_FC_GMD_PB3 0x1108 | ||
268 | #define HDMI_FC_GMD_PB4 0x1109 | ||
269 | #define HDMI_FC_GMD_PB5 0x110A | ||
270 | #define HDMI_FC_GMD_PB6 0x110B | ||
271 | #define HDMI_FC_GMD_PB7 0x110C | ||
272 | #define HDMI_FC_GMD_PB8 0x110D | ||
273 | #define HDMI_FC_GMD_PB9 0x110E | ||
274 | #define HDMI_FC_GMD_PB10 0x110F | ||
275 | #define HDMI_FC_GMD_PB11 0x1110 | ||
276 | #define HDMI_FC_GMD_PB12 0x1111 | ||
277 | #define HDMI_FC_GMD_PB13 0x1112 | ||
278 | #define HDMI_FC_GMD_PB14 0x1113 | ||
279 | #define HDMI_FC_GMD_PB15 0x1114 | ||
280 | #define HDMI_FC_GMD_PB16 0x1115 | ||
281 | #define HDMI_FC_GMD_PB17 0x1116 | ||
282 | #define HDMI_FC_GMD_PB18 0x1117 | ||
283 | #define HDMI_FC_GMD_PB19 0x1118 | ||
284 | #define HDMI_FC_GMD_PB20 0x1119 | ||
285 | #define HDMI_FC_GMD_PB21 0x111A | ||
286 | #define HDMI_FC_GMD_PB22 0x111B | ||
287 | #define HDMI_FC_GMD_PB23 0x111C | ||
288 | #define HDMI_FC_GMD_PB24 0x111D | ||
289 | #define HDMI_FC_GMD_PB25 0x111E | ||
290 | #define HDMI_FC_GMD_PB26 0x111F | ||
291 | #define HDMI_FC_GMD_PB27 0x1120 | ||
292 | |||
293 | #define HDMI_FC_DBGFORCE 0x1200 | ||
294 | #define HDMI_FC_DBGAUD0CH0 0x1201 | ||
295 | #define HDMI_FC_DBGAUD1CH0 0x1202 | ||
296 | #define HDMI_FC_DBGAUD2CH0 0x1203 | ||
297 | #define HDMI_FC_DBGAUD0CH1 0x1204 | ||
298 | #define HDMI_FC_DBGAUD1CH1 0x1205 | ||
299 | #define HDMI_FC_DBGAUD2CH1 0x1206 | ||
300 | #define HDMI_FC_DBGAUD0CH2 0x1207 | ||
301 | #define HDMI_FC_DBGAUD1CH2 0x1208 | ||
302 | #define HDMI_FC_DBGAUD2CH2 0x1209 | ||
303 | #define HDMI_FC_DBGAUD0CH3 0x120A | ||
304 | #define HDMI_FC_DBGAUD1CH3 0x120B | ||
305 | #define HDMI_FC_DBGAUD2CH3 0x120C | ||
306 | #define HDMI_FC_DBGAUD0CH4 0x120D | ||
307 | #define HDMI_FC_DBGAUD1CH4 0x120E | ||
308 | #define HDMI_FC_DBGAUD2CH4 0x120F | ||
309 | #define HDMI_FC_DBGAUD0CH5 0x1210 | ||
310 | #define HDMI_FC_DBGAUD1CH5 0x1211 | ||
311 | #define HDMI_FC_DBGAUD2CH5 0x1212 | ||
312 | #define HDMI_FC_DBGAUD0CH6 0x1213 | ||
313 | #define HDMI_FC_DBGAUD1CH6 0x1214 | ||
314 | #define HDMI_FC_DBGAUD2CH6 0x1215 | ||
315 | #define HDMI_FC_DBGAUD0CH7 0x1216 | ||
316 | #define HDMI_FC_DBGAUD1CH7 0x1217 | ||
317 | #define HDMI_FC_DBGAUD2CH7 0x1218 | ||
318 | #define HDMI_FC_DBGTMDS0 0x1219 | ||
319 | #define HDMI_FC_DBGTMDS1 0x121A | ||
320 | #define HDMI_FC_DBGTMDS2 0x121B | ||
321 | |||
322 | /* HDMI Source PHY Registers */ | ||
323 | #define HDMI_PHY_CONF0 0x3000 | ||
324 | #define HDMI_PHY_TST0 0x3001 | ||
325 | #define HDMI_PHY_TST1 0x3002 | ||
326 | #define HDMI_PHY_TST2 0x3003 | ||
327 | #define HDMI_PHY_STAT0 0x3004 | ||
328 | #define HDMI_PHY_INT0 0x3005 | ||
329 | #define HDMI_PHY_MASK0 0x3006 | ||
330 | #define HDMI_PHY_POL0 0x3007 | ||
331 | |||
332 | /* HDMI Master PHY Registers */ | ||
333 | #define HDMI_PHY_I2CM_SLAVE_ADDR 0x3020 | ||
334 | #define HDMI_PHY_I2CM_ADDRESS_ADDR 0x3021 | ||
335 | #define HDMI_PHY_I2CM_DATAO_1_ADDR 0x3022 | ||
336 | #define HDMI_PHY_I2CM_DATAO_0_ADDR 0x3023 | ||
337 | #define HDMI_PHY_I2CM_DATAI_1_ADDR 0x3024 | ||
338 | #define HDMI_PHY_I2CM_DATAI_0_ADDR 0x3025 | ||
339 | #define HDMI_PHY_I2CM_OPERATION_ADDR 0x3026 | ||
340 | #define HDMI_PHY_I2CM_INT_ADDR 0x3027 | ||
341 | #define HDMI_PHY_I2CM_CTLINT_ADDR 0x3028 | ||
342 | #define HDMI_PHY_I2CM_DIV_ADDR 0x3029 | ||
343 | #define HDMI_PHY_I2CM_SOFTRSTZ_ADDR 0x302a | ||
344 | #define HDMI_PHY_I2CM_SS_SCL_HCNT_1_ADDR 0x302b | ||
345 | #define HDMI_PHY_I2CM_SS_SCL_HCNT_0_ADDR 0x302c | ||
346 | #define HDMI_PHY_I2CM_SS_SCL_LCNT_1_ADDR 0x302d | ||
347 | #define HDMI_PHY_I2CM_SS_SCL_LCNT_0_ADDR 0x302e | ||
348 | #define HDMI_PHY_I2CM_FS_SCL_HCNT_1_ADDR 0x302f | ||
349 | #define HDMI_PHY_I2CM_FS_SCL_HCNT_0_ADDR 0x3030 | ||
350 | #define HDMI_PHY_I2CM_FS_SCL_LCNT_1_ADDR 0x3031 | ||
351 | #define HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR 0x3032 | ||
352 | |||
353 | /* Audio Sampler Registers */ | ||
354 | #define HDMI_AUD_CONF0 0x3100 | ||
355 | #define HDMI_AUD_CONF1 0x3101 | ||
356 | #define HDMI_AUD_INT 0x3102 | ||
357 | #define HDMI_AUD_CONF2 0x3103 | ||
358 | #define HDMI_AUD_N1 0x3200 | ||
359 | #define HDMI_AUD_N2 0x3201 | ||
360 | #define HDMI_AUD_N3 0x3202 | ||
361 | #define HDMI_AUD_CTS1 0x3203 | ||
362 | #define HDMI_AUD_CTS2 0x3204 | ||
363 | #define HDMI_AUD_CTS3 0x3205 | ||
364 | #define HDMI_AUD_INPUTCLKFS 0x3206 | ||
365 | #define HDMI_AUD_SPDIFINT 0x3302 | ||
366 | #define HDMI_AUD_CONF0_HBR 0x3400 | ||
367 | #define HDMI_AUD_HBR_STATUS 0x3401 | ||
368 | #define HDMI_AUD_HBR_INT 0x3402 | ||
369 | #define HDMI_AUD_HBR_POL 0x3403 | ||
370 | #define HDMI_AUD_HBR_MASK 0x3404 | ||
371 | |||
372 | /* | ||
373 | * Generic Parallel Audio Interface Registers | ||
374 | * Not used as GPAUD interface is not enabled in hw | ||
375 | */ | ||
376 | #define HDMI_GP_CONF0 0x3500 | ||
377 | #define HDMI_GP_CONF1 0x3501 | ||
378 | #define HDMI_GP_CONF2 0x3502 | ||
379 | #define HDMI_GP_STAT 0x3503 | ||
380 | #define HDMI_GP_INT 0x3504 | ||
381 | #define HDMI_GP_MASK 0x3505 | ||
382 | #define HDMI_GP_POL 0x3506 | ||
383 | |||
384 | /* Audio DMA Registers */ | ||
385 | #define HDMI_AHB_DMA_CONF0 0x3600 | ||
386 | #define HDMI_AHB_DMA_START 0x3601 | ||
387 | #define HDMI_AHB_DMA_STOP 0x3602 | ||
388 | #define HDMI_AHB_DMA_THRSLD 0x3603 | ||
389 | #define HDMI_AHB_DMA_STRADDR0 0x3604 | ||
390 | #define HDMI_AHB_DMA_STRADDR1 0x3605 | ||
391 | #define HDMI_AHB_DMA_STRADDR2 0x3606 | ||
392 | #define HDMI_AHB_DMA_STRADDR3 0x3607 | ||
393 | #define HDMI_AHB_DMA_STPADDR0 0x3608 | ||
394 | #define HDMI_AHB_DMA_STPADDR1 0x3609 | ||
395 | #define HDMI_AHB_DMA_STPADDR2 0x360a | ||
396 | #define HDMI_AHB_DMA_STPADDR3 0x360b | ||
397 | #define HDMI_AHB_DMA_BSTADDR0 0x360c | ||
398 | #define HDMI_AHB_DMA_BSTADDR1 0x360d | ||
399 | #define HDMI_AHB_DMA_BSTADDR2 0x360e | ||
400 | #define HDMI_AHB_DMA_BSTADDR3 0x360f | ||
401 | #define HDMI_AHB_DMA_MBLENGTH0 0x3610 | ||
402 | #define HDMI_AHB_DMA_MBLENGTH1 0x3611 | ||
403 | #define HDMI_AHB_DMA_STAT 0x3612 | ||
404 | #define HDMI_AHB_DMA_INT 0x3613 | ||
405 | #define HDMI_AHB_DMA_MASK 0x3614 | ||
406 | #define HDMI_AHB_DMA_POL 0x3615 | ||
407 | #define HDMI_AHB_DMA_CONF1 0x3616 | ||
408 | #define HDMI_AHB_DMA_BUFFSTAT 0x3617 | ||
409 | #define HDMI_AHB_DMA_BUFFINT 0x3618 | ||
410 | #define HDMI_AHB_DMA_BUFFMASK 0x3619 | ||
411 | #define HDMI_AHB_DMA_BUFFPOL 0x361a | ||
412 | |||
413 | /* Main Controller Registers */ | ||
414 | #define HDMI_MC_SFRDIV 0x4000 | ||
415 | #define HDMI_MC_CLKDIS 0x4001 | ||
416 | #define HDMI_MC_SWRSTZ 0x4002 | ||
417 | #define HDMI_MC_OPCTRL 0x4003 | ||
418 | #define HDMI_MC_FLOWCTRL 0x4004 | ||
419 | #define HDMI_MC_PHYRSTZ 0x4005 | ||
420 | #define HDMI_MC_LOCKONCLOCK 0x4006 | ||
421 | #define HDMI_MC_HEACPHY_RST 0x4007 | ||
422 | |||
423 | /* Color Space Converter Registers */ | ||
424 | #define HDMI_CSC_CFG 0x4100 | ||
425 | #define HDMI_CSC_SCALE 0x4101 | ||
426 | #define HDMI_CSC_COEF_A1_MSB 0x4102 | ||
427 | #define HDMI_CSC_COEF_A1_LSB 0x4103 | ||
428 | #define HDMI_CSC_COEF_A2_MSB 0x4104 | ||
429 | #define HDMI_CSC_COEF_A2_LSB 0x4105 | ||
430 | #define HDMI_CSC_COEF_A3_MSB 0x4106 | ||
431 | #define HDMI_CSC_COEF_A3_LSB 0x4107 | ||
432 | #define HDMI_CSC_COEF_A4_MSB 0x4108 | ||
433 | #define HDMI_CSC_COEF_A4_LSB 0x4109 | ||
434 | #define HDMI_CSC_COEF_B1_MSB 0x410A | ||
435 | #define HDMI_CSC_COEF_B1_LSB 0x410B | ||
436 | #define HDMI_CSC_COEF_B2_MSB 0x410C | ||
437 | #define HDMI_CSC_COEF_B2_LSB 0x410D | ||
438 | #define HDMI_CSC_COEF_B3_MSB 0x410E | ||
439 | #define HDMI_CSC_COEF_B3_LSB 0x410F | ||
440 | #define HDMI_CSC_COEF_B4_MSB 0x4110 | ||
441 | #define HDMI_CSC_COEF_B4_LSB 0x4111 | ||
442 | #define HDMI_CSC_COEF_C1_MSB 0x4112 | ||
443 | #define HDMI_CSC_COEF_C1_LSB 0x4113 | ||
444 | #define HDMI_CSC_COEF_C2_MSB 0x4114 | ||
445 | #define HDMI_CSC_COEF_C2_LSB 0x4115 | ||
446 | #define HDMI_CSC_COEF_C3_MSB 0x4116 | ||
447 | #define HDMI_CSC_COEF_C3_LSB 0x4117 | ||
448 | #define HDMI_CSC_COEF_C4_MSB 0x4118 | ||
449 | #define HDMI_CSC_COEF_C4_LSB 0x4119 | ||
450 | |||
451 | /* HDCP Encryption Engine Registers */ | ||
452 | #define HDMI_A_HDCPCFG0 0x5000 | ||
453 | #define HDMI_A_HDCPCFG1 0x5001 | ||
454 | #define HDMI_A_HDCPOBS0 0x5002 | ||
455 | #define HDMI_A_HDCPOBS1 0x5003 | ||
456 | #define HDMI_A_HDCPOBS2 0x5004 | ||
457 | #define HDMI_A_HDCPOBS3 0x5005 | ||
458 | #define HDMI_A_APIINTCLR 0x5006 | ||
459 | #define HDMI_A_APIINTSTAT 0x5007 | ||
460 | #define HDMI_A_APIINTMSK 0x5008 | ||
461 | #define HDMI_A_VIDPOLCFG 0x5009 | ||
462 | #define HDMI_A_OESSWCFG 0x500A | ||
463 | #define HDMI_A_TIMER1SETUP0 0x500B | ||
464 | #define HDMI_A_TIMER1SETUP1 0x500C | ||
465 | #define HDMI_A_TIMER2SETUP0 0x500D | ||
466 | #define HDMI_A_TIMER2SETUP1 0x500E | ||
467 | #define HDMI_A_100MSCFG 0x500F | ||
468 | #define HDMI_A_2SCFG0 0x5010 | ||
469 | #define HDMI_A_2SCFG1 0x5011 | ||
470 | #define HDMI_A_5SCFG0 0x5012 | ||
471 | #define HDMI_A_5SCFG1 0x5013 | ||
472 | #define HDMI_A_SRMVERLSB 0x5014 | ||
473 | #define HDMI_A_SRMVERMSB 0x5015 | ||
474 | #define HDMI_A_SRMCTRL 0x5016 | ||
475 | #define HDMI_A_SFRSETUP 0x5017 | ||
476 | #define HDMI_A_I2CHSETUP 0x5018 | ||
477 | #define HDMI_A_INTSETUP 0x5019 | ||
478 | #define HDMI_A_PRESETUP 0x501A | ||
479 | #define HDMI_A_SRM_BASE 0x5020 | ||
480 | |||
481 | /* CEC Engine Registers */ | ||
482 | #define HDMI_CEC_CTRL 0x7D00 | ||
483 | #define HDMI_CEC_STAT 0x7D01 | ||
484 | #define HDMI_CEC_MASK 0x7D02 | ||
485 | #define HDMI_CEC_POLARITY 0x7D03 | ||
486 | #define HDMI_CEC_INT 0x7D04 | ||
487 | #define HDMI_CEC_ADDR_L 0x7D05 | ||
488 | #define HDMI_CEC_ADDR_H 0x7D06 | ||
489 | #define HDMI_CEC_TX_CNT 0x7D07 | ||
490 | #define HDMI_CEC_RX_CNT 0x7D08 | ||
491 | #define HDMI_CEC_TX_DATA0 0x7D10 | ||
492 | #define HDMI_CEC_TX_DATA1 0x7D11 | ||
493 | #define HDMI_CEC_TX_DATA2 0x7D12 | ||
494 | #define HDMI_CEC_TX_DATA3 0x7D13 | ||
495 | #define HDMI_CEC_TX_DATA4 0x7D14 | ||
496 | #define HDMI_CEC_TX_DATA5 0x7D15 | ||
497 | #define HDMI_CEC_TX_DATA6 0x7D16 | ||
498 | #define HDMI_CEC_TX_DATA7 0x7D17 | ||
499 | #define HDMI_CEC_TX_DATA8 0x7D18 | ||
500 | #define HDMI_CEC_TX_DATA9 0x7D19 | ||
501 | #define HDMI_CEC_TX_DATA10 0x7D1a | ||
502 | #define HDMI_CEC_TX_DATA11 0x7D1b | ||
503 | #define HDMI_CEC_TX_DATA12 0x7D1c | ||
504 | #define HDMI_CEC_TX_DATA13 0x7D1d | ||
505 | #define HDMI_CEC_TX_DATA14 0x7D1e | ||
506 | #define HDMI_CEC_TX_DATA15 0x7D1f | ||
507 | #define HDMI_CEC_RX_DATA0 0x7D20 | ||
508 | #define HDMI_CEC_RX_DATA1 0x7D21 | ||
509 | #define HDMI_CEC_RX_DATA2 0x7D22 | ||
510 | #define HDMI_CEC_RX_DATA3 0x7D23 | ||
511 | #define HDMI_CEC_RX_DATA4 0x7D24 | ||
512 | #define HDMI_CEC_RX_DATA5 0x7D25 | ||
513 | #define HDMI_CEC_RX_DATA6 0x7D26 | ||
514 | #define HDMI_CEC_RX_DATA7 0x7D27 | ||
515 | #define HDMI_CEC_RX_DATA8 0x7D28 | ||
516 | #define HDMI_CEC_RX_DATA9 0x7D29 | ||
517 | #define HDMI_CEC_RX_DATA10 0x7D2a | ||
518 | #define HDMI_CEC_RX_DATA11 0x7D2b | ||
519 | #define HDMI_CEC_RX_DATA12 0x7D2c | ||
520 | #define HDMI_CEC_RX_DATA13 0x7D2d | ||
521 | #define HDMI_CEC_RX_DATA14 0x7D2e | ||
522 | #define HDMI_CEC_RX_DATA15 0x7D2f | ||
523 | #define HDMI_CEC_LOCK 0x7D30 | ||
524 | #define HDMI_CEC_WKUPCTRL 0x7D31 | ||
525 | |||
526 | /* I2C Master Registers (E-DDC) */ | ||
527 | #define HDMI_I2CM_SLAVE 0x7E00 | ||
528 | #define HDMI_I2CMESS 0x7E01 | ||
529 | #define HDMI_I2CM_DATAO 0x7E02 | ||
530 | #define HDMI_I2CM_DATAI 0x7E03 | ||
531 | #define HDMI_I2CM_OPERATION 0x7E04 | ||
532 | #define HDMI_I2CM_INT 0x7E05 | ||
533 | #define HDMI_I2CM_CTLINT 0x7E06 | ||
534 | #define HDMI_I2CM_DIV 0x7E07 | ||
535 | #define HDMI_I2CM_SEGADDR 0x7E08 | ||
536 | #define HDMI_I2CM_SOFTRSTZ 0x7E09 | ||
537 | #define HDMI_I2CM_SEGPTR 0x7E0A | ||
538 | #define HDMI_I2CM_SS_SCL_HCNT_1_ADDR 0x7E0B | ||
539 | #define HDMI_I2CM_SS_SCL_HCNT_0_ADDR 0x7E0C | ||
540 | #define HDMI_I2CM_SS_SCL_LCNT_1_ADDR 0x7E0D | ||
541 | #define HDMI_I2CM_SS_SCL_LCNT_0_ADDR 0x7E0E | ||
542 | #define HDMI_I2CM_FS_SCL_HCNT_1_ADDR 0x7E0F | ||
543 | #define HDMI_I2CM_FS_SCL_HCNT_0_ADDR 0x7E10 | ||
544 | #define HDMI_I2CM_FS_SCL_LCNT_1_ADDR 0x7E11 | ||
545 | #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12 | ||
546 | |||
547 | enum { | ||
548 | /* IH_FC_INT2 field values */ | ||
549 | HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03, | ||
550 | HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02, | ||
551 | HDMI_IH_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01, | ||
552 | |||
553 | /* IH_FC_STAT2 field values */ | ||
554 | HDMI_IH_FC_STAT2_OVERFLOW_MASK = 0x03, | ||
555 | HDMI_IH_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02, | ||
556 | HDMI_IH_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01, | ||
557 | |||
558 | /* IH_PHY_STAT0 field values */ | ||
559 | HDMI_IH_PHY_STAT0_RX_SENSE3 = 0x20, | ||
560 | HDMI_IH_PHY_STAT0_RX_SENSE2 = 0x10, | ||
561 | HDMI_IH_PHY_STAT0_RX_SENSE1 = 0x8, | ||
562 | HDMI_IH_PHY_STAT0_RX_SENSE0 = 0x4, | ||
563 | HDMI_IH_PHY_STAT0_TX_PHY_LOCK = 0x2, | ||
564 | HDMI_IH_PHY_STAT0_HPD = 0x1, | ||
565 | |||
566 | /* IH_MUTE_I2CMPHY_STAT0 field values */ | ||
567 | HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYDONE = 0x2, | ||
568 | HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYERROR = 0x1, | ||
569 | |||
570 | /* IH_AHBDMAAUD_STAT0 field values */ | ||
571 | HDMI_IH_AHBDMAAUD_STAT0_ERROR = 0x20, | ||
572 | HDMI_IH_AHBDMAAUD_STAT0_LOST = 0x10, | ||
573 | HDMI_IH_AHBDMAAUD_STAT0_RETRY = 0x08, | ||
574 | HDMI_IH_AHBDMAAUD_STAT0_DONE = 0x04, | ||
575 | HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = 0x02, | ||
576 | HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01, | ||
577 | |||
578 | /* IH_MUTE_FC_STAT2 field values */ | ||
579 | HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK = 0x03, | ||
580 | HDMI_IH_MUTE_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02, | ||
581 | HDMI_IH_MUTE_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01, | ||
582 | |||
583 | /* IH_MUTE_AHBDMAAUD_STAT0 field values */ | ||
584 | HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = 0x20, | ||
585 | HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = 0x10, | ||
586 | HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = 0x08, | ||
587 | HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = 0x04, | ||
588 | HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = 0x02, | ||
589 | HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01, | ||
590 | |||
591 | /* IH_MUTE field values */ | ||
592 | HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT = 0x2, | ||
593 | HDMI_IH_MUTE_MUTE_ALL_INTERRUPT = 0x1, | ||
594 | |||
595 | /* TX_INVID0 field values */ | ||
596 | HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_MASK = 0x80, | ||
597 | HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_ENABLE = 0x80, | ||
598 | HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE = 0x00, | ||
599 | HDMI_TX_INVID0_VIDEO_MAPPING_MASK = 0x1F, | ||
600 | HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET = 0, | ||
601 | |||
602 | /* TX_INSTUFFING field values */ | ||
603 | HDMI_TX_INSTUFFING_BDBDATA_STUFFING_MASK = 0x4, | ||
604 | HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE = 0x4, | ||
605 | HDMI_TX_INSTUFFING_BDBDATA_STUFFING_DISABLE = 0x0, | ||
606 | HDMI_TX_INSTUFFING_RCRDATA_STUFFING_MASK = 0x2, | ||
607 | HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE = 0x2, | ||
608 | HDMI_TX_INSTUFFING_RCRDATA_STUFFING_DISABLE = 0x0, | ||
609 | HDMI_TX_INSTUFFING_GYDATA_STUFFING_MASK = 0x1, | ||
610 | HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE = 0x1, | ||
611 | HDMI_TX_INSTUFFING_GYDATA_STUFFING_DISABLE = 0x0, | ||
612 | |||
613 | /* VP_PR_CD field values */ | ||
614 | HDMI_VP_PR_CD_COLOR_DEPTH_MASK = 0xF0, | ||
615 | HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET = 4, | ||
616 | HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK = 0x0F, | ||
617 | HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET = 0, | ||
618 | |||
619 | /* VP_STUFF field values */ | ||
620 | HDMI_VP_STUFF_IDEFAULT_PHASE_MASK = 0x20, | ||
621 | HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET = 5, | ||
622 | HDMI_VP_STUFF_IFIX_PP_TO_LAST_MASK = 0x10, | ||
623 | HDMI_VP_STUFF_IFIX_PP_TO_LAST_OFFSET = 4, | ||
624 | HDMI_VP_STUFF_ICX_GOTO_P0_ST_MASK = 0x8, | ||
625 | HDMI_VP_STUFF_ICX_GOTO_P0_ST_OFFSET = 3, | ||
626 | HDMI_VP_STUFF_YCC422_STUFFING_MASK = 0x4, | ||
627 | HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE = 0x4, | ||
628 | HDMI_VP_STUFF_YCC422_STUFFING_DIRECT_MODE = 0x0, | ||
629 | HDMI_VP_STUFF_PP_STUFFING_MASK = 0x2, | ||
630 | HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE = 0x2, | ||
631 | HDMI_VP_STUFF_PP_STUFFING_DIRECT_MODE = 0x0, | ||
632 | HDMI_VP_STUFF_PR_STUFFING_MASK = 0x1, | ||
633 | HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE = 0x1, | ||
634 | HDMI_VP_STUFF_PR_STUFFING_DIRECT_MODE = 0x0, | ||
635 | |||
636 | /* VP_CONF field values */ | ||
637 | HDMI_VP_CONF_BYPASS_EN_MASK = 0x40, | ||
638 | HDMI_VP_CONF_BYPASS_EN_ENABLE = 0x40, | ||
639 | HDMI_VP_CONF_BYPASS_EN_DISABLE = 0x00, | ||
640 | HDMI_VP_CONF_PP_EN_ENMASK = 0x20, | ||
641 | HDMI_VP_CONF_PP_EN_ENABLE = 0x20, | ||
642 | HDMI_VP_CONF_PP_EN_DISABLE = 0x00, | ||
643 | HDMI_VP_CONF_PR_EN_MASK = 0x10, | ||
644 | HDMI_VP_CONF_PR_EN_ENABLE = 0x10, | ||
645 | HDMI_VP_CONF_PR_EN_DISABLE = 0x00, | ||
646 | HDMI_VP_CONF_YCC422_EN_MASK = 0x8, | ||
647 | HDMI_VP_CONF_YCC422_EN_ENABLE = 0x8, | ||
648 | HDMI_VP_CONF_YCC422_EN_DISABLE = 0x0, | ||
649 | HDMI_VP_CONF_BYPASS_SELECT_MASK = 0x4, | ||
650 | HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER = 0x4, | ||
651 | HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER = 0x0, | ||
652 | HDMI_VP_CONF_OUTPUT_SELECTOR_MASK = 0x3, | ||
653 | HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS = 0x3, | ||
654 | HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422 = 0x1, | ||
655 | HDMI_VP_CONF_OUTPUT_SELECTOR_PP = 0x0, | ||
656 | |||
657 | /* VP_REMAP field values */ | ||
658 | HDMI_VP_REMAP_MASK = 0x3, | ||
659 | HDMI_VP_REMAP_YCC422_24bit = 0x2, | ||
660 | HDMI_VP_REMAP_YCC422_20bit = 0x1, | ||
661 | HDMI_VP_REMAP_YCC422_16bit = 0x0, | ||
662 | |||
663 | /* FC_INVIDCONF field values */ | ||
664 | HDMI_FC_INVIDCONF_HDCP_KEEPOUT_MASK = 0x80, | ||
665 | HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE = 0x80, | ||
666 | HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE = 0x00, | ||
667 | HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_MASK = 0x40, | ||
668 | HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH = 0x40, | ||
669 | HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW = 0x00, | ||
670 | HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_MASK = 0x20, | ||
671 | HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH = 0x20, | ||
672 | HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW = 0x00, | ||
673 | HDMI_FC_INVIDCONF_DE_IN_POLARITY_MASK = 0x10, | ||
674 | HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH = 0x10, | ||
675 | HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW = 0x00, | ||
676 | HDMI_FC_INVIDCONF_DVI_MODEZ_MASK = 0x8, | ||
677 | HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE = 0x8, | ||
678 | HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE = 0x0, | ||
679 | HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_MASK = 0x2, | ||
680 | HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH = 0x2, | ||
681 | HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW = 0x0, | ||
682 | HDMI_FC_INVIDCONF_IN_I_P_MASK = 0x1, | ||
683 | HDMI_FC_INVIDCONF_IN_I_P_INTERLACED = 0x1, | ||
684 | HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE = 0x0, | ||
685 | |||
686 | /* FC_AUDICONF0 field values */ | ||
687 | HDMI_FC_AUDICONF0_CC_OFFSET = 4, | ||
688 | HDMI_FC_AUDICONF0_CC_MASK = 0x70, | ||
689 | HDMI_FC_AUDICONF0_CT_OFFSET = 0, | ||
690 | HDMI_FC_AUDICONF0_CT_MASK = 0xF, | ||
691 | |||
692 | /* FC_AUDICONF1 field values */ | ||
693 | HDMI_FC_AUDICONF1_SS_OFFSET = 3, | ||
694 | HDMI_FC_AUDICONF1_SS_MASK = 0x18, | ||
695 | HDMI_FC_AUDICONF1_SF_OFFSET = 0, | ||
696 | HDMI_FC_AUDICONF1_SF_MASK = 0x7, | ||
697 | |||
698 | /* FC_AUDICONF3 field values */ | ||
699 | HDMI_FC_AUDICONF3_LFEPBL_OFFSET = 5, | ||
700 | HDMI_FC_AUDICONF3_LFEPBL_MASK = 0x60, | ||
701 | HDMI_FC_AUDICONF3_DM_INH_OFFSET = 4, | ||
702 | HDMI_FC_AUDICONF3_DM_INH_MASK = 0x10, | ||
703 | HDMI_FC_AUDICONF3_LSV_OFFSET = 0, | ||
704 | HDMI_FC_AUDICONF3_LSV_MASK = 0xF, | ||
705 | |||
706 | /* FC_AUDSCHNLS0 field values */ | ||
707 | HDMI_FC_AUDSCHNLS0_CGMSA_OFFSET = 4, | ||
708 | HDMI_FC_AUDSCHNLS0_CGMSA_MASK = 0x30, | ||
709 | HDMI_FC_AUDSCHNLS0_COPYRIGHT_OFFSET = 0, | ||
710 | HDMI_FC_AUDSCHNLS0_COPYRIGHT_MASK = 0x01, | ||
711 | |||
712 | /* FC_AUDSCHNLS3-6 field values */ | ||
713 | HDMI_FC_AUDSCHNLS3_OIEC_CH0_OFFSET = 0, | ||
714 | HDMI_FC_AUDSCHNLS3_OIEC_CH0_MASK = 0x0f, | ||
715 | HDMI_FC_AUDSCHNLS3_OIEC_CH1_OFFSET = 4, | ||
716 | HDMI_FC_AUDSCHNLS3_OIEC_CH1_MASK = 0xf0, | ||
717 | HDMI_FC_AUDSCHNLS4_OIEC_CH2_OFFSET = 0, | ||
718 | HDMI_FC_AUDSCHNLS4_OIEC_CH2_MASK = 0x0f, | ||
719 | HDMI_FC_AUDSCHNLS4_OIEC_CH3_OFFSET = 4, | ||
720 | HDMI_FC_AUDSCHNLS4_OIEC_CH3_MASK = 0xf0, | ||
721 | |||
722 | HDMI_FC_AUDSCHNLS5_OIEC_CH0_OFFSET = 0, | ||
723 | HDMI_FC_AUDSCHNLS5_OIEC_CH0_MASK = 0x0f, | ||
724 | HDMI_FC_AUDSCHNLS5_OIEC_CH1_OFFSET = 4, | ||
725 | HDMI_FC_AUDSCHNLS5_OIEC_CH1_MASK = 0xf0, | ||
726 | HDMI_FC_AUDSCHNLS6_OIEC_CH2_OFFSET = 0, | ||
727 | HDMI_FC_AUDSCHNLS6_OIEC_CH2_MASK = 0x0f, | ||
728 | HDMI_FC_AUDSCHNLS6_OIEC_CH3_OFFSET = 4, | ||
729 | HDMI_FC_AUDSCHNLS6_OIEC_CH3_MASK = 0xf0, | ||
730 | |||
731 | /* HDMI_FC_AUDSCHNLS7 field values */ | ||
732 | HDMI_FC_AUDSCHNLS7_ACCURACY_OFFSET = 4, | ||
733 | HDMI_FC_AUDSCHNLS7_ACCURACY_MASK = 0x30, | ||
734 | |||
735 | /* HDMI_FC_AUDSCHNLS8 field values */ | ||
736 | HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_MASK = 0xf0, | ||
737 | HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_OFFSET = 4, | ||
738 | HDMI_FC_AUDSCHNLS8_WORDLEGNTH_MASK = 0x0f, | ||
739 | HDMI_FC_AUDSCHNLS8_WORDLEGNTH_OFFSET = 0, | ||
740 | |||
741 | /* FC_AUDSCONF field values */ | ||
742 | HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_MASK = 0xF0, | ||
743 | HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_OFFSET = 4, | ||
744 | HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK = 0x1, | ||
745 | HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_OFFSET = 0, | ||
746 | HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1 = 0x1, | ||
747 | HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0 = 0x0, | ||
748 | |||
749 | /* FC_STAT2 field values */ | ||
750 | HDMI_FC_STAT2_OVERFLOW_MASK = 0x03, | ||
751 | HDMI_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02, | ||
752 | HDMI_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01, | ||
753 | |||
754 | /* FC_INT2 field values */ | ||
755 | HDMI_FC_INT2_OVERFLOW_MASK = 0x03, | ||
756 | HDMI_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02, | ||
757 | HDMI_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01, | ||
758 | |||
759 | /* FC_MASK2 field values */ | ||
760 | HDMI_FC_MASK2_OVERFLOW_MASK = 0x03, | ||
761 | HDMI_FC_MASK2_LOW_PRIORITY_OVERFLOW = 0x02, | ||
762 | HDMI_FC_MASK2_HIGH_PRIORITY_OVERFLOW = 0x01, | ||
763 | |||
764 | /* FC_PRCONF field values */ | ||
765 | HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK = 0xF0, | ||
766 | HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET = 4, | ||
767 | HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK = 0x0F, | ||
768 | HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET = 0, | ||
769 | |||
770 | /* FC_AVICONF0-FC_AVICONF3 field values */ | ||
771 | HDMI_FC_AVICONF0_PIX_FMT_MASK = 0x03, | ||
772 | HDMI_FC_AVICONF0_PIX_FMT_RGB = 0x00, | ||
773 | HDMI_FC_AVICONF0_PIX_FMT_YCBCR422 = 0x01, | ||
774 | HDMI_FC_AVICONF0_PIX_FMT_YCBCR444 = 0x02, | ||
775 | HDMI_FC_AVICONF0_ACTIVE_FMT_MASK = 0x40, | ||
776 | HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT = 0x40, | ||
777 | HDMI_FC_AVICONF0_ACTIVE_FMT_NO_INFO = 0x00, | ||
778 | HDMI_FC_AVICONF0_BAR_DATA_MASK = 0x0C, | ||
779 | HDMI_FC_AVICONF0_BAR_DATA_NO_DATA = 0x00, | ||
780 | HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR = 0x04, | ||
781 | HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR = 0x08, | ||
782 | HDMI_FC_AVICONF0_BAR_DATA_VERT_HORIZ_BAR = 0x0C, | ||
783 | HDMI_FC_AVICONF0_SCAN_INFO_MASK = 0x30, | ||
784 | HDMI_FC_AVICONF0_SCAN_INFO_OVERSCAN = 0x10, | ||
785 | HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN = 0x20, | ||
786 | HDMI_FC_AVICONF0_SCAN_INFO_NODATA = 0x00, | ||
787 | |||
788 | HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_MASK = 0x0F, | ||
789 | HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_USE_CODED = 0x08, | ||
790 | HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_4_3 = 0x09, | ||
791 | HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_16_9 = 0x0A, | ||
792 | HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_14_9 = 0x0B, | ||
793 | HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_MASK = 0x30, | ||
794 | HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_NO_DATA = 0x00, | ||
795 | HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_4_3 = 0x10, | ||
796 | HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_16_9 = 0x20, | ||
797 | HDMI_FC_AVICONF1_COLORIMETRY_MASK = 0xC0, | ||
798 | HDMI_FC_AVICONF1_COLORIMETRY_NO_DATA = 0x00, | ||
799 | HDMI_FC_AVICONF1_COLORIMETRY_SMPTE = 0x40, | ||
800 | HDMI_FC_AVICONF1_COLORIMETRY_ITUR = 0x80, | ||
801 | HDMI_FC_AVICONF1_COLORIMETRY_EXTENDED_INFO = 0xC0, | ||
802 | |||
803 | HDMI_FC_AVICONF2_SCALING_MASK = 0x03, | ||
804 | HDMI_FC_AVICONF2_SCALING_NONE = 0x00, | ||
805 | HDMI_FC_AVICONF2_SCALING_HORIZ = 0x01, | ||
806 | HDMI_FC_AVICONF2_SCALING_VERT = 0x02, | ||
807 | HDMI_FC_AVICONF2_SCALING_HORIZ_VERT = 0x03, | ||
808 | HDMI_FC_AVICONF2_RGB_QUANT_MASK = 0x0C, | ||
809 | HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT = 0x00, | ||
810 | HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE = 0x04, | ||
811 | HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE = 0x08, | ||
812 | HDMI_FC_AVICONF2_EXT_COLORIMETRY_MASK = 0x70, | ||
813 | HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601 = 0x00, | ||
814 | HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC709 = 0x10, | ||
815 | HDMI_FC_AVICONF2_EXT_COLORIMETRY_SYCC601 = 0x20, | ||
816 | HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_YCC601 = 0x30, | ||
817 | HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_RGB = 0x40, | ||
818 | HDMI_FC_AVICONF2_IT_CONTENT_MASK = 0x80, | ||
819 | HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA = 0x00, | ||
820 | HDMI_FC_AVICONF2_IT_CONTENT_VALID = 0x80, | ||
821 | |||
822 | HDMI_FC_AVICONF3_IT_CONTENT_TYPE_MASK = 0x03, | ||
823 | HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS = 0x00, | ||
824 | HDMI_FC_AVICONF3_IT_CONTENT_TYPE_PHOTO = 0x01, | ||
825 | HDMI_FC_AVICONF3_IT_CONTENT_TYPE_CINEMA = 0x02, | ||
826 | HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GAME = 0x03, | ||
827 | HDMI_FC_AVICONF3_QUANT_RANGE_MASK = 0x0C, | ||
828 | HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00, | ||
829 | HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04, | ||
830 | |||
831 | /* FC_DBGFORCE field values */ | ||
832 | HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10, | ||
833 | HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1, | ||
834 | |||
835 | /* PHY_CONF0 field values */ | ||
836 | HDMI_PHY_CONF0_PDZ_MASK = 0x80, | ||
837 | HDMI_PHY_CONF0_PDZ_OFFSET = 7, | ||
838 | HDMI_PHY_CONF0_ENTMDS_MASK = 0x40, | ||
839 | HDMI_PHY_CONF0_ENTMDS_OFFSET = 6, | ||
840 | HDMI_PHY_CONF0_SPARECTRL = 0x20, | ||
841 | HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10, | ||
842 | HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4, | ||
843 | HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8, | ||
844 | HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET = 3, | ||
845 | HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK = 0x4, | ||
846 | HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET = 2, | ||
847 | HDMI_PHY_CONF0_SELDATAENPOL_MASK = 0x2, | ||
848 | HDMI_PHY_CONF0_SELDATAENPOL_OFFSET = 1, | ||
849 | HDMI_PHY_CONF0_SELDIPIF_MASK = 0x1, | ||
850 | HDMI_PHY_CONF0_SELDIPIF_OFFSET = 0, | ||
851 | |||
852 | /* PHY_TST0 field values */ | ||
853 | HDMI_PHY_TST0_TSTCLR_MASK = 0x20, | ||
854 | HDMI_PHY_TST0_TSTCLR_OFFSET = 5, | ||
855 | HDMI_PHY_TST0_TSTEN_MASK = 0x10, | ||
856 | HDMI_PHY_TST0_TSTEN_OFFSET = 4, | ||
857 | HDMI_PHY_TST0_TSTCLK_MASK = 0x1, | ||
858 | HDMI_PHY_TST0_TSTCLK_OFFSET = 0, | ||
859 | |||
860 | /* PHY_STAT0 field values */ | ||
861 | HDMI_PHY_RX_SENSE3 = 0x80, | ||
862 | HDMI_PHY_RX_SENSE2 = 0x40, | ||
863 | HDMI_PHY_RX_SENSE1 = 0x20, | ||
864 | HDMI_PHY_RX_SENSE0 = 0x10, | ||
865 | HDMI_PHY_HPD = 0x02, | ||
866 | HDMI_PHY_TX_PHY_LOCK = 0x01, | ||
867 | |||
868 | /* PHY_I2CM_SLAVE_ADDR field values */ | ||
869 | HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 = 0x69, | ||
870 | HDMI_PHY_I2CM_SLAVE_ADDR_HEAC_PHY = 0x49, | ||
871 | |||
872 | /* PHY_I2CM_OPERATION_ADDR field values */ | ||
873 | HDMI_PHY_I2CM_OPERATION_ADDR_WRITE = 0x10, | ||
874 | HDMI_PHY_I2CM_OPERATION_ADDR_READ = 0x1, | ||
875 | |||
876 | /* HDMI_PHY_I2CM_INT_ADDR */ | ||
877 | HDMI_PHY_I2CM_INT_ADDR_DONE_POL = 0x08, | ||
878 | HDMI_PHY_I2CM_INT_ADDR_DONE_MASK = 0x04, | ||
879 | |||
880 | /* HDMI_PHY_I2CM_CTLINT_ADDR */ | ||
881 | HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL = 0x80, | ||
882 | HDMI_PHY_I2CM_CTLINT_ADDR_NAC_MASK = 0x40, | ||
883 | HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL = 0x08, | ||
884 | HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_MASK = 0x04, | ||
885 | |||
886 | /* AUD_CTS3 field values */ | ||
887 | HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5, | ||
888 | HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0, | ||
889 | HDMI_AUD_CTS3_N_SHIFT_1 = 0, | ||
890 | HDMI_AUD_CTS3_N_SHIFT_16 = 0x20, | ||
891 | HDMI_AUD_CTS3_N_SHIFT_32 = 0x40, | ||
892 | HDMI_AUD_CTS3_N_SHIFT_64 = 0x60, | ||
893 | HDMI_AUD_CTS3_N_SHIFT_128 = 0x80, | ||
894 | HDMI_AUD_CTS3_N_SHIFT_256 = 0xa0, | ||
895 | /* note that the CTS3 MANUAL bit has been removed | ||
896 | from our part. Can't set it, will read as 0. */ | ||
897 | HDMI_AUD_CTS3_CTS_MANUAL = 0x10, | ||
898 | HDMI_AUD_CTS3_AUDCTS19_16_MASK = 0x0f, | ||
899 | |||
900 | /* AHB_DMA_CONF0 field values */ | ||
901 | HDMI_AHB_DMA_CONF0_SW_FIFO_RST_OFFSET = 7, | ||
902 | HDMI_AHB_DMA_CONF0_SW_FIFO_RST_MASK = 0x80, | ||
903 | HDMI_AHB_DMA_CONF0_HBR = 0x10, | ||
904 | HDMI_AHB_DMA_CONF0_EN_HLOCK_OFFSET = 3, | ||
905 | HDMI_AHB_DMA_CONF0_EN_HLOCK_MASK = 0x08, | ||
906 | HDMI_AHB_DMA_CONF0_INCR_TYPE_OFFSET = 1, | ||
907 | HDMI_AHB_DMA_CONF0_INCR_TYPE_MASK = 0x06, | ||
908 | HDMI_AHB_DMA_CONF0_INCR4 = 0x0, | ||
909 | HDMI_AHB_DMA_CONF0_INCR8 = 0x2, | ||
910 | HDMI_AHB_DMA_CONF0_INCR16 = 0x4, | ||
911 | HDMI_AHB_DMA_CONF0_BURST_MODE = 0x1, | ||
912 | |||
913 | /* HDMI_AHB_DMA_START field values */ | ||
914 | HDMI_AHB_DMA_START_START_OFFSET = 0, | ||
915 | HDMI_AHB_DMA_START_START_MASK = 0x01, | ||
916 | |||
917 | /* HDMI_AHB_DMA_STOP field values */ | ||
918 | HDMI_AHB_DMA_STOP_STOP_OFFSET = 0, | ||
919 | HDMI_AHB_DMA_STOP_STOP_MASK = 0x01, | ||
920 | |||
921 | /* AHB_DMA_STAT, AHB_DMA_INT, AHB_DMA_MASK, AHB_DMA_POL field values */ | ||
922 | HDMI_AHB_DMA_DONE = 0x80, | ||
923 | HDMI_AHB_DMA_RETRY_SPLIT = 0x40, | ||
924 | HDMI_AHB_DMA_LOSTOWNERSHIP = 0x20, | ||
925 | HDMI_AHB_DMA_ERROR = 0x10, | ||
926 | HDMI_AHB_DMA_FIFO_THREMPTY = 0x04, | ||
927 | HDMI_AHB_DMA_FIFO_FULL = 0x02, | ||
928 | HDMI_AHB_DMA_FIFO_EMPTY = 0x01, | ||
929 | |||
930 | /* AHB_DMA_BUFFSTAT, AHB_DMA_BUFFINT,AHB_DMA_BUFFMASK,AHB_DMA_BUFFPOL values */ | ||
931 | HDMI_AHB_DMA_BUFFSTAT_FULL = 0x02, | ||
932 | HDMI_AHB_DMA_BUFFSTAT_EMPTY = 0x01, | ||
933 | |||
934 | /* MC_CLKDIS field values */ | ||
935 | HDMI_MC_CLKDIS_HDCPCLK_DISABLE = 0x40, | ||
936 | HDMI_MC_CLKDIS_CECCLK_DISABLE = 0x20, | ||
937 | HDMI_MC_CLKDIS_CSCCLK_DISABLE = 0x10, | ||
938 | HDMI_MC_CLKDIS_AUDCLK_DISABLE = 0x8, | ||
939 | HDMI_MC_CLKDIS_PREPCLK_DISABLE = 0x4, | ||
940 | HDMI_MC_CLKDIS_TMDSCLK_DISABLE = 0x2, | ||
941 | HDMI_MC_CLKDIS_PIXELCLK_DISABLE = 0x1, | ||
942 | |||
943 | /* MC_SWRSTZ field values */ | ||
944 | HDMI_MC_SWRSTZ_TMDSSWRST_REQ = 0x02, | ||
945 | |||
946 | /* MC_FLOWCTRL field values */ | ||
947 | HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_MASK = 0x1, | ||
948 | HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH = 0x1, | ||
949 | HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS = 0x0, | ||
950 | |||
951 | /* MC_PHYRSTZ field values */ | ||
952 | HDMI_MC_PHYRSTZ_ASSERT = 0x0, | ||
953 | HDMI_MC_PHYRSTZ_DEASSERT = 0x1, | ||
954 | |||
955 | /* MC_HEACPHY_RST field values */ | ||
956 | HDMI_MC_HEACPHY_RST_ASSERT = 0x1, | ||
957 | HDMI_MC_HEACPHY_RST_DEASSERT = 0x0, | ||
958 | |||
959 | /* CSC_CFG field values */ | ||
960 | HDMI_CSC_CFG_INTMODE_MASK = 0x30, | ||
961 | HDMI_CSC_CFG_INTMODE_OFFSET = 4, | ||
962 | HDMI_CSC_CFG_INTMODE_DISABLE = 0x00, | ||
963 | HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1 = 0x10, | ||
964 | HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA2 = 0x20, | ||
965 | HDMI_CSC_CFG_DECMODE_MASK = 0x3, | ||
966 | HDMI_CSC_CFG_DECMODE_OFFSET = 0, | ||
967 | HDMI_CSC_CFG_DECMODE_DISABLE = 0x0, | ||
968 | HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA1 = 0x1, | ||
969 | HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA2 = 0x2, | ||
970 | HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3 = 0x3, | ||
971 | |||
972 | /* CSC_SCALE field values */ | ||
973 | HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK = 0xF0, | ||
974 | HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP = 0x00, | ||
975 | HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP = 0x50, | ||
976 | HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP = 0x60, | ||
977 | HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP = 0x70, | ||
978 | HDMI_CSC_SCALE_CSCSCALE_MASK = 0x03, | ||
979 | |||
980 | /* A_HDCPCFG0 field values */ | ||
981 | HDMI_A_HDCPCFG0_ELVENA_MASK = 0x80, | ||
982 | HDMI_A_HDCPCFG0_ELVENA_ENABLE = 0x80, | ||
983 | HDMI_A_HDCPCFG0_ELVENA_DISABLE = 0x00, | ||
984 | HDMI_A_HDCPCFG0_I2CFASTMODE_MASK = 0x40, | ||
985 | HDMI_A_HDCPCFG0_I2CFASTMODE_ENABLE = 0x40, | ||
986 | HDMI_A_HDCPCFG0_I2CFASTMODE_DISABLE = 0x00, | ||
987 | HDMI_A_HDCPCFG0_BYPENCRYPTION_MASK = 0x20, | ||
988 | HDMI_A_HDCPCFG0_BYPENCRYPTION_ENABLE = 0x20, | ||
989 | HDMI_A_HDCPCFG0_BYPENCRYPTION_DISABLE = 0x00, | ||
990 | HDMI_A_HDCPCFG0_SYNCRICHECK_MASK = 0x10, | ||
991 | HDMI_A_HDCPCFG0_SYNCRICHECK_ENABLE = 0x10, | ||
992 | HDMI_A_HDCPCFG0_SYNCRICHECK_DISABLE = 0x00, | ||
993 | HDMI_A_HDCPCFG0_AVMUTE_MASK = 0x8, | ||
994 | HDMI_A_HDCPCFG0_AVMUTE_ENABLE = 0x8, | ||
995 | HDMI_A_HDCPCFG0_AVMUTE_DISABLE = 0x0, | ||
996 | HDMI_A_HDCPCFG0_RXDETECT_MASK = 0x4, | ||
997 | HDMI_A_HDCPCFG0_RXDETECT_ENABLE = 0x4, | ||
998 | HDMI_A_HDCPCFG0_RXDETECT_DISABLE = 0x0, | ||
999 | HDMI_A_HDCPCFG0_EN11FEATURE_MASK = 0x2, | ||
1000 | HDMI_A_HDCPCFG0_EN11FEATURE_ENABLE = 0x2, | ||
1001 | HDMI_A_HDCPCFG0_EN11FEATURE_DISABLE = 0x0, | ||
1002 | HDMI_A_HDCPCFG0_HDMIDVI_MASK = 0x1, | ||
1003 | HDMI_A_HDCPCFG0_HDMIDVI_HDMI = 0x1, | ||
1004 | HDMI_A_HDCPCFG0_HDMIDVI_DVI = 0x0, | ||
1005 | |||
1006 | /* A_HDCPCFG1 field values */ | ||
1007 | HDMI_A_HDCPCFG1_DISSHA1CHECK_MASK = 0x8, | ||
1008 | HDMI_A_HDCPCFG1_DISSHA1CHECK_DISABLE = 0x8, | ||
1009 | HDMI_A_HDCPCFG1_DISSHA1CHECK_ENABLE = 0x0, | ||
1010 | HDMI_A_HDCPCFG1_PH2UPSHFTENC_MASK = 0x4, | ||
1011 | HDMI_A_HDCPCFG1_PH2UPSHFTENC_ENABLE = 0x4, | ||
1012 | HDMI_A_HDCPCFG1_PH2UPSHFTENC_DISABLE = 0x0, | ||
1013 | HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK = 0x2, | ||
1014 | HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE = 0x2, | ||
1015 | HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_ENABLE = 0x0, | ||
1016 | HDMI_A_HDCPCFG1_SWRESET_MASK = 0x1, | ||
1017 | HDMI_A_HDCPCFG1_SWRESET_ASSERT = 0x0, | ||
1018 | |||
1019 | /* A_VIDPOLCFG field values */ | ||
1020 | HDMI_A_VIDPOLCFG_UNENCRYPTCONF_MASK = 0x60, | ||
1021 | HDMI_A_VIDPOLCFG_UNENCRYPTCONF_OFFSET = 5, | ||
1022 | HDMI_A_VIDPOLCFG_DATAENPOL_MASK = 0x10, | ||
1023 | HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH = 0x10, | ||
1024 | HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW = 0x0, | ||
1025 | HDMI_A_VIDPOLCFG_VSYNCPOL_MASK = 0x8, | ||
1026 | HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH = 0x8, | ||
1027 | HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW = 0x0, | ||
1028 | HDMI_A_VIDPOLCFG_HSYNCPOL_MASK = 0x2, | ||
1029 | HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH = 0x2, | ||
1030 | HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW = 0x0, | ||
1031 | }; | ||
1032 | #endif /* __IMX_HDMI_H__ */ | ||
diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c new file mode 100644 index 000000000000..2638dc1671d0 --- /dev/null +++ b/drivers/gpu/drm/imx/imx-ldb.c | |||
@@ -0,0 +1,615 @@ | |||
1 | /* | ||
2 | * i.MX drm driver - LVDS display bridge | ||
3 | * | ||
4 | * Copyright (C) 2012 Sascha Hauer, Pengutronix | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version 2 | ||
9 | * of the License, or (at your option) any later version. | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
18 | * MA 02110-1301, USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/module.h> | ||
22 | #include <linux/clk.h> | ||
23 | #include <linux/component.h> | ||
24 | #include <drm/drmP.h> | ||
25 | #include <drm/drm_fb_helper.h> | ||
26 | #include <drm/drm_crtc_helper.h> | ||
27 | #include <linux/mfd/syscon.h> | ||
28 | #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> | ||
29 | #include <linux/of_address.h> | ||
30 | #include <linux/of_device.h> | ||
31 | #include <video/of_videomode.h> | ||
32 | #include <linux/regmap.h> | ||
33 | #include <linux/videodev2.h> | ||
34 | |||
35 | #include "imx-drm.h" | ||
36 | |||
37 | #define DRIVER_NAME "imx-ldb" | ||
38 | |||
39 | #define LDB_CH0_MODE_EN_TO_DI0 (1 << 0) | ||
40 | #define LDB_CH0_MODE_EN_TO_DI1 (3 << 0) | ||
41 | #define LDB_CH0_MODE_EN_MASK (3 << 0) | ||
42 | #define LDB_CH1_MODE_EN_TO_DI0 (1 << 2) | ||
43 | #define LDB_CH1_MODE_EN_TO_DI1 (3 << 2) | ||
44 | #define LDB_CH1_MODE_EN_MASK (3 << 2) | ||
45 | #define LDB_SPLIT_MODE_EN (1 << 4) | ||
46 | #define LDB_DATA_WIDTH_CH0_24 (1 << 5) | ||
47 | #define LDB_BIT_MAP_CH0_JEIDA (1 << 6) | ||
48 | #define LDB_DATA_WIDTH_CH1_24 (1 << 7) | ||
49 | #define LDB_BIT_MAP_CH1_JEIDA (1 << 8) | ||
50 | #define LDB_DI0_VS_POL_ACT_LOW (1 << 9) | ||
51 | #define LDB_DI1_VS_POL_ACT_LOW (1 << 10) | ||
52 | #define LDB_BGREF_RMODE_INT (1 << 15) | ||
53 | |||
54 | #define con_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, connector) | ||
55 | #define enc_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, encoder) | ||
56 | |||
57 | struct imx_ldb; | ||
58 | |||
59 | struct imx_ldb_channel { | ||
60 | struct imx_ldb *ldb; | ||
61 | struct drm_connector connector; | ||
62 | struct drm_encoder encoder; | ||
63 | struct device_node *child; | ||
64 | int chno; | ||
65 | void *edid; | ||
66 | int edid_len; | ||
67 | struct drm_display_mode mode; | ||
68 | int mode_valid; | ||
69 | }; | ||
70 | |||
71 | struct bus_mux { | ||
72 | int reg; | ||
73 | int shift; | ||
74 | int mask; | ||
75 | }; | ||
76 | |||
77 | struct imx_ldb { | ||
78 | struct regmap *regmap; | ||
79 | struct device *dev; | ||
80 | struct imx_ldb_channel channel[2]; | ||
81 | struct clk *clk[2]; /* our own clock */ | ||
82 | struct clk *clk_sel[4]; /* parent of display clock */ | ||
83 | struct clk *clk_pll[2]; /* upstream clock we can adjust */ | ||
84 | u32 ldb_ctrl; | ||
85 | const struct bus_mux *lvds_mux; | ||
86 | }; | ||
87 | |||
88 | static enum drm_connector_status imx_ldb_connector_detect( | ||
89 | struct drm_connector *connector, bool force) | ||
90 | { | ||
91 | return connector_status_connected; | ||
92 | } | ||
93 | |||
94 | static int imx_ldb_connector_get_modes(struct drm_connector *connector) | ||
95 | { | ||
96 | struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector); | ||
97 | int num_modes = 0; | ||
98 | |||
99 | if (imx_ldb_ch->edid) { | ||
100 | drm_mode_connector_update_edid_property(connector, | ||
101 | imx_ldb_ch->edid); | ||
102 | num_modes = drm_add_edid_modes(connector, imx_ldb_ch->edid); | ||
103 | } | ||
104 | |||
105 | if (imx_ldb_ch->mode_valid) { | ||
106 | struct drm_display_mode *mode; | ||
107 | |||
108 | mode = drm_mode_create(connector->dev); | ||
109 | if (!mode) | ||
110 | return -EINVAL; | ||
111 | drm_mode_copy(mode, &imx_ldb_ch->mode); | ||
112 | mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; | ||
113 | drm_mode_probed_add(connector, mode); | ||
114 | num_modes++; | ||
115 | } | ||
116 | |||
117 | return num_modes; | ||
118 | } | ||
119 | |||
120 | static struct drm_encoder *imx_ldb_connector_best_encoder( | ||
121 | struct drm_connector *connector) | ||
122 | { | ||
123 | struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector); | ||
124 | |||
125 | return &imx_ldb_ch->encoder; | ||
126 | } | ||
127 | |||
128 | static void imx_ldb_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
129 | { | ||
130 | } | ||
131 | |||
132 | static bool imx_ldb_encoder_mode_fixup(struct drm_encoder *encoder, | ||
133 | const struct drm_display_mode *mode, | ||
134 | struct drm_display_mode *adjusted_mode) | ||
135 | { | ||
136 | return true; | ||
137 | } | ||
138 | |||
139 | static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno, | ||
140 | unsigned long serial_clk, unsigned long di_clk) | ||
141 | { | ||
142 | int ret; | ||
143 | |||
144 | dev_dbg(ldb->dev, "%s: now: %ld want: %ld\n", __func__, | ||
145 | clk_get_rate(ldb->clk_pll[chno]), serial_clk); | ||
146 | clk_set_rate(ldb->clk_pll[chno], serial_clk); | ||
147 | |||
148 | dev_dbg(ldb->dev, "%s after: %ld\n", __func__, | ||
149 | clk_get_rate(ldb->clk_pll[chno])); | ||
150 | |||
151 | dev_dbg(ldb->dev, "%s: now: %ld want: %ld\n", __func__, | ||
152 | clk_get_rate(ldb->clk[chno]), | ||
153 | (long int)di_clk); | ||
154 | clk_set_rate(ldb->clk[chno], di_clk); | ||
155 | |||
156 | dev_dbg(ldb->dev, "%s after: %ld\n", __func__, | ||
157 | clk_get_rate(ldb->clk[chno])); | ||
158 | |||
159 | /* set display clock mux to LDB input clock */ | ||
160 | ret = clk_set_parent(ldb->clk_sel[mux], ldb->clk[chno]); | ||
161 | if (ret) | ||
162 | dev_err(ldb->dev, | ||
163 | "unable to set di%d parent clock to ldb_di%d\n", mux, | ||
164 | chno); | ||
165 | } | ||
166 | |||
167 | static void imx_ldb_encoder_prepare(struct drm_encoder *encoder) | ||
168 | { | ||
169 | struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); | ||
170 | struct imx_ldb *ldb = imx_ldb_ch->ldb; | ||
171 | struct drm_display_mode *mode = &encoder->crtc->mode; | ||
172 | u32 pixel_fmt; | ||
173 | unsigned long serial_clk; | ||
174 | unsigned long di_clk = mode->clock * 1000; | ||
175 | int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder); | ||
176 | |||
177 | if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { | ||
178 | /* dual channel LVDS mode */ | ||
179 | serial_clk = 3500UL * mode->clock; | ||
180 | imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk); | ||
181 | imx_ldb_set_clock(ldb, mux, 1, serial_clk, di_clk); | ||
182 | } else { | ||
183 | serial_clk = 7000UL * mode->clock; | ||
184 | imx_ldb_set_clock(ldb, mux, imx_ldb_ch->chno, serial_clk, | ||
185 | di_clk); | ||
186 | } | ||
187 | |||
188 | switch (imx_ldb_ch->chno) { | ||
189 | case 0: | ||
190 | pixel_fmt = (ldb->ldb_ctrl & LDB_DATA_WIDTH_CH0_24) ? | ||
191 | V4L2_PIX_FMT_RGB24 : V4L2_PIX_FMT_BGR666; | ||
192 | break; | ||
193 | case 1: | ||
194 | pixel_fmt = (ldb->ldb_ctrl & LDB_DATA_WIDTH_CH1_24) ? | ||
195 | V4L2_PIX_FMT_RGB24 : V4L2_PIX_FMT_BGR666; | ||
196 | break; | ||
197 | default: | ||
198 | dev_err(ldb->dev, "unable to config di%d panel format\n", | ||
199 | imx_ldb_ch->chno); | ||
200 | pixel_fmt = V4L2_PIX_FMT_RGB24; | ||
201 | } | ||
202 | |||
203 | imx_drm_panel_format(encoder, pixel_fmt); | ||
204 | } | ||
205 | |||
206 | static void imx_ldb_encoder_commit(struct drm_encoder *encoder) | ||
207 | { | ||
208 | struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); | ||
209 | struct imx_ldb *ldb = imx_ldb_ch->ldb; | ||
210 | int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; | ||
211 | int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder); | ||
212 | |||
213 | if (dual) { | ||
214 | clk_prepare_enable(ldb->clk[0]); | ||
215 | clk_prepare_enable(ldb->clk[1]); | ||
216 | } | ||
217 | |||
218 | if (imx_ldb_ch == &ldb->channel[0] || dual) { | ||
219 | ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK; | ||
220 | if (mux == 0 || ldb->lvds_mux) | ||
221 | ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI0; | ||
222 | else if (mux == 1) | ||
223 | ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI1; | ||
224 | } | ||
225 | if (imx_ldb_ch == &ldb->channel[1] || dual) { | ||
226 | ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK; | ||
227 | if (mux == 1 || ldb->lvds_mux) | ||
228 | ldb->ldb_ctrl |= LDB_CH1_MODE_EN_TO_DI1; | ||
229 | else if (mux == 0) | ||
230 | ldb->ldb_ctrl |= LDB_CH1_MODE_EN_TO_DI0; | ||
231 | } | ||
232 | |||
233 | if (ldb->lvds_mux) { | ||
234 | const struct bus_mux *lvds_mux = NULL; | ||
235 | |||
236 | if (imx_ldb_ch == &ldb->channel[0]) | ||
237 | lvds_mux = &ldb->lvds_mux[0]; | ||
238 | else if (imx_ldb_ch == &ldb->channel[1]) | ||
239 | lvds_mux = &ldb->lvds_mux[1]; | ||
240 | |||
241 | regmap_update_bits(ldb->regmap, lvds_mux->reg, lvds_mux->mask, | ||
242 | mux << lvds_mux->shift); | ||
243 | } | ||
244 | |||
245 | regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl); | ||
246 | } | ||
247 | |||
248 | static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder, | ||
249 | struct drm_display_mode *mode, | ||
250 | struct drm_display_mode *adjusted_mode) | ||
251 | { | ||
252 | struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); | ||
253 | struct imx_ldb *ldb = imx_ldb_ch->ldb; | ||
254 | int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; | ||
255 | |||
256 | if (mode->clock > 170000) { | ||
257 | dev_warn(ldb->dev, | ||
258 | "%s: mode exceeds 170 MHz pixel clock\n", __func__); | ||
259 | } | ||
260 | if (mode->clock > 85000 && !dual) { | ||
261 | dev_warn(ldb->dev, | ||
262 | "%s: mode exceeds 85 MHz pixel clock\n", __func__); | ||
263 | } | ||
264 | |||
265 | /* FIXME - assumes straight connections DI0 --> CH0, DI1 --> CH1 */ | ||
266 | if (imx_ldb_ch == &ldb->channel[0]) { | ||
267 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | ||
268 | ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW; | ||
269 | else if (mode->flags & DRM_MODE_FLAG_PVSYNC) | ||
270 | ldb->ldb_ctrl &= ~LDB_DI0_VS_POL_ACT_LOW; | ||
271 | } | ||
272 | if (imx_ldb_ch == &ldb->channel[1]) { | ||
273 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | ||
274 | ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW; | ||
275 | else if (mode->flags & DRM_MODE_FLAG_PVSYNC) | ||
276 | ldb->ldb_ctrl &= ~LDB_DI1_VS_POL_ACT_LOW; | ||
277 | } | ||
278 | } | ||
279 | |||
280 | static void imx_ldb_encoder_disable(struct drm_encoder *encoder) | ||
281 | { | ||
282 | struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); | ||
283 | struct imx_ldb *ldb = imx_ldb_ch->ldb; | ||
284 | |||
285 | /* | ||
286 | * imx_ldb_encoder_disable is called by | ||
287 | * drm_helper_disable_unused_functions without | ||
288 | * the encoder being enabled before. | ||
289 | */ | ||
290 | if (imx_ldb_ch == &ldb->channel[0] && | ||
291 | (ldb->ldb_ctrl & LDB_CH0_MODE_EN_MASK) == 0) | ||
292 | return; | ||
293 | else if (imx_ldb_ch == &ldb->channel[1] && | ||
294 | (ldb->ldb_ctrl & LDB_CH1_MODE_EN_MASK) == 0) | ||
295 | return; | ||
296 | |||
297 | if (imx_ldb_ch == &ldb->channel[0]) | ||
298 | ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK; | ||
299 | else if (imx_ldb_ch == &ldb->channel[1]) | ||
300 | ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK; | ||
301 | |||
302 | regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl); | ||
303 | |||
304 | if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { | ||
305 | clk_disable_unprepare(ldb->clk[0]); | ||
306 | clk_disable_unprepare(ldb->clk[1]); | ||
307 | } | ||
308 | } | ||
309 | |||
310 | static struct drm_connector_funcs imx_ldb_connector_funcs = { | ||
311 | .dpms = drm_helper_connector_dpms, | ||
312 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
313 | .detect = imx_ldb_connector_detect, | ||
314 | .destroy = imx_drm_connector_destroy, | ||
315 | }; | ||
316 | |||
317 | static struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = { | ||
318 | .get_modes = imx_ldb_connector_get_modes, | ||
319 | .best_encoder = imx_ldb_connector_best_encoder, | ||
320 | }; | ||
321 | |||
322 | static struct drm_encoder_funcs imx_ldb_encoder_funcs = { | ||
323 | .destroy = imx_drm_encoder_destroy, | ||
324 | }; | ||
325 | |||
326 | static struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = { | ||
327 | .dpms = imx_ldb_encoder_dpms, | ||
328 | .mode_fixup = imx_ldb_encoder_mode_fixup, | ||
329 | .prepare = imx_ldb_encoder_prepare, | ||
330 | .commit = imx_ldb_encoder_commit, | ||
331 | .mode_set = imx_ldb_encoder_mode_set, | ||
332 | .disable = imx_ldb_encoder_disable, | ||
333 | }; | ||
334 | |||
335 | static int imx_ldb_get_clk(struct imx_ldb *ldb, int chno) | ||
336 | { | ||
337 | char clkname[16]; | ||
338 | |||
339 | snprintf(clkname, sizeof(clkname), "di%d", chno); | ||
340 | ldb->clk[chno] = devm_clk_get(ldb->dev, clkname); | ||
341 | if (IS_ERR(ldb->clk[chno])) | ||
342 | return PTR_ERR(ldb->clk[chno]); | ||
343 | |||
344 | snprintf(clkname, sizeof(clkname), "di%d_pll", chno); | ||
345 | ldb->clk_pll[chno] = devm_clk_get(ldb->dev, clkname); | ||
346 | |||
347 | return PTR_ERR_OR_ZERO(ldb->clk_pll[chno]); | ||
348 | } | ||
349 | |||
350 | static int imx_ldb_register(struct drm_device *drm, | ||
351 | struct imx_ldb_channel *imx_ldb_ch) | ||
352 | { | ||
353 | struct imx_ldb *ldb = imx_ldb_ch->ldb; | ||
354 | int ret; | ||
355 | |||
356 | ret = imx_drm_encoder_parse_of(drm, &imx_ldb_ch->encoder, | ||
357 | imx_ldb_ch->child); | ||
358 | if (ret) | ||
359 | return ret; | ||
360 | |||
361 | ret = imx_ldb_get_clk(ldb, imx_ldb_ch->chno); | ||
362 | if (ret) | ||
363 | return ret; | ||
364 | |||
365 | if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { | ||
366 | ret = imx_ldb_get_clk(ldb, 1); | ||
367 | if (ret) | ||
368 | return ret; | ||
369 | } | ||
370 | |||
371 | drm_encoder_helper_add(&imx_ldb_ch->encoder, | ||
372 | &imx_ldb_encoder_helper_funcs); | ||
373 | drm_encoder_init(drm, &imx_ldb_ch->encoder, &imx_ldb_encoder_funcs, | ||
374 | DRM_MODE_ENCODER_LVDS); | ||
375 | |||
376 | drm_connector_helper_add(&imx_ldb_ch->connector, | ||
377 | &imx_ldb_connector_helper_funcs); | ||
378 | drm_connector_init(drm, &imx_ldb_ch->connector, | ||
379 | &imx_ldb_connector_funcs, DRM_MODE_CONNECTOR_LVDS); | ||
380 | |||
381 | drm_mode_connector_attach_encoder(&imx_ldb_ch->connector, | ||
382 | &imx_ldb_ch->encoder); | ||
383 | |||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | enum { | ||
388 | LVDS_BIT_MAP_SPWG, | ||
389 | LVDS_BIT_MAP_JEIDA | ||
390 | }; | ||
391 | |||
392 | static const char * const imx_ldb_bit_mappings[] = { | ||
393 | [LVDS_BIT_MAP_SPWG] = "spwg", | ||
394 | [LVDS_BIT_MAP_JEIDA] = "jeida", | ||
395 | }; | ||
396 | |||
397 | static const int of_get_data_mapping(struct device_node *np) | ||
398 | { | ||
399 | const char *bm; | ||
400 | int ret, i; | ||
401 | |||
402 | ret = of_property_read_string(np, "fsl,data-mapping", &bm); | ||
403 | if (ret < 0) | ||
404 | return ret; | ||
405 | |||
406 | for (i = 0; i < ARRAY_SIZE(imx_ldb_bit_mappings); i++) | ||
407 | if (!strcasecmp(bm, imx_ldb_bit_mappings[i])) | ||
408 | return i; | ||
409 | |||
410 | return -EINVAL; | ||
411 | } | ||
412 | |||
413 | static struct bus_mux imx6q_lvds_mux[2] = { | ||
414 | { | ||
415 | .reg = IOMUXC_GPR3, | ||
416 | .shift = 6, | ||
417 | .mask = IMX6Q_GPR3_LVDS0_MUX_CTL_MASK, | ||
418 | }, { | ||
419 | .reg = IOMUXC_GPR3, | ||
420 | .shift = 8, | ||
421 | .mask = IMX6Q_GPR3_LVDS1_MUX_CTL_MASK, | ||
422 | } | ||
423 | }; | ||
424 | |||
425 | /* | ||
426 | * For a device declaring compatible = "fsl,imx6q-ldb", "fsl,imx53-ldb", | ||
427 | * of_match_device will walk through this list and take the first entry | ||
428 | * matching any of its compatible values. Therefore, the more generic | ||
429 | * entries (in this case fsl,imx53-ldb) need to be ordered last. | ||
430 | */ | ||
431 | static const struct of_device_id imx_ldb_dt_ids[] = { | ||
432 | { .compatible = "fsl,imx6q-ldb", .data = imx6q_lvds_mux, }, | ||
433 | { .compatible = "fsl,imx53-ldb", .data = NULL, }, | ||
434 | { } | ||
435 | }; | ||
436 | MODULE_DEVICE_TABLE(of, imx_ldb_dt_ids); | ||
437 | |||
438 | static int imx_ldb_bind(struct device *dev, struct device *master, void *data) | ||
439 | { | ||
440 | struct drm_device *drm = data; | ||
441 | struct device_node *np = dev->of_node; | ||
442 | const struct of_device_id *of_id = | ||
443 | of_match_device(imx_ldb_dt_ids, dev); | ||
444 | struct device_node *child; | ||
445 | const u8 *edidp; | ||
446 | struct imx_ldb *imx_ldb; | ||
447 | int datawidth; | ||
448 | int mapping; | ||
449 | int dual; | ||
450 | int ret; | ||
451 | int i; | ||
452 | |||
453 | imx_ldb = devm_kzalloc(dev, sizeof(*imx_ldb), GFP_KERNEL); | ||
454 | if (!imx_ldb) | ||
455 | return -ENOMEM; | ||
456 | |||
457 | imx_ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr"); | ||
458 | if (IS_ERR(imx_ldb->regmap)) { | ||
459 | dev_err(dev, "failed to get parent regmap\n"); | ||
460 | return PTR_ERR(imx_ldb->regmap); | ||
461 | } | ||
462 | |||
463 | imx_ldb->dev = dev; | ||
464 | |||
465 | if (of_id) | ||
466 | imx_ldb->lvds_mux = of_id->data; | ||
467 | |||
468 | dual = of_property_read_bool(np, "fsl,dual-channel"); | ||
469 | if (dual) | ||
470 | imx_ldb->ldb_ctrl |= LDB_SPLIT_MODE_EN; | ||
471 | |||
472 | /* | ||
473 | * There are three different possible clock mux configurations: | ||
474 | * i.MX53: ipu1_di0_sel, ipu1_di1_sel | ||
475 | * i.MX6q: ipu1_di0_sel, ipu1_di1_sel, ipu2_di0_sel, ipu2_di1_sel | ||
476 | * i.MX6dl: ipu1_di0_sel, ipu1_di1_sel, lcdif_sel | ||
477 | * Map them all to di0_sel...di3_sel. | ||
478 | */ | ||
479 | for (i = 0; i < 4; i++) { | ||
480 | char clkname[16]; | ||
481 | |||
482 | sprintf(clkname, "di%d_sel", i); | ||
483 | imx_ldb->clk_sel[i] = devm_clk_get(imx_ldb->dev, clkname); | ||
484 | if (IS_ERR(imx_ldb->clk_sel[i])) { | ||
485 | ret = PTR_ERR(imx_ldb->clk_sel[i]); | ||
486 | imx_ldb->clk_sel[i] = NULL; | ||
487 | break; | ||
488 | } | ||
489 | } | ||
490 | if (i == 0) | ||
491 | return ret; | ||
492 | |||
493 | for_each_child_of_node(np, child) { | ||
494 | struct imx_ldb_channel *channel; | ||
495 | |||
496 | ret = of_property_read_u32(child, "reg", &i); | ||
497 | if (ret || i < 0 || i > 1) | ||
498 | return -EINVAL; | ||
499 | |||
500 | if (dual && i > 0) { | ||
501 | dev_warn(dev, "dual-channel mode, ignoring second output\n"); | ||
502 | continue; | ||
503 | } | ||
504 | |||
505 | if (!of_device_is_available(child)) | ||
506 | continue; | ||
507 | |||
508 | channel = &imx_ldb->channel[i]; | ||
509 | channel->ldb = imx_ldb; | ||
510 | channel->chno = i; | ||
511 | channel->child = child; | ||
512 | |||
513 | edidp = of_get_property(child, "edid", &channel->edid_len); | ||
514 | if (edidp) { | ||
515 | channel->edid = kmemdup(edidp, channel->edid_len, | ||
516 | GFP_KERNEL); | ||
517 | } else { | ||
518 | ret = of_get_drm_display_mode(child, &channel->mode, 0); | ||
519 | if (!ret) | ||
520 | channel->mode_valid = 1; | ||
521 | } | ||
522 | |||
523 | ret = of_property_read_u32(child, "fsl,data-width", &datawidth); | ||
524 | if (ret) | ||
525 | datawidth = 0; | ||
526 | else if (datawidth != 18 && datawidth != 24) | ||
527 | return -EINVAL; | ||
528 | |||
529 | mapping = of_get_data_mapping(child); | ||
530 | switch (mapping) { | ||
531 | case LVDS_BIT_MAP_SPWG: | ||
532 | if (datawidth == 24) { | ||
533 | if (i == 0 || dual) | ||
534 | imx_ldb->ldb_ctrl |= | ||
535 | LDB_DATA_WIDTH_CH0_24; | ||
536 | if (i == 1 || dual) | ||
537 | imx_ldb->ldb_ctrl |= | ||
538 | LDB_DATA_WIDTH_CH1_24; | ||
539 | } | ||
540 | break; | ||
541 | case LVDS_BIT_MAP_JEIDA: | ||
542 | if (datawidth == 18) { | ||
543 | dev_err(dev, "JEIDA standard only supported in 24 bit\n"); | ||
544 | return -EINVAL; | ||
545 | } | ||
546 | if (i == 0 || dual) | ||
547 | imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 | | ||
548 | LDB_BIT_MAP_CH0_JEIDA; | ||
549 | if (i == 1 || dual) | ||
550 | imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 | | ||
551 | LDB_BIT_MAP_CH1_JEIDA; | ||
552 | break; | ||
553 | default: | ||
554 | dev_err(dev, "data mapping not specified or invalid\n"); | ||
555 | return -EINVAL; | ||
556 | } | ||
557 | |||
558 | ret = imx_ldb_register(drm, channel); | ||
559 | if (ret) | ||
560 | return ret; | ||
561 | } | ||
562 | |||
563 | dev_set_drvdata(dev, imx_ldb); | ||
564 | |||
565 | return 0; | ||
566 | } | ||
567 | |||
568 | static void imx_ldb_unbind(struct device *dev, struct device *master, | ||
569 | void *data) | ||
570 | { | ||
571 | struct imx_ldb *imx_ldb = dev_get_drvdata(dev); | ||
572 | int i; | ||
573 | |||
574 | for (i = 0; i < 2; i++) { | ||
575 | struct imx_ldb_channel *channel = &imx_ldb->channel[i]; | ||
576 | |||
577 | if (!channel->connector.funcs) | ||
578 | continue; | ||
579 | |||
580 | channel->connector.funcs->destroy(&channel->connector); | ||
581 | channel->encoder.funcs->destroy(&channel->encoder); | ||
582 | } | ||
583 | } | ||
584 | |||
585 | static const struct component_ops imx_ldb_ops = { | ||
586 | .bind = imx_ldb_bind, | ||
587 | .unbind = imx_ldb_unbind, | ||
588 | }; | ||
589 | |||
590 | static int imx_ldb_probe(struct platform_device *pdev) | ||
591 | { | ||
592 | return component_add(&pdev->dev, &imx_ldb_ops); | ||
593 | } | ||
594 | |||
595 | static int imx_ldb_remove(struct platform_device *pdev) | ||
596 | { | ||
597 | component_del(&pdev->dev, &imx_ldb_ops); | ||
598 | return 0; | ||
599 | } | ||
600 | |||
601 | static struct platform_driver imx_ldb_driver = { | ||
602 | .probe = imx_ldb_probe, | ||
603 | .remove = imx_ldb_remove, | ||
604 | .driver = { | ||
605 | .of_match_table = imx_ldb_dt_ids, | ||
606 | .name = DRIVER_NAME, | ||
607 | }, | ||
608 | }; | ||
609 | |||
610 | module_platform_driver(imx_ldb_driver); | ||
611 | |||
612 | MODULE_DESCRIPTION("i.MX LVDS driver"); | ||
613 | MODULE_AUTHOR("Sascha Hauer, Pengutronix"); | ||
614 | MODULE_LICENSE("GPL"); | ||
615 | MODULE_ALIAS("platform:" DRIVER_NAME); | ||
diff --git a/drivers/gpu/drm/imx/imx-tve.c b/drivers/gpu/drm/imx/imx-tve.c new file mode 100644 index 000000000000..64b54d7f996c --- /dev/null +++ b/drivers/gpu/drm/imx/imx-tve.c | |||
@@ -0,0 +1,735 @@ | |||
1 | /* | ||
2 | * i.MX drm driver - Television Encoder (TVEv2) | ||
3 | * | ||
4 | * Copyright (C) 2013 Philipp Zabel, Pengutronix | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version 2 | ||
9 | * of the License, or (at your option) any later version. | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
18 | * MA 02110-1301, USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/clk.h> | ||
22 | #include <linux/clk-provider.h> | ||
23 | #include <linux/component.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/i2c.h> | ||
26 | #include <linux/regmap.h> | ||
27 | #include <linux/regulator/consumer.h> | ||
28 | #include <linux/spinlock.h> | ||
29 | #include <linux/videodev2.h> | ||
30 | #include <drm/drmP.h> | ||
31 | #include <drm/drm_fb_helper.h> | ||
32 | #include <drm/drm_crtc_helper.h> | ||
33 | #include <video/imx-ipu-v3.h> | ||
34 | |||
35 | #include "imx-drm.h" | ||
36 | |||
37 | #define TVE_COM_CONF_REG 0x00 | ||
38 | #define TVE_TVDAC0_CONT_REG 0x28 | ||
39 | #define TVE_TVDAC1_CONT_REG 0x2c | ||
40 | #define TVE_TVDAC2_CONT_REG 0x30 | ||
41 | #define TVE_CD_CONT_REG 0x34 | ||
42 | #define TVE_INT_CONT_REG 0x64 | ||
43 | #define TVE_STAT_REG 0x68 | ||
44 | #define TVE_TST_MODE_REG 0x6c | ||
45 | #define TVE_MV_CONT_REG 0xdc | ||
46 | |||
47 | /* TVE_COM_CONF_REG */ | ||
48 | #define TVE_SYNC_CH_2_EN BIT(22) | ||
49 | #define TVE_SYNC_CH_1_EN BIT(21) | ||
50 | #define TVE_SYNC_CH_0_EN BIT(20) | ||
51 | #define TVE_TV_OUT_MODE_MASK (0x7 << 12) | ||
52 | #define TVE_TV_OUT_DISABLE (0x0 << 12) | ||
53 | #define TVE_TV_OUT_CVBS_0 (0x1 << 12) | ||
54 | #define TVE_TV_OUT_CVBS_2 (0x2 << 12) | ||
55 | #define TVE_TV_OUT_CVBS_0_2 (0x3 << 12) | ||
56 | #define TVE_TV_OUT_SVIDEO_0_1 (0x4 << 12) | ||
57 | #define TVE_TV_OUT_SVIDEO_0_1_CVBS2_2 (0x5 << 12) | ||
58 | #define TVE_TV_OUT_YPBPR (0x6 << 12) | ||
59 | #define TVE_TV_OUT_RGB (0x7 << 12) | ||
60 | #define TVE_TV_STAND_MASK (0xf << 8) | ||
61 | #define TVE_TV_STAND_HD_1080P30 (0xc << 8) | ||
62 | #define TVE_P2I_CONV_EN BIT(7) | ||
63 | #define TVE_INP_VIDEO_FORM BIT(6) | ||
64 | #define TVE_INP_YCBCR_422 (0x0 << 6) | ||
65 | #define TVE_INP_YCBCR_444 (0x1 << 6) | ||
66 | #define TVE_DATA_SOURCE_MASK (0x3 << 4) | ||
67 | #define TVE_DATA_SOURCE_BUS1 (0x0 << 4) | ||
68 | #define TVE_DATA_SOURCE_BUS2 (0x1 << 4) | ||
69 | #define TVE_DATA_SOURCE_EXT (0x2 << 4) | ||
70 | #define TVE_DATA_SOURCE_TESTGEN (0x3 << 4) | ||
71 | #define TVE_IPU_CLK_EN_OFS 3 | ||
72 | #define TVE_IPU_CLK_EN BIT(3) | ||
73 | #define TVE_DAC_SAMP_RATE_OFS 1 | ||
74 | #define TVE_DAC_SAMP_RATE_WIDTH 2 | ||
75 | #define TVE_DAC_SAMP_RATE_MASK (0x3 << 1) | ||
76 | #define TVE_DAC_FULL_RATE (0x0 << 1) | ||
77 | #define TVE_DAC_DIV2_RATE (0x1 << 1) | ||
78 | #define TVE_DAC_DIV4_RATE (0x2 << 1) | ||
79 | #define TVE_EN BIT(0) | ||
80 | |||
81 | /* TVE_TVDACx_CONT_REG */ | ||
82 | #define TVE_TVDAC_GAIN_MASK (0x3f << 0) | ||
83 | |||
84 | /* TVE_CD_CONT_REG */ | ||
85 | #define TVE_CD_CH_2_SM_EN BIT(22) | ||
86 | #define TVE_CD_CH_1_SM_EN BIT(21) | ||
87 | #define TVE_CD_CH_0_SM_EN BIT(20) | ||
88 | #define TVE_CD_CH_2_LM_EN BIT(18) | ||
89 | #define TVE_CD_CH_1_LM_EN BIT(17) | ||
90 | #define TVE_CD_CH_0_LM_EN BIT(16) | ||
91 | #define TVE_CD_CH_2_REF_LVL BIT(10) | ||
92 | #define TVE_CD_CH_1_REF_LVL BIT(9) | ||
93 | #define TVE_CD_CH_0_REF_LVL BIT(8) | ||
94 | #define TVE_CD_EN BIT(0) | ||
95 | |||
96 | /* TVE_INT_CONT_REG */ | ||
97 | #define TVE_FRAME_END_IEN BIT(13) | ||
98 | #define TVE_CD_MON_END_IEN BIT(2) | ||
99 | #define TVE_CD_SM_IEN BIT(1) | ||
100 | #define TVE_CD_LM_IEN BIT(0) | ||
101 | |||
102 | /* TVE_TST_MODE_REG */ | ||
103 | #define TVE_TVDAC_TEST_MODE_MASK (0x7 << 0) | ||
104 | |||
105 | #define con_to_tve(x) container_of(x, struct imx_tve, connector) | ||
106 | #define enc_to_tve(x) container_of(x, struct imx_tve, encoder) | ||
107 | |||
108 | enum { | ||
109 | TVE_MODE_TVOUT, | ||
110 | TVE_MODE_VGA, | ||
111 | }; | ||
112 | |||
113 | struct imx_tve { | ||
114 | struct drm_connector connector; | ||
115 | struct drm_encoder encoder; | ||
116 | struct device *dev; | ||
117 | spinlock_t lock; /* register lock */ | ||
118 | bool enabled; | ||
119 | int mode; | ||
120 | |||
121 | struct regmap *regmap; | ||
122 | struct regulator *dac_reg; | ||
123 | struct i2c_adapter *ddc; | ||
124 | struct clk *clk; | ||
125 | struct clk *di_sel_clk; | ||
126 | struct clk_hw clk_hw_di; | ||
127 | struct clk *di_clk; | ||
128 | int vsync_pin; | ||
129 | int hsync_pin; | ||
130 | }; | ||
131 | |||
132 | static void tve_lock(void *__tve) | ||
133 | __acquires(&tve->lock) | ||
134 | { | ||
135 | struct imx_tve *tve = __tve; | ||
136 | |||
137 | spin_lock(&tve->lock); | ||
138 | } | ||
139 | |||
140 | static void tve_unlock(void *__tve) | ||
141 | __releases(&tve->lock) | ||
142 | { | ||
143 | struct imx_tve *tve = __tve; | ||
144 | |||
145 | spin_unlock(&tve->lock); | ||
146 | } | ||
147 | |||
148 | static void tve_enable(struct imx_tve *tve) | ||
149 | { | ||
150 | int ret; | ||
151 | |||
152 | if (!tve->enabled) { | ||
153 | tve->enabled = true; | ||
154 | clk_prepare_enable(tve->clk); | ||
155 | ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, | ||
156 | TVE_IPU_CLK_EN | TVE_EN, | ||
157 | TVE_IPU_CLK_EN | TVE_EN); | ||
158 | } | ||
159 | |||
160 | /* clear interrupt status register */ | ||
161 | regmap_write(tve->regmap, TVE_STAT_REG, 0xffffffff); | ||
162 | |||
163 | /* cable detection irq disabled in VGA mode, enabled in TVOUT mode */ | ||
164 | if (tve->mode == TVE_MODE_VGA) | ||
165 | regmap_write(tve->regmap, TVE_INT_CONT_REG, 0); | ||
166 | else | ||
167 | regmap_write(tve->regmap, TVE_INT_CONT_REG, | ||
168 | TVE_CD_SM_IEN | | ||
169 | TVE_CD_LM_IEN | | ||
170 | TVE_CD_MON_END_IEN); | ||
171 | } | ||
172 | |||
173 | static void tve_disable(struct imx_tve *tve) | ||
174 | { | ||
175 | int ret; | ||
176 | |||
177 | if (tve->enabled) { | ||
178 | tve->enabled = false; | ||
179 | ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, | ||
180 | TVE_IPU_CLK_EN | TVE_EN, 0); | ||
181 | clk_disable_unprepare(tve->clk); | ||
182 | } | ||
183 | } | ||
184 | |||
185 | static int tve_setup_tvout(struct imx_tve *tve) | ||
186 | { | ||
187 | return -ENOTSUPP; | ||
188 | } | ||
189 | |||
190 | static int tve_setup_vga(struct imx_tve *tve) | ||
191 | { | ||
192 | unsigned int mask; | ||
193 | unsigned int val; | ||
194 | int ret; | ||
195 | |||
196 | /* set gain to (1 + 10/128) to provide 0.7V peak-to-peak amplitude */ | ||
197 | ret = regmap_update_bits(tve->regmap, TVE_TVDAC0_CONT_REG, | ||
198 | TVE_TVDAC_GAIN_MASK, 0x0a); | ||
199 | ret = regmap_update_bits(tve->regmap, TVE_TVDAC1_CONT_REG, | ||
200 | TVE_TVDAC_GAIN_MASK, 0x0a); | ||
201 | ret = regmap_update_bits(tve->regmap, TVE_TVDAC2_CONT_REG, | ||
202 | TVE_TVDAC_GAIN_MASK, 0x0a); | ||
203 | |||
204 | /* set configuration register */ | ||
205 | mask = TVE_DATA_SOURCE_MASK | TVE_INP_VIDEO_FORM; | ||
206 | val = TVE_DATA_SOURCE_BUS2 | TVE_INP_YCBCR_444; | ||
207 | mask |= TVE_TV_STAND_MASK | TVE_P2I_CONV_EN; | ||
208 | val |= TVE_TV_STAND_HD_1080P30 | 0; | ||
209 | mask |= TVE_TV_OUT_MODE_MASK | TVE_SYNC_CH_0_EN; | ||
210 | val |= TVE_TV_OUT_RGB | TVE_SYNC_CH_0_EN; | ||
211 | ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, mask, val); | ||
212 | if (ret < 0) { | ||
213 | dev_err(tve->dev, "failed to set configuration: %d\n", ret); | ||
214 | return ret; | ||
215 | } | ||
216 | |||
217 | /* set test mode (as documented) */ | ||
218 | ret = regmap_update_bits(tve->regmap, TVE_TST_MODE_REG, | ||
219 | TVE_TVDAC_TEST_MODE_MASK, 1); | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static enum drm_connector_status imx_tve_connector_detect( | ||
225 | struct drm_connector *connector, bool force) | ||
226 | { | ||
227 | return connector_status_connected; | ||
228 | } | ||
229 | |||
230 | static int imx_tve_connector_get_modes(struct drm_connector *connector) | ||
231 | { | ||
232 | struct imx_tve *tve = con_to_tve(connector); | ||
233 | struct edid *edid; | ||
234 | int ret = 0; | ||
235 | |||
236 | if (!tve->ddc) | ||
237 | return 0; | ||
238 | |||
239 | edid = drm_get_edid(connector, tve->ddc); | ||
240 | if (edid) { | ||
241 | drm_mode_connector_update_edid_property(connector, edid); | ||
242 | ret = drm_add_edid_modes(connector, edid); | ||
243 | kfree(edid); | ||
244 | } | ||
245 | |||
246 | return ret; | ||
247 | } | ||
248 | |||
249 | static int imx_tve_connector_mode_valid(struct drm_connector *connector, | ||
250 | struct drm_display_mode *mode) | ||
251 | { | ||
252 | struct imx_tve *tve = con_to_tve(connector); | ||
253 | unsigned long rate; | ||
254 | |||
255 | /* pixel clock with 2x oversampling */ | ||
256 | rate = clk_round_rate(tve->clk, 2000UL * mode->clock) / 2000; | ||
257 | if (rate == mode->clock) | ||
258 | return MODE_OK; | ||
259 | |||
260 | /* pixel clock without oversampling */ | ||
261 | rate = clk_round_rate(tve->clk, 1000UL * mode->clock) / 1000; | ||
262 | if (rate == mode->clock) | ||
263 | return MODE_OK; | ||
264 | |||
265 | dev_warn(tve->dev, "ignoring mode %dx%d\n", | ||
266 | mode->hdisplay, mode->vdisplay); | ||
267 | |||
268 | return MODE_BAD; | ||
269 | } | ||
270 | |||
271 | static struct drm_encoder *imx_tve_connector_best_encoder( | ||
272 | struct drm_connector *connector) | ||
273 | { | ||
274 | struct imx_tve *tve = con_to_tve(connector); | ||
275 | |||
276 | return &tve->encoder; | ||
277 | } | ||
278 | |||
279 | static void imx_tve_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
280 | { | ||
281 | struct imx_tve *tve = enc_to_tve(encoder); | ||
282 | int ret; | ||
283 | |||
284 | ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, | ||
285 | TVE_TV_OUT_MODE_MASK, TVE_TV_OUT_DISABLE); | ||
286 | if (ret < 0) | ||
287 | dev_err(tve->dev, "failed to disable TVOUT: %d\n", ret); | ||
288 | } | ||
289 | |||
290 | static bool imx_tve_encoder_mode_fixup(struct drm_encoder *encoder, | ||
291 | const struct drm_display_mode *mode, | ||
292 | struct drm_display_mode *adjusted_mode) | ||
293 | { | ||
294 | return true; | ||
295 | } | ||
296 | |||
297 | static void imx_tve_encoder_prepare(struct drm_encoder *encoder) | ||
298 | { | ||
299 | struct imx_tve *tve = enc_to_tve(encoder); | ||
300 | |||
301 | tve_disable(tve); | ||
302 | |||
303 | switch (tve->mode) { | ||
304 | case TVE_MODE_VGA: | ||
305 | imx_drm_panel_format_pins(encoder, IPU_PIX_FMT_GBR24, | ||
306 | tve->hsync_pin, tve->vsync_pin); | ||
307 | break; | ||
308 | case TVE_MODE_TVOUT: | ||
309 | imx_drm_panel_format(encoder, V4L2_PIX_FMT_YUV444); | ||
310 | break; | ||
311 | } | ||
312 | } | ||
313 | |||
314 | static void imx_tve_encoder_mode_set(struct drm_encoder *encoder, | ||
315 | struct drm_display_mode *mode, | ||
316 | struct drm_display_mode *adjusted_mode) | ||
317 | { | ||
318 | struct imx_tve *tve = enc_to_tve(encoder); | ||
319 | unsigned long rounded_rate; | ||
320 | unsigned long rate; | ||
321 | int div = 1; | ||
322 | int ret; | ||
323 | |||
324 | /* | ||
325 | * FIXME | ||
326 | * we should try 4k * mode->clock first, | ||
327 | * and enable 4x oversampling for lower resolutions | ||
328 | */ | ||
329 | rate = 2000UL * mode->clock; | ||
330 | clk_set_rate(tve->clk, rate); | ||
331 | rounded_rate = clk_get_rate(tve->clk); | ||
332 | if (rounded_rate >= rate) | ||
333 | div = 2; | ||
334 | clk_set_rate(tve->di_clk, rounded_rate / div); | ||
335 | |||
336 | ret = clk_set_parent(tve->di_sel_clk, tve->di_clk); | ||
337 | if (ret < 0) { | ||
338 | dev_err(tve->dev, "failed to set di_sel parent to tve_di: %d\n", | ||
339 | ret); | ||
340 | } | ||
341 | |||
342 | if (tve->mode == TVE_MODE_VGA) | ||
343 | tve_setup_vga(tve); | ||
344 | else | ||
345 | tve_setup_tvout(tve); | ||
346 | } | ||
347 | |||
348 | static void imx_tve_encoder_commit(struct drm_encoder *encoder) | ||
349 | { | ||
350 | struct imx_tve *tve = enc_to_tve(encoder); | ||
351 | |||
352 | tve_enable(tve); | ||
353 | } | ||
354 | |||
355 | static void imx_tve_encoder_disable(struct drm_encoder *encoder) | ||
356 | { | ||
357 | struct imx_tve *tve = enc_to_tve(encoder); | ||
358 | |||
359 | tve_disable(tve); | ||
360 | } | ||
361 | |||
362 | static struct drm_connector_funcs imx_tve_connector_funcs = { | ||
363 | .dpms = drm_helper_connector_dpms, | ||
364 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
365 | .detect = imx_tve_connector_detect, | ||
366 | .destroy = imx_drm_connector_destroy, | ||
367 | }; | ||
368 | |||
369 | static struct drm_connector_helper_funcs imx_tve_connector_helper_funcs = { | ||
370 | .get_modes = imx_tve_connector_get_modes, | ||
371 | .best_encoder = imx_tve_connector_best_encoder, | ||
372 | .mode_valid = imx_tve_connector_mode_valid, | ||
373 | }; | ||
374 | |||
375 | static struct drm_encoder_funcs imx_tve_encoder_funcs = { | ||
376 | .destroy = imx_drm_encoder_destroy, | ||
377 | }; | ||
378 | |||
379 | static struct drm_encoder_helper_funcs imx_tve_encoder_helper_funcs = { | ||
380 | .dpms = imx_tve_encoder_dpms, | ||
381 | .mode_fixup = imx_tve_encoder_mode_fixup, | ||
382 | .prepare = imx_tve_encoder_prepare, | ||
383 | .mode_set = imx_tve_encoder_mode_set, | ||
384 | .commit = imx_tve_encoder_commit, | ||
385 | .disable = imx_tve_encoder_disable, | ||
386 | }; | ||
387 | |||
388 | static irqreturn_t imx_tve_irq_handler(int irq, void *data) | ||
389 | { | ||
390 | struct imx_tve *tve = data; | ||
391 | unsigned int val; | ||
392 | |||
393 | regmap_read(tve->regmap, TVE_STAT_REG, &val); | ||
394 | |||
395 | /* clear interrupt status register */ | ||
396 | regmap_write(tve->regmap, TVE_STAT_REG, 0xffffffff); | ||
397 | |||
398 | return IRQ_HANDLED; | ||
399 | } | ||
400 | |||
401 | static unsigned long clk_tve_di_recalc_rate(struct clk_hw *hw, | ||
402 | unsigned long parent_rate) | ||
403 | { | ||
404 | struct imx_tve *tve = container_of(hw, struct imx_tve, clk_hw_di); | ||
405 | unsigned int val; | ||
406 | int ret; | ||
407 | |||
408 | ret = regmap_read(tve->regmap, TVE_COM_CONF_REG, &val); | ||
409 | if (ret < 0) | ||
410 | return 0; | ||
411 | |||
412 | switch (val & TVE_DAC_SAMP_RATE_MASK) { | ||
413 | case TVE_DAC_DIV4_RATE: | ||
414 | return parent_rate / 4; | ||
415 | case TVE_DAC_DIV2_RATE: | ||
416 | return parent_rate / 2; | ||
417 | case TVE_DAC_FULL_RATE: | ||
418 | default: | ||
419 | return parent_rate; | ||
420 | } | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | static long clk_tve_di_round_rate(struct clk_hw *hw, unsigned long rate, | ||
426 | unsigned long *prate) | ||
427 | { | ||
428 | unsigned long div; | ||
429 | |||
430 | div = *prate / rate; | ||
431 | if (div >= 4) | ||
432 | return *prate / 4; | ||
433 | else if (div >= 2) | ||
434 | return *prate / 2; | ||
435 | return *prate; | ||
436 | } | ||
437 | |||
438 | static int clk_tve_di_set_rate(struct clk_hw *hw, unsigned long rate, | ||
439 | unsigned long parent_rate) | ||
440 | { | ||
441 | struct imx_tve *tve = container_of(hw, struct imx_tve, clk_hw_di); | ||
442 | unsigned long div; | ||
443 | u32 val; | ||
444 | int ret; | ||
445 | |||
446 | div = parent_rate / rate; | ||
447 | if (div >= 4) | ||
448 | val = TVE_DAC_DIV4_RATE; | ||
449 | else if (div >= 2) | ||
450 | val = TVE_DAC_DIV2_RATE; | ||
451 | else | ||
452 | val = TVE_DAC_FULL_RATE; | ||
453 | |||
454 | ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, | ||
455 | TVE_DAC_SAMP_RATE_MASK, val); | ||
456 | |||
457 | if (ret < 0) { | ||
458 | dev_err(tve->dev, "failed to set divider: %d\n", ret); | ||
459 | return ret; | ||
460 | } | ||
461 | |||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | static struct clk_ops clk_tve_di_ops = { | ||
466 | .round_rate = clk_tve_di_round_rate, | ||
467 | .set_rate = clk_tve_di_set_rate, | ||
468 | .recalc_rate = clk_tve_di_recalc_rate, | ||
469 | }; | ||
470 | |||
471 | static int tve_clk_init(struct imx_tve *tve, void __iomem *base) | ||
472 | { | ||
473 | const char *tve_di_parent[1]; | ||
474 | struct clk_init_data init = { | ||
475 | .name = "tve_di", | ||
476 | .ops = &clk_tve_di_ops, | ||
477 | .num_parents = 1, | ||
478 | .flags = 0, | ||
479 | }; | ||
480 | |||
481 | tve_di_parent[0] = __clk_get_name(tve->clk); | ||
482 | init.parent_names = (const char **)&tve_di_parent; | ||
483 | |||
484 | tve->clk_hw_di.init = &init; | ||
485 | tve->di_clk = clk_register(tve->dev, &tve->clk_hw_di); | ||
486 | if (IS_ERR(tve->di_clk)) { | ||
487 | dev_err(tve->dev, "failed to register TVE output clock: %ld\n", | ||
488 | PTR_ERR(tve->di_clk)); | ||
489 | return PTR_ERR(tve->di_clk); | ||
490 | } | ||
491 | |||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | static int imx_tve_register(struct drm_device *drm, struct imx_tve *tve) | ||
496 | { | ||
497 | int encoder_type; | ||
498 | int ret; | ||
499 | |||
500 | encoder_type = tve->mode == TVE_MODE_VGA ? | ||
501 | DRM_MODE_ENCODER_DAC : DRM_MODE_ENCODER_TVDAC; | ||
502 | |||
503 | ret = imx_drm_encoder_parse_of(drm, &tve->encoder, | ||
504 | tve->dev->of_node); | ||
505 | if (ret) | ||
506 | return ret; | ||
507 | |||
508 | drm_encoder_helper_add(&tve->encoder, &imx_tve_encoder_helper_funcs); | ||
509 | drm_encoder_init(drm, &tve->encoder, &imx_tve_encoder_funcs, | ||
510 | encoder_type); | ||
511 | |||
512 | drm_connector_helper_add(&tve->connector, | ||
513 | &imx_tve_connector_helper_funcs); | ||
514 | drm_connector_init(drm, &tve->connector, &imx_tve_connector_funcs, | ||
515 | DRM_MODE_CONNECTOR_VGA); | ||
516 | |||
517 | drm_mode_connector_attach_encoder(&tve->connector, &tve->encoder); | ||
518 | |||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | static bool imx_tve_readable_reg(struct device *dev, unsigned int reg) | ||
523 | { | ||
524 | return (reg % 4 == 0) && (reg <= 0xdc); | ||
525 | } | ||
526 | |||
527 | static struct regmap_config tve_regmap_config = { | ||
528 | .reg_bits = 32, | ||
529 | .val_bits = 32, | ||
530 | .reg_stride = 4, | ||
531 | |||
532 | .readable_reg = imx_tve_readable_reg, | ||
533 | |||
534 | .lock = tve_lock, | ||
535 | .unlock = tve_unlock, | ||
536 | |||
537 | .max_register = 0xdc, | ||
538 | }; | ||
539 | |||
540 | static const char * const imx_tve_modes[] = { | ||
541 | [TVE_MODE_TVOUT] = "tvout", | ||
542 | [TVE_MODE_VGA] = "vga", | ||
543 | }; | ||
544 | |||
545 | static const int of_get_tve_mode(struct device_node *np) | ||
546 | { | ||
547 | const char *bm; | ||
548 | int ret, i; | ||
549 | |||
550 | ret = of_property_read_string(np, "fsl,tve-mode", &bm); | ||
551 | if (ret < 0) | ||
552 | return ret; | ||
553 | |||
554 | for (i = 0; i < ARRAY_SIZE(imx_tve_modes); i++) | ||
555 | if (!strcasecmp(bm, imx_tve_modes[i])) | ||
556 | return i; | ||
557 | |||
558 | return -EINVAL; | ||
559 | } | ||
560 | |||
561 | static int imx_tve_bind(struct device *dev, struct device *master, void *data) | ||
562 | { | ||
563 | struct platform_device *pdev = to_platform_device(dev); | ||
564 | struct drm_device *drm = data; | ||
565 | struct device_node *np = dev->of_node; | ||
566 | struct device_node *ddc_node; | ||
567 | struct imx_tve *tve; | ||
568 | struct resource *res; | ||
569 | void __iomem *base; | ||
570 | unsigned int val; | ||
571 | int irq; | ||
572 | int ret; | ||
573 | |||
574 | tve = devm_kzalloc(dev, sizeof(*tve), GFP_KERNEL); | ||
575 | if (!tve) | ||
576 | return -ENOMEM; | ||
577 | |||
578 | tve->dev = dev; | ||
579 | spin_lock_init(&tve->lock); | ||
580 | |||
581 | ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); | ||
582 | if (ddc_node) { | ||
583 | tve->ddc = of_find_i2c_adapter_by_node(ddc_node); | ||
584 | of_node_put(ddc_node); | ||
585 | } | ||
586 | |||
587 | tve->mode = of_get_tve_mode(np); | ||
588 | if (tve->mode != TVE_MODE_VGA) { | ||
589 | dev_err(dev, "only VGA mode supported, currently\n"); | ||
590 | return -EINVAL; | ||
591 | } | ||
592 | |||
593 | if (tve->mode == TVE_MODE_VGA) { | ||
594 | ret = of_property_read_u32(np, "fsl,hsync-pin", | ||
595 | &tve->hsync_pin); | ||
596 | |||
597 | if (ret < 0) { | ||
598 | dev_err(dev, "failed to get vsync pin\n"); | ||
599 | return ret; | ||
600 | } | ||
601 | |||
602 | ret |= of_property_read_u32(np, "fsl,vsync-pin", | ||
603 | &tve->vsync_pin); | ||
604 | |||
605 | if (ret < 0) { | ||
606 | dev_err(dev, "failed to get vsync pin\n"); | ||
607 | return ret; | ||
608 | } | ||
609 | } | ||
610 | |||
611 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
612 | base = devm_ioremap_resource(dev, res); | ||
613 | if (IS_ERR(base)) | ||
614 | return PTR_ERR(base); | ||
615 | |||
616 | tve_regmap_config.lock_arg = tve; | ||
617 | tve->regmap = devm_regmap_init_mmio_clk(dev, "tve", base, | ||
618 | &tve_regmap_config); | ||
619 | if (IS_ERR(tve->regmap)) { | ||
620 | dev_err(dev, "failed to init regmap: %ld\n", | ||
621 | PTR_ERR(tve->regmap)); | ||
622 | return PTR_ERR(tve->regmap); | ||
623 | } | ||
624 | |||
625 | irq = platform_get_irq(pdev, 0); | ||
626 | if (irq < 0) { | ||
627 | dev_err(dev, "failed to get irq\n"); | ||
628 | return irq; | ||
629 | } | ||
630 | |||
631 | ret = devm_request_threaded_irq(dev, irq, NULL, | ||
632 | imx_tve_irq_handler, IRQF_ONESHOT, | ||
633 | "imx-tve", tve); | ||
634 | if (ret < 0) { | ||
635 | dev_err(dev, "failed to request irq: %d\n", ret); | ||
636 | return ret; | ||
637 | } | ||
638 | |||
639 | tve->dac_reg = devm_regulator_get(dev, "dac"); | ||
640 | if (!IS_ERR(tve->dac_reg)) { | ||
641 | regulator_set_voltage(tve->dac_reg, 2750000, 2750000); | ||
642 | ret = regulator_enable(tve->dac_reg); | ||
643 | if (ret) | ||
644 | return ret; | ||
645 | } | ||
646 | |||
647 | tve->clk = devm_clk_get(dev, "tve"); | ||
648 | if (IS_ERR(tve->clk)) { | ||
649 | dev_err(dev, "failed to get high speed tve clock: %ld\n", | ||
650 | PTR_ERR(tve->clk)); | ||
651 | return PTR_ERR(tve->clk); | ||
652 | } | ||
653 | |||
654 | /* this is the IPU DI clock input selector, can be parented to tve_di */ | ||
655 | tve->di_sel_clk = devm_clk_get(dev, "di_sel"); | ||
656 | if (IS_ERR(tve->di_sel_clk)) { | ||
657 | dev_err(dev, "failed to get ipu di mux clock: %ld\n", | ||
658 | PTR_ERR(tve->di_sel_clk)); | ||
659 | return PTR_ERR(tve->di_sel_clk); | ||
660 | } | ||
661 | |||
662 | ret = tve_clk_init(tve, base); | ||
663 | if (ret < 0) | ||
664 | return ret; | ||
665 | |||
666 | ret = regmap_read(tve->regmap, TVE_COM_CONF_REG, &val); | ||
667 | if (ret < 0) { | ||
668 | dev_err(dev, "failed to read configuration register: %d\n", ret); | ||
669 | return ret; | ||
670 | } | ||
671 | if (val != 0x00100000) { | ||
672 | dev_err(dev, "configuration register default value indicates this is not a TVEv2\n"); | ||
673 | return -ENODEV; | ||
674 | } | ||
675 | |||
676 | /* disable cable detection for VGA mode */ | ||
677 | ret = regmap_write(tve->regmap, TVE_CD_CONT_REG, 0); | ||
678 | |||
679 | ret = imx_tve_register(drm, tve); | ||
680 | if (ret) | ||
681 | return ret; | ||
682 | |||
683 | dev_set_drvdata(dev, tve); | ||
684 | |||
685 | return 0; | ||
686 | } | ||
687 | |||
688 | static void imx_tve_unbind(struct device *dev, struct device *master, | ||
689 | void *data) | ||
690 | { | ||
691 | struct imx_tve *tve = dev_get_drvdata(dev); | ||
692 | |||
693 | tve->connector.funcs->destroy(&tve->connector); | ||
694 | tve->encoder.funcs->destroy(&tve->encoder); | ||
695 | |||
696 | if (!IS_ERR(tve->dac_reg)) | ||
697 | regulator_disable(tve->dac_reg); | ||
698 | } | ||
699 | |||
700 | static const struct component_ops imx_tve_ops = { | ||
701 | .bind = imx_tve_bind, | ||
702 | .unbind = imx_tve_unbind, | ||
703 | }; | ||
704 | |||
705 | static int imx_tve_probe(struct platform_device *pdev) | ||
706 | { | ||
707 | return component_add(&pdev->dev, &imx_tve_ops); | ||
708 | } | ||
709 | |||
710 | static int imx_tve_remove(struct platform_device *pdev) | ||
711 | { | ||
712 | component_del(&pdev->dev, &imx_tve_ops); | ||
713 | return 0; | ||
714 | } | ||
715 | |||
716 | static const struct of_device_id imx_tve_dt_ids[] = { | ||
717 | { .compatible = "fsl,imx53-tve", }, | ||
718 | { /* sentinel */ } | ||
719 | }; | ||
720 | |||
721 | static struct platform_driver imx_tve_driver = { | ||
722 | .probe = imx_tve_probe, | ||
723 | .remove = imx_tve_remove, | ||
724 | .driver = { | ||
725 | .of_match_table = imx_tve_dt_ids, | ||
726 | .name = "imx-tve", | ||
727 | }, | ||
728 | }; | ||
729 | |||
730 | module_platform_driver(imx_tve_driver); | ||
731 | |||
732 | MODULE_DESCRIPTION("i.MX Television Encoder driver"); | ||
733 | MODULE_AUTHOR("Philipp Zabel, Pengutronix"); | ||
734 | MODULE_LICENSE("GPL"); | ||
735 | MODULE_ALIAS("platform:imx-tve"); | ||
diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c new file mode 100644 index 000000000000..11e84a251773 --- /dev/null +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c | |||
@@ -0,0 +1,518 @@ | |||
1 | /* | ||
2 | * i.MX IPUv3 Graphics driver | ||
3 | * | ||
4 | * Copyright (C) 2011 Sascha Hauer, Pengutronix | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version 2 | ||
9 | * of the License, or (at your option) any later version. | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
18 | * MA 02110-1301, USA. | ||
19 | */ | ||
20 | #include <linux/component.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/export.h> | ||
23 | #include <linux/device.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <drm/drmP.h> | ||
26 | #include <drm/drm_crtc_helper.h> | ||
27 | #include <linux/fb.h> | ||
28 | #include <linux/clk.h> | ||
29 | #include <linux/errno.h> | ||
30 | #include <drm/drm_gem_cma_helper.h> | ||
31 | #include <drm/drm_fb_cma_helper.h> | ||
32 | |||
33 | #include <video/imx-ipu-v3.h> | ||
34 | #include "imx-drm.h" | ||
35 | #include "ipuv3-plane.h" | ||
36 | |||
37 | #define DRIVER_DESC "i.MX IPUv3 Graphics" | ||
38 | |||
39 | struct ipu_crtc { | ||
40 | struct device *dev; | ||
41 | struct drm_crtc base; | ||
42 | struct imx_drm_crtc *imx_crtc; | ||
43 | |||
44 | /* plane[0] is the full plane, plane[1] is the partial plane */ | ||
45 | struct ipu_plane *plane[2]; | ||
46 | |||
47 | struct ipu_dc *dc; | ||
48 | struct ipu_di *di; | ||
49 | int enabled; | ||
50 | struct drm_pending_vblank_event *page_flip_event; | ||
51 | struct drm_framebuffer *newfb; | ||
52 | int irq; | ||
53 | u32 interface_pix_fmt; | ||
54 | unsigned long di_clkflags; | ||
55 | int di_hsync_pin; | ||
56 | int di_vsync_pin; | ||
57 | }; | ||
58 | |||
59 | #define to_ipu_crtc(x) container_of(x, struct ipu_crtc, base) | ||
60 | |||
61 | static void ipu_fb_enable(struct ipu_crtc *ipu_crtc) | ||
62 | { | ||
63 | struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); | ||
64 | |||
65 | if (ipu_crtc->enabled) | ||
66 | return; | ||
67 | |||
68 | ipu_dc_enable(ipu); | ||
69 | ipu_plane_enable(ipu_crtc->plane[0]); | ||
70 | /* Start DC channel and DI after IDMAC */ | ||
71 | ipu_dc_enable_channel(ipu_crtc->dc); | ||
72 | ipu_di_enable(ipu_crtc->di); | ||
73 | |||
74 | ipu_crtc->enabled = 1; | ||
75 | } | ||
76 | |||
77 | static void ipu_fb_disable(struct ipu_crtc *ipu_crtc) | ||
78 | { | ||
79 | struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); | ||
80 | |||
81 | if (!ipu_crtc->enabled) | ||
82 | return; | ||
83 | |||
84 | /* Stop DC channel and DI before IDMAC */ | ||
85 | ipu_dc_disable_channel(ipu_crtc->dc); | ||
86 | ipu_di_disable(ipu_crtc->di); | ||
87 | ipu_plane_disable(ipu_crtc->plane[0]); | ||
88 | ipu_dc_disable(ipu); | ||
89 | |||
90 | ipu_crtc->enabled = 0; | ||
91 | } | ||
92 | |||
93 | static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode) | ||
94 | { | ||
95 | struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); | ||
96 | |||
97 | dev_dbg(ipu_crtc->dev, "%s mode: %d\n", __func__, mode); | ||
98 | |||
99 | switch (mode) { | ||
100 | case DRM_MODE_DPMS_ON: | ||
101 | ipu_fb_enable(ipu_crtc); | ||
102 | break; | ||
103 | case DRM_MODE_DPMS_STANDBY: | ||
104 | case DRM_MODE_DPMS_SUSPEND: | ||
105 | case DRM_MODE_DPMS_OFF: | ||
106 | ipu_fb_disable(ipu_crtc); | ||
107 | break; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | static int ipu_page_flip(struct drm_crtc *crtc, | ||
112 | struct drm_framebuffer *fb, | ||
113 | struct drm_pending_vblank_event *event, | ||
114 | uint32_t page_flip_flags) | ||
115 | { | ||
116 | struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); | ||
117 | int ret; | ||
118 | |||
119 | if (ipu_crtc->newfb) | ||
120 | return -EBUSY; | ||
121 | |||
122 | ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc); | ||
123 | if (ret) { | ||
124 | dev_dbg(ipu_crtc->dev, "failed to acquire vblank counter\n"); | ||
125 | list_del(&event->base.link); | ||
126 | |||
127 | return ret; | ||
128 | } | ||
129 | |||
130 | ipu_crtc->newfb = fb; | ||
131 | ipu_crtc->page_flip_event = event; | ||
132 | crtc->primary->fb = fb; | ||
133 | |||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | static const struct drm_crtc_funcs ipu_crtc_funcs = { | ||
138 | .set_config = drm_crtc_helper_set_config, | ||
139 | .destroy = drm_crtc_cleanup, | ||
140 | .page_flip = ipu_page_flip, | ||
141 | }; | ||
142 | |||
143 | static int ipu_crtc_mode_set(struct drm_crtc *crtc, | ||
144 | struct drm_display_mode *orig_mode, | ||
145 | struct drm_display_mode *mode, | ||
146 | int x, int y, | ||
147 | struct drm_framebuffer *old_fb) | ||
148 | { | ||
149 | struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); | ||
150 | int ret; | ||
151 | struct ipu_di_signal_cfg sig_cfg = {}; | ||
152 | u32 out_pixel_fmt; | ||
153 | |||
154 | dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__, | ||
155 | mode->hdisplay); | ||
156 | dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__, | ||
157 | mode->vdisplay); | ||
158 | |||
159 | out_pixel_fmt = ipu_crtc->interface_pix_fmt; | ||
160 | |||
161 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) | ||
162 | sig_cfg.interlaced = 1; | ||
163 | if (mode->flags & DRM_MODE_FLAG_PHSYNC) | ||
164 | sig_cfg.Hsync_pol = 1; | ||
165 | if (mode->flags & DRM_MODE_FLAG_PVSYNC) | ||
166 | sig_cfg.Vsync_pol = 1; | ||
167 | |||
168 | sig_cfg.enable_pol = 1; | ||
169 | sig_cfg.clk_pol = 0; | ||
170 | sig_cfg.width = mode->hdisplay; | ||
171 | sig_cfg.height = mode->vdisplay; | ||
172 | sig_cfg.pixel_fmt = out_pixel_fmt; | ||
173 | sig_cfg.h_start_width = mode->htotal - mode->hsync_end; | ||
174 | sig_cfg.h_sync_width = mode->hsync_end - mode->hsync_start; | ||
175 | sig_cfg.h_end_width = mode->hsync_start - mode->hdisplay; | ||
176 | |||
177 | sig_cfg.v_start_width = mode->vtotal - mode->vsync_end; | ||
178 | sig_cfg.v_sync_width = mode->vsync_end - mode->vsync_start; | ||
179 | sig_cfg.v_end_width = mode->vsync_start - mode->vdisplay; | ||
180 | sig_cfg.pixelclock = mode->clock * 1000; | ||
181 | sig_cfg.clkflags = ipu_crtc->di_clkflags; | ||
182 | |||
183 | sig_cfg.v_to_h_sync = 0; | ||
184 | |||
185 | sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin; | ||
186 | sig_cfg.vsync_pin = ipu_crtc->di_vsync_pin; | ||
187 | |||
188 | ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, sig_cfg.interlaced, | ||
189 | out_pixel_fmt, mode->hdisplay); | ||
190 | if (ret) { | ||
191 | dev_err(ipu_crtc->dev, | ||
192 | "initializing display controller failed with %d\n", | ||
193 | ret); | ||
194 | return ret; | ||
195 | } | ||
196 | |||
197 | ret = ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg); | ||
198 | if (ret) { | ||
199 | dev_err(ipu_crtc->dev, | ||
200 | "initializing panel failed with %d\n", ret); | ||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | return ipu_plane_mode_set(ipu_crtc->plane[0], crtc, mode, | ||
205 | crtc->primary->fb, | ||
206 | 0, 0, mode->hdisplay, mode->vdisplay, | ||
207 | x, y, mode->hdisplay, mode->vdisplay); | ||
208 | } | ||
209 | |||
210 | static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc) | ||
211 | { | ||
212 | unsigned long flags; | ||
213 | struct drm_device *drm = ipu_crtc->base.dev; | ||
214 | |||
215 | spin_lock_irqsave(&drm->event_lock, flags); | ||
216 | if (ipu_crtc->page_flip_event) | ||
217 | drm_send_vblank_event(drm, -1, ipu_crtc->page_flip_event); | ||
218 | ipu_crtc->page_flip_event = NULL; | ||
219 | imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc); | ||
220 | spin_unlock_irqrestore(&drm->event_lock, flags); | ||
221 | } | ||
222 | |||
223 | static irqreturn_t ipu_irq_handler(int irq, void *dev_id) | ||
224 | { | ||
225 | struct ipu_crtc *ipu_crtc = dev_id; | ||
226 | |||
227 | imx_drm_handle_vblank(ipu_crtc->imx_crtc); | ||
228 | |||
229 | if (ipu_crtc->newfb) { | ||
230 | struct ipu_plane *plane = ipu_crtc->plane[0]; | ||
231 | |||
232 | ipu_crtc->newfb = NULL; | ||
233 | ipu_plane_set_base(plane, ipu_crtc->base.primary->fb, | ||
234 | plane->x, plane->y); | ||
235 | ipu_crtc_handle_pageflip(ipu_crtc); | ||
236 | } | ||
237 | |||
238 | return IRQ_HANDLED; | ||
239 | } | ||
240 | |||
241 | static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc, | ||
242 | const struct drm_display_mode *mode, | ||
243 | struct drm_display_mode *adjusted_mode) | ||
244 | { | ||
245 | return true; | ||
246 | } | ||
247 | |||
248 | static void ipu_crtc_prepare(struct drm_crtc *crtc) | ||
249 | { | ||
250 | struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); | ||
251 | |||
252 | ipu_fb_disable(ipu_crtc); | ||
253 | } | ||
254 | |||
255 | static void ipu_crtc_commit(struct drm_crtc *crtc) | ||
256 | { | ||
257 | struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); | ||
258 | |||
259 | ipu_fb_enable(ipu_crtc); | ||
260 | } | ||
261 | |||
262 | static struct drm_crtc_helper_funcs ipu_helper_funcs = { | ||
263 | .dpms = ipu_crtc_dpms, | ||
264 | .mode_fixup = ipu_crtc_mode_fixup, | ||
265 | .mode_set = ipu_crtc_mode_set, | ||
266 | .prepare = ipu_crtc_prepare, | ||
267 | .commit = ipu_crtc_commit, | ||
268 | }; | ||
269 | |||
270 | static int ipu_enable_vblank(struct drm_crtc *crtc) | ||
271 | { | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static void ipu_disable_vblank(struct drm_crtc *crtc) | ||
276 | { | ||
277 | struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); | ||
278 | |||
279 | ipu_crtc->page_flip_event = NULL; | ||
280 | ipu_crtc->newfb = NULL; | ||
281 | } | ||
282 | |||
283 | static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type, | ||
284 | u32 pixfmt, int hsync_pin, int vsync_pin) | ||
285 | { | ||
286 | struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); | ||
287 | |||
288 | ipu_crtc->interface_pix_fmt = pixfmt; | ||
289 | ipu_crtc->di_hsync_pin = hsync_pin; | ||
290 | ipu_crtc->di_vsync_pin = vsync_pin; | ||
291 | |||
292 | switch (encoder_type) { | ||
293 | case DRM_MODE_ENCODER_DAC: | ||
294 | case DRM_MODE_ENCODER_TVDAC: | ||
295 | case DRM_MODE_ENCODER_LVDS: | ||
296 | ipu_crtc->di_clkflags = IPU_DI_CLKMODE_SYNC | | ||
297 | IPU_DI_CLKMODE_EXT; | ||
298 | break; | ||
299 | case DRM_MODE_ENCODER_TMDS: | ||
300 | case DRM_MODE_ENCODER_NONE: | ||
301 | ipu_crtc->di_clkflags = 0; | ||
302 | break; | ||
303 | } | ||
304 | |||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | static const struct imx_drm_crtc_helper_funcs ipu_crtc_helper_funcs = { | ||
309 | .enable_vblank = ipu_enable_vblank, | ||
310 | .disable_vblank = ipu_disable_vblank, | ||
311 | .set_interface_pix_fmt = ipu_set_interface_pix_fmt, | ||
312 | .crtc_funcs = &ipu_crtc_funcs, | ||
313 | .crtc_helper_funcs = &ipu_helper_funcs, | ||
314 | }; | ||
315 | |||
316 | static void ipu_put_resources(struct ipu_crtc *ipu_crtc) | ||
317 | { | ||
318 | if (!IS_ERR_OR_NULL(ipu_crtc->dc)) | ||
319 | ipu_dc_put(ipu_crtc->dc); | ||
320 | if (!IS_ERR_OR_NULL(ipu_crtc->di)) | ||
321 | ipu_di_put(ipu_crtc->di); | ||
322 | } | ||
323 | |||
324 | static int ipu_get_resources(struct ipu_crtc *ipu_crtc, | ||
325 | struct ipu_client_platformdata *pdata) | ||
326 | { | ||
327 | struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); | ||
328 | int ret; | ||
329 | |||
330 | ipu_crtc->dc = ipu_dc_get(ipu, pdata->dc); | ||
331 | if (IS_ERR(ipu_crtc->dc)) { | ||
332 | ret = PTR_ERR(ipu_crtc->dc); | ||
333 | goto err_out; | ||
334 | } | ||
335 | |||
336 | ipu_crtc->di = ipu_di_get(ipu, pdata->di); | ||
337 | if (IS_ERR(ipu_crtc->di)) { | ||
338 | ret = PTR_ERR(ipu_crtc->di); | ||
339 | goto err_out; | ||
340 | } | ||
341 | |||
342 | return 0; | ||
343 | err_out: | ||
344 | ipu_put_resources(ipu_crtc); | ||
345 | |||
346 | return ret; | ||
347 | } | ||
348 | |||
349 | static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, | ||
350 | struct ipu_client_platformdata *pdata, struct drm_device *drm) | ||
351 | { | ||
352 | struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); | ||
353 | int dp = -EINVAL; | ||
354 | int ret; | ||
355 | int id; | ||
356 | |||
357 | ret = ipu_get_resources(ipu_crtc, pdata); | ||
358 | if (ret) { | ||
359 | dev_err(ipu_crtc->dev, "getting resources failed with %d.\n", | ||
360 | ret); | ||
361 | return ret; | ||
362 | } | ||
363 | |||
364 | ret = imx_drm_add_crtc(drm, &ipu_crtc->base, &ipu_crtc->imx_crtc, | ||
365 | &ipu_crtc_helper_funcs, ipu_crtc->dev->of_node); | ||
366 | if (ret) { | ||
367 | dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret); | ||
368 | goto err_put_resources; | ||
369 | } | ||
370 | |||
371 | if (pdata->dp >= 0) | ||
372 | dp = IPU_DP_FLOW_SYNC_BG; | ||
373 | id = imx_drm_crtc_id(ipu_crtc->imx_crtc); | ||
374 | ipu_crtc->plane[0] = ipu_plane_init(ipu_crtc->base.dev, ipu, | ||
375 | pdata->dma[0], dp, BIT(id), true); | ||
376 | ret = ipu_plane_get_resources(ipu_crtc->plane[0]); | ||
377 | if (ret) { | ||
378 | dev_err(ipu_crtc->dev, "getting plane 0 resources failed with %d.\n", | ||
379 | ret); | ||
380 | goto err_remove_crtc; | ||
381 | } | ||
382 | |||
383 | /* If this crtc is using the DP, add an overlay plane */ | ||
384 | if (pdata->dp >= 0 && pdata->dma[1] > 0) { | ||
385 | ipu_crtc->plane[1] = ipu_plane_init(ipu_crtc->base.dev, ipu, | ||
386 | pdata->dma[1], | ||
387 | IPU_DP_FLOW_SYNC_FG, | ||
388 | BIT(id), false); | ||
389 | if (IS_ERR(ipu_crtc->plane[1])) | ||
390 | ipu_crtc->plane[1] = NULL; | ||
391 | } | ||
392 | |||
393 | ipu_crtc->irq = ipu_plane_irq(ipu_crtc->plane[0]); | ||
394 | ret = devm_request_irq(ipu_crtc->dev, ipu_crtc->irq, ipu_irq_handler, 0, | ||
395 | "imx_drm", ipu_crtc); | ||
396 | if (ret < 0) { | ||
397 | dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret); | ||
398 | goto err_put_plane_res; | ||
399 | } | ||
400 | |||
401 | return 0; | ||
402 | |||
403 | err_put_plane_res: | ||
404 | ipu_plane_put_resources(ipu_crtc->plane[0]); | ||
405 | err_remove_crtc: | ||
406 | imx_drm_remove_crtc(ipu_crtc->imx_crtc); | ||
407 | err_put_resources: | ||
408 | ipu_put_resources(ipu_crtc); | ||
409 | |||
410 | return ret; | ||
411 | } | ||
412 | |||
413 | static struct device_node *ipu_drm_get_port_by_id(struct device_node *parent, | ||
414 | int port_id) | ||
415 | { | ||
416 | struct device_node *port; | ||
417 | int id, ret; | ||
418 | |||
419 | port = of_get_child_by_name(parent, "port"); | ||
420 | while (port) { | ||
421 | ret = of_property_read_u32(port, "reg", &id); | ||
422 | if (!ret && id == port_id) | ||
423 | return port; | ||
424 | |||
425 | do { | ||
426 | port = of_get_next_child(parent, port); | ||
427 | if (!port) | ||
428 | return NULL; | ||
429 | } while (of_node_cmp(port->name, "port")); | ||
430 | } | ||
431 | |||
432 | return NULL; | ||
433 | } | ||
434 | |||
435 | static int ipu_drm_bind(struct device *dev, struct device *master, void *data) | ||
436 | { | ||
437 | struct ipu_client_platformdata *pdata = dev->platform_data; | ||
438 | struct drm_device *drm = data; | ||
439 | struct ipu_crtc *ipu_crtc; | ||
440 | int ret; | ||
441 | |||
442 | ipu_crtc = devm_kzalloc(dev, sizeof(*ipu_crtc), GFP_KERNEL); | ||
443 | if (!ipu_crtc) | ||
444 | return -ENOMEM; | ||
445 | |||
446 | ipu_crtc->dev = dev; | ||
447 | |||
448 | ret = ipu_crtc_init(ipu_crtc, pdata, drm); | ||
449 | if (ret) | ||
450 | return ret; | ||
451 | |||
452 | dev_set_drvdata(dev, ipu_crtc); | ||
453 | |||
454 | return 0; | ||
455 | } | ||
456 | |||
457 | static void ipu_drm_unbind(struct device *dev, struct device *master, | ||
458 | void *data) | ||
459 | { | ||
460 | struct ipu_crtc *ipu_crtc = dev_get_drvdata(dev); | ||
461 | |||
462 | imx_drm_remove_crtc(ipu_crtc->imx_crtc); | ||
463 | |||
464 | ipu_plane_put_resources(ipu_crtc->plane[0]); | ||
465 | ipu_put_resources(ipu_crtc); | ||
466 | } | ||
467 | |||
468 | static const struct component_ops ipu_crtc_ops = { | ||
469 | .bind = ipu_drm_bind, | ||
470 | .unbind = ipu_drm_unbind, | ||
471 | }; | ||
472 | |||
473 | static int ipu_drm_probe(struct platform_device *pdev) | ||
474 | { | ||
475 | struct device *dev = &pdev->dev; | ||
476 | struct ipu_client_platformdata *pdata = dev->platform_data; | ||
477 | int ret; | ||
478 | |||
479 | if (!dev->platform_data) | ||
480 | return -EINVAL; | ||
481 | |||
482 | if (!dev->of_node) { | ||
483 | /* Associate crtc device with the corresponding DI port node */ | ||
484 | dev->of_node = ipu_drm_get_port_by_id(dev->parent->of_node, | ||
485 | pdata->di + 2); | ||
486 | if (!dev->of_node) { | ||
487 | dev_err(dev, "missing port@%d node in %s\n", | ||
488 | pdata->di + 2, dev->parent->of_node->full_name); | ||
489 | return -ENODEV; | ||
490 | } | ||
491 | } | ||
492 | |||
493 | ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); | ||
494 | if (ret) | ||
495 | return ret; | ||
496 | |||
497 | return component_add(dev, &ipu_crtc_ops); | ||
498 | } | ||
499 | |||
500 | static int ipu_drm_remove(struct platform_device *pdev) | ||
501 | { | ||
502 | component_del(&pdev->dev, &ipu_crtc_ops); | ||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static struct platform_driver ipu_drm_driver = { | ||
507 | .driver = { | ||
508 | .name = "imx-ipuv3-crtc", | ||
509 | }, | ||
510 | .probe = ipu_drm_probe, | ||
511 | .remove = ipu_drm_remove, | ||
512 | }; | ||
513 | module_platform_driver(ipu_drm_driver); | ||
514 | |||
515 | MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); | ||
516 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
517 | MODULE_LICENSE("GPL"); | ||
518 | MODULE_ALIAS("platform:imx-ipuv3-crtc"); | ||
diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c new file mode 100644 index 000000000000..944962b692bb --- /dev/null +++ b/drivers/gpu/drm/imx/ipuv3-plane.c | |||
@@ -0,0 +1,363 @@ | |||
1 | /* | ||
2 | * i.MX IPUv3 DP Overlay Planes | ||
3 | * | ||
4 | * Copyright (C) 2013 Philipp Zabel, Pengutronix | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version 2 | ||
9 | * of the License, or (at your option) any later version. | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | */ | ||
15 | |||
16 | #include <drm/drmP.h> | ||
17 | #include <drm/drm_fb_cma_helper.h> | ||
18 | #include <drm/drm_gem_cma_helper.h> | ||
19 | |||
20 | #include "video/imx-ipu-v3.h" | ||
21 | #include "ipuv3-plane.h" | ||
22 | |||
23 | #define to_ipu_plane(x) container_of(x, struct ipu_plane, base) | ||
24 | |||
25 | static const uint32_t ipu_plane_formats[] = { | ||
26 | DRM_FORMAT_XRGB1555, | ||
27 | DRM_FORMAT_XBGR1555, | ||
28 | DRM_FORMAT_ARGB8888, | ||
29 | DRM_FORMAT_XRGB8888, | ||
30 | DRM_FORMAT_ABGR8888, | ||
31 | DRM_FORMAT_XBGR8888, | ||
32 | DRM_FORMAT_YUYV, | ||
33 | DRM_FORMAT_YVYU, | ||
34 | DRM_FORMAT_YUV420, | ||
35 | DRM_FORMAT_YVU420, | ||
36 | }; | ||
37 | |||
38 | int ipu_plane_irq(struct ipu_plane *ipu_plane) | ||
39 | { | ||
40 | return ipu_idmac_channel_irq(ipu_plane->ipu, ipu_plane->ipu_ch, | ||
41 | IPU_IRQ_EOF); | ||
42 | } | ||
43 | |||
44 | static int calc_vref(struct drm_display_mode *mode) | ||
45 | { | ||
46 | unsigned long htotal, vtotal; | ||
47 | |||
48 | htotal = mode->htotal; | ||
49 | vtotal = mode->vtotal; | ||
50 | |||
51 | if (!htotal || !vtotal) | ||
52 | return 60; | ||
53 | |||
54 | return DIV_ROUND_UP(mode->clock * 1000, vtotal * htotal); | ||
55 | } | ||
56 | |||
57 | static inline int calc_bandwidth(int width, int height, unsigned int vref) | ||
58 | { | ||
59 | return width * height * vref; | ||
60 | } | ||
61 | |||
62 | int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb, | ||
63 | int x, int y) | ||
64 | { | ||
65 | struct drm_gem_cma_object *cma_obj; | ||
66 | unsigned long eba; | ||
67 | |||
68 | cma_obj = drm_fb_cma_get_gem_obj(fb, 0); | ||
69 | if (!cma_obj) { | ||
70 | DRM_DEBUG_KMS("entry is null.\n"); | ||
71 | return -EFAULT; | ||
72 | } | ||
73 | |||
74 | dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d", | ||
75 | &cma_obj->paddr, x, y); | ||
76 | |||
77 | ipu_cpmem_set_stride(ipu_plane->ipu_ch, fb->pitches[0]); | ||
78 | |||
79 | eba = cma_obj->paddr + fb->offsets[0] + | ||
80 | fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x; | ||
81 | ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba); | ||
82 | ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba); | ||
83 | |||
84 | /* cache offsets for subsequent pageflips */ | ||
85 | ipu_plane->x = x; | ||
86 | ipu_plane->y = y; | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc, | ||
92 | struct drm_display_mode *mode, | ||
93 | struct drm_framebuffer *fb, int crtc_x, int crtc_y, | ||
94 | unsigned int crtc_w, unsigned int crtc_h, | ||
95 | uint32_t src_x, uint32_t src_y, | ||
96 | uint32_t src_w, uint32_t src_h) | ||
97 | { | ||
98 | struct device *dev = ipu_plane->base.dev->dev; | ||
99 | int ret; | ||
100 | |||
101 | /* no scaling */ | ||
102 | if (src_w != crtc_w || src_h != crtc_h) | ||
103 | return -EINVAL; | ||
104 | |||
105 | /* clip to crtc bounds */ | ||
106 | if (crtc_x < 0) { | ||
107 | if (-crtc_x > crtc_w) | ||
108 | return -EINVAL; | ||
109 | src_x += -crtc_x; | ||
110 | src_w -= -crtc_x; | ||
111 | crtc_w -= -crtc_x; | ||
112 | crtc_x = 0; | ||
113 | } | ||
114 | if (crtc_y < 0) { | ||
115 | if (-crtc_y > crtc_h) | ||
116 | return -EINVAL; | ||
117 | src_y += -crtc_y; | ||
118 | src_h -= -crtc_y; | ||
119 | crtc_h -= -crtc_y; | ||
120 | crtc_y = 0; | ||
121 | } | ||
122 | if (crtc_x + crtc_w > mode->hdisplay) { | ||
123 | if (crtc_x > mode->hdisplay) | ||
124 | return -EINVAL; | ||
125 | crtc_w = mode->hdisplay - crtc_x; | ||
126 | src_w = crtc_w; | ||
127 | } | ||
128 | if (crtc_y + crtc_h > mode->vdisplay) { | ||
129 | if (crtc_y > mode->vdisplay) | ||
130 | return -EINVAL; | ||
131 | crtc_h = mode->vdisplay - crtc_y; | ||
132 | src_h = crtc_h; | ||
133 | } | ||
134 | /* full plane minimum width is 13 pixels */ | ||
135 | if (crtc_w < 13 && (ipu_plane->dp_flow != IPU_DP_FLOW_SYNC_FG)) | ||
136 | return -EINVAL; | ||
137 | if (crtc_h < 2) | ||
138 | return -EINVAL; | ||
139 | |||
140 | switch (ipu_plane->dp_flow) { | ||
141 | case IPU_DP_FLOW_SYNC_BG: | ||
142 | ret = ipu_dp_setup_channel(ipu_plane->dp, | ||
143 | IPUV3_COLORSPACE_RGB, | ||
144 | IPUV3_COLORSPACE_RGB); | ||
145 | if (ret) { | ||
146 | dev_err(dev, | ||
147 | "initializing display processor failed with %d\n", | ||
148 | ret); | ||
149 | return ret; | ||
150 | } | ||
151 | ipu_dp_set_global_alpha(ipu_plane->dp, 1, 0, 1); | ||
152 | break; | ||
153 | case IPU_DP_FLOW_SYNC_FG: | ||
154 | ipu_dp_setup_channel(ipu_plane->dp, | ||
155 | ipu_drm_fourcc_to_colorspace(fb->pixel_format), | ||
156 | IPUV3_COLORSPACE_UNKNOWN); | ||
157 | ipu_dp_set_window_pos(ipu_plane->dp, crtc_x, crtc_y); | ||
158 | break; | ||
159 | } | ||
160 | |||
161 | ret = ipu_dmfc_init_channel(ipu_plane->dmfc, crtc_w); | ||
162 | if (ret) { | ||
163 | dev_err(dev, "initializing dmfc channel failed with %d\n", ret); | ||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | ret = ipu_dmfc_alloc_bandwidth(ipu_plane->dmfc, | ||
168 | calc_bandwidth(crtc_w, crtc_h, | ||
169 | calc_vref(mode)), 64); | ||
170 | if (ret) { | ||
171 | dev_err(dev, "allocating dmfc bandwidth failed with %d\n", ret); | ||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | ipu_cpmem_zero(ipu_plane->ipu_ch); | ||
176 | ipu_cpmem_set_resolution(ipu_plane->ipu_ch, src_w, src_h); | ||
177 | ret = ipu_cpmem_set_fmt(ipu_plane->ipu_ch, fb->pixel_format); | ||
178 | if (ret < 0) { | ||
179 | dev_err(dev, "unsupported pixel format 0x%08x\n", | ||
180 | fb->pixel_format); | ||
181 | return ret; | ||
182 | } | ||
183 | ipu_cpmem_set_high_priority(ipu_plane->ipu_ch); | ||
184 | |||
185 | ret = ipu_plane_set_base(ipu_plane, fb, src_x, src_y); | ||
186 | if (ret < 0) | ||
187 | return ret; | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | void ipu_plane_put_resources(struct ipu_plane *ipu_plane) | ||
193 | { | ||
194 | if (!IS_ERR_OR_NULL(ipu_plane->dp)) | ||
195 | ipu_dp_put(ipu_plane->dp); | ||
196 | if (!IS_ERR_OR_NULL(ipu_plane->dmfc)) | ||
197 | ipu_dmfc_put(ipu_plane->dmfc); | ||
198 | if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch)) | ||
199 | ipu_idmac_put(ipu_plane->ipu_ch); | ||
200 | } | ||
201 | |||
202 | int ipu_plane_get_resources(struct ipu_plane *ipu_plane) | ||
203 | { | ||
204 | int ret; | ||
205 | |||
206 | ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma); | ||
207 | if (IS_ERR(ipu_plane->ipu_ch)) { | ||
208 | ret = PTR_ERR(ipu_plane->ipu_ch); | ||
209 | DRM_ERROR("failed to get idmac channel: %d\n", ret); | ||
210 | return ret; | ||
211 | } | ||
212 | |||
213 | ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma); | ||
214 | if (IS_ERR(ipu_plane->dmfc)) { | ||
215 | ret = PTR_ERR(ipu_plane->dmfc); | ||
216 | DRM_ERROR("failed to get dmfc: ret %d\n", ret); | ||
217 | goto err_out; | ||
218 | } | ||
219 | |||
220 | if (ipu_plane->dp_flow >= 0) { | ||
221 | ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow); | ||
222 | if (IS_ERR(ipu_plane->dp)) { | ||
223 | ret = PTR_ERR(ipu_plane->dp); | ||
224 | DRM_ERROR("failed to get dp flow: %d\n", ret); | ||
225 | goto err_out; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | return 0; | ||
230 | err_out: | ||
231 | ipu_plane_put_resources(ipu_plane); | ||
232 | |||
233 | return ret; | ||
234 | } | ||
235 | |||
236 | void ipu_plane_enable(struct ipu_plane *ipu_plane) | ||
237 | { | ||
238 | if (ipu_plane->dp) | ||
239 | ipu_dp_enable(ipu_plane->ipu); | ||
240 | ipu_dmfc_enable_channel(ipu_plane->dmfc); | ||
241 | ipu_idmac_enable_channel(ipu_plane->ipu_ch); | ||
242 | if (ipu_plane->dp) | ||
243 | ipu_dp_enable_channel(ipu_plane->dp); | ||
244 | |||
245 | ipu_plane->enabled = true; | ||
246 | } | ||
247 | |||
248 | void ipu_plane_disable(struct ipu_plane *ipu_plane) | ||
249 | { | ||
250 | ipu_plane->enabled = false; | ||
251 | |||
252 | ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50); | ||
253 | |||
254 | if (ipu_plane->dp) | ||
255 | ipu_dp_disable_channel(ipu_plane->dp); | ||
256 | ipu_idmac_disable_channel(ipu_plane->ipu_ch); | ||
257 | ipu_dmfc_disable_channel(ipu_plane->dmfc); | ||
258 | if (ipu_plane->dp) | ||
259 | ipu_dp_disable(ipu_plane->ipu); | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | * drm_plane API | ||
264 | */ | ||
265 | |||
266 | static int ipu_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, | ||
267 | struct drm_framebuffer *fb, int crtc_x, int crtc_y, | ||
268 | unsigned int crtc_w, unsigned int crtc_h, | ||
269 | uint32_t src_x, uint32_t src_y, | ||
270 | uint32_t src_w, uint32_t src_h) | ||
271 | { | ||
272 | struct ipu_plane *ipu_plane = to_ipu_plane(plane); | ||
273 | int ret = 0; | ||
274 | |||
275 | DRM_DEBUG_KMS("plane - %p\n", plane); | ||
276 | |||
277 | if (!ipu_plane->enabled) | ||
278 | ret = ipu_plane_get_resources(ipu_plane); | ||
279 | if (ret < 0) | ||
280 | return ret; | ||
281 | |||
282 | ret = ipu_plane_mode_set(ipu_plane, crtc, &crtc->hwmode, fb, | ||
283 | crtc_x, crtc_y, crtc_w, crtc_h, | ||
284 | src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16); | ||
285 | if (ret < 0) { | ||
286 | ipu_plane_put_resources(ipu_plane); | ||
287 | return ret; | ||
288 | } | ||
289 | |||
290 | if (crtc != plane->crtc) | ||
291 | dev_info(plane->dev->dev, "crtc change: %p -> %p\n", | ||
292 | plane->crtc, crtc); | ||
293 | plane->crtc = crtc; | ||
294 | |||
295 | if (!ipu_plane->enabled) | ||
296 | ipu_plane_enable(ipu_plane); | ||
297 | |||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | static int ipu_disable_plane(struct drm_plane *plane) | ||
302 | { | ||
303 | struct ipu_plane *ipu_plane = to_ipu_plane(plane); | ||
304 | |||
305 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
306 | |||
307 | if (ipu_plane->enabled) | ||
308 | ipu_plane_disable(ipu_plane); | ||
309 | |||
310 | ipu_plane_put_resources(ipu_plane); | ||
311 | |||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | static void ipu_plane_destroy(struct drm_plane *plane) | ||
316 | { | ||
317 | struct ipu_plane *ipu_plane = to_ipu_plane(plane); | ||
318 | |||
319 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
320 | |||
321 | ipu_disable_plane(plane); | ||
322 | drm_plane_cleanup(plane); | ||
323 | kfree(ipu_plane); | ||
324 | } | ||
325 | |||
326 | static struct drm_plane_funcs ipu_plane_funcs = { | ||
327 | .update_plane = ipu_update_plane, | ||
328 | .disable_plane = ipu_disable_plane, | ||
329 | .destroy = ipu_plane_destroy, | ||
330 | }; | ||
331 | |||
332 | struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu, | ||
333 | int dma, int dp, unsigned int possible_crtcs, | ||
334 | bool priv) | ||
335 | { | ||
336 | struct ipu_plane *ipu_plane; | ||
337 | int ret; | ||
338 | |||
339 | DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n", | ||
340 | dma, dp, possible_crtcs); | ||
341 | |||
342 | ipu_plane = kzalloc(sizeof(*ipu_plane), GFP_KERNEL); | ||
343 | if (!ipu_plane) { | ||
344 | DRM_ERROR("failed to allocate plane\n"); | ||
345 | return ERR_PTR(-ENOMEM); | ||
346 | } | ||
347 | |||
348 | ipu_plane->ipu = ipu; | ||
349 | ipu_plane->dma = dma; | ||
350 | ipu_plane->dp_flow = dp; | ||
351 | |||
352 | ret = drm_plane_init(dev, &ipu_plane->base, possible_crtcs, | ||
353 | &ipu_plane_funcs, ipu_plane_formats, | ||
354 | ARRAY_SIZE(ipu_plane_formats), | ||
355 | priv); | ||
356 | if (ret) { | ||
357 | DRM_ERROR("failed to initialize plane\n"); | ||
358 | kfree(ipu_plane); | ||
359 | return ERR_PTR(ret); | ||
360 | } | ||
361 | |||
362 | return ipu_plane; | ||
363 | } | ||
diff --git a/drivers/gpu/drm/imx/ipuv3-plane.h b/drivers/gpu/drm/imx/ipuv3-plane.h new file mode 100644 index 000000000000..c0aae5bcb5d4 --- /dev/null +++ b/drivers/gpu/drm/imx/ipuv3-plane.h | |||
@@ -0,0 +1,55 @@ | |||
1 | #ifndef __IPUV3_PLANE_H__ | ||
2 | #define __IPUV3_PLANE_H__ | ||
3 | |||
4 | #include <drm/drm_crtc.h> /* drm_plane */ | ||
5 | |||
6 | struct drm_plane; | ||
7 | struct drm_device; | ||
8 | struct ipu_soc; | ||
9 | struct drm_crtc; | ||
10 | struct drm_framebuffer; | ||
11 | |||
12 | struct ipuv3_channel; | ||
13 | struct dmfc_channel; | ||
14 | struct ipu_dp; | ||
15 | |||
16 | struct ipu_plane { | ||
17 | struct drm_plane base; | ||
18 | |||
19 | struct ipu_soc *ipu; | ||
20 | struct ipuv3_channel *ipu_ch; | ||
21 | struct dmfc_channel *dmfc; | ||
22 | struct ipu_dp *dp; | ||
23 | |||
24 | int dma; | ||
25 | int dp_flow; | ||
26 | |||
27 | int x; | ||
28 | int y; | ||
29 | |||
30 | bool enabled; | ||
31 | }; | ||
32 | |||
33 | struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu, | ||
34 | int dma, int dp, unsigned int possible_crtcs, | ||
35 | bool priv); | ||
36 | |||
37 | /* Init IDMAC, DMFC, DP */ | ||
38 | int ipu_plane_mode_set(struct ipu_plane *plane, struct drm_crtc *crtc, | ||
39 | struct drm_display_mode *mode, | ||
40 | struct drm_framebuffer *fb, int crtc_x, int crtc_y, | ||
41 | unsigned int crtc_w, unsigned int crtc_h, | ||
42 | uint32_t src_x, uint32_t src_y, uint32_t src_w, | ||
43 | uint32_t src_h); | ||
44 | |||
45 | void ipu_plane_enable(struct ipu_plane *plane); | ||
46 | void ipu_plane_disable(struct ipu_plane *plane); | ||
47 | int ipu_plane_set_base(struct ipu_plane *plane, struct drm_framebuffer *fb, | ||
48 | int x, int y); | ||
49 | |||
50 | int ipu_plane_get_resources(struct ipu_plane *plane); | ||
51 | void ipu_plane_put_resources(struct ipu_plane *plane); | ||
52 | |||
53 | int ipu_plane_irq(struct ipu_plane *plane); | ||
54 | |||
55 | #endif | ||
diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c new file mode 100644 index 000000000000..8a76a5c1c34b --- /dev/null +++ b/drivers/gpu/drm/imx/parallel-display.c | |||
@@ -0,0 +1,295 @@ | |||
1 | /* | ||
2 | * i.MX drm driver - parallel display implementation | ||
3 | * | ||
4 | * Copyright (C) 2012 Sascha Hauer, Pengutronix | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version 2 | ||
9 | * of the License, or (at your option) any later version. | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
18 | * MA 02110-1301, USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/component.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <drm/drmP.h> | ||
24 | #include <drm/drm_fb_helper.h> | ||
25 | #include <drm/drm_crtc_helper.h> | ||
26 | #include <drm/drm_panel.h> | ||
27 | #include <linux/videodev2.h> | ||
28 | #include <video/of_display_timing.h> | ||
29 | |||
30 | #include "imx-drm.h" | ||
31 | |||
32 | #define con_to_imxpd(x) container_of(x, struct imx_parallel_display, connector) | ||
33 | #define enc_to_imxpd(x) container_of(x, struct imx_parallel_display, encoder) | ||
34 | |||
35 | struct imx_parallel_display { | ||
36 | struct drm_connector connector; | ||
37 | struct drm_encoder encoder; | ||
38 | struct device *dev; | ||
39 | void *edid; | ||
40 | int edid_len; | ||
41 | u32 interface_pix_fmt; | ||
42 | int mode_valid; | ||
43 | struct drm_display_mode mode; | ||
44 | struct drm_panel *panel; | ||
45 | }; | ||
46 | |||
47 | static enum drm_connector_status imx_pd_connector_detect( | ||
48 | struct drm_connector *connector, bool force) | ||
49 | { | ||
50 | return connector_status_connected; | ||
51 | } | ||
52 | |||
53 | static int imx_pd_connector_get_modes(struct drm_connector *connector) | ||
54 | { | ||
55 | struct imx_parallel_display *imxpd = con_to_imxpd(connector); | ||
56 | struct device_node *np = imxpd->dev->of_node; | ||
57 | int num_modes = 0; | ||
58 | |||
59 | if (imxpd->panel && imxpd->panel->funcs && | ||
60 | imxpd->panel->funcs->get_modes) { | ||
61 | num_modes = imxpd->panel->funcs->get_modes(imxpd->panel); | ||
62 | if (num_modes > 0) | ||
63 | return num_modes; | ||
64 | } | ||
65 | |||
66 | if (imxpd->edid) { | ||
67 | drm_mode_connector_update_edid_property(connector, imxpd->edid); | ||
68 | num_modes = drm_add_edid_modes(connector, imxpd->edid); | ||
69 | } | ||
70 | |||
71 | if (imxpd->mode_valid) { | ||
72 | struct drm_display_mode *mode = drm_mode_create(connector->dev); | ||
73 | |||
74 | if (!mode) | ||
75 | return -EINVAL; | ||
76 | drm_mode_copy(mode, &imxpd->mode); | ||
77 | mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, | ||
78 | drm_mode_probed_add(connector, mode); | ||
79 | num_modes++; | ||
80 | } | ||
81 | |||
82 | if (np) { | ||
83 | struct drm_display_mode *mode = drm_mode_create(connector->dev); | ||
84 | |||
85 | if (!mode) | ||
86 | return -EINVAL; | ||
87 | of_get_drm_display_mode(np, &imxpd->mode, OF_USE_NATIVE_MODE); | ||
88 | drm_mode_copy(mode, &imxpd->mode); | ||
89 | mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, | ||
90 | drm_mode_probed_add(connector, mode); | ||
91 | num_modes++; | ||
92 | } | ||
93 | |||
94 | return num_modes; | ||
95 | } | ||
96 | |||
97 | static struct drm_encoder *imx_pd_connector_best_encoder( | ||
98 | struct drm_connector *connector) | ||
99 | { | ||
100 | struct imx_parallel_display *imxpd = con_to_imxpd(connector); | ||
101 | |||
102 | return &imxpd->encoder; | ||
103 | } | ||
104 | |||
105 | static void imx_pd_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
106 | { | ||
107 | struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); | ||
108 | |||
109 | if (mode != DRM_MODE_DPMS_ON) | ||
110 | drm_panel_disable(imxpd->panel); | ||
111 | else | ||
112 | drm_panel_enable(imxpd->panel); | ||
113 | } | ||
114 | |||
115 | static bool imx_pd_encoder_mode_fixup(struct drm_encoder *encoder, | ||
116 | const struct drm_display_mode *mode, | ||
117 | struct drm_display_mode *adjusted_mode) | ||
118 | { | ||
119 | return true; | ||
120 | } | ||
121 | |||
122 | static void imx_pd_encoder_prepare(struct drm_encoder *encoder) | ||
123 | { | ||
124 | struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); | ||
125 | |||
126 | imx_drm_panel_format(encoder, imxpd->interface_pix_fmt); | ||
127 | } | ||
128 | |||
129 | static void imx_pd_encoder_commit(struct drm_encoder *encoder) | ||
130 | { | ||
131 | } | ||
132 | |||
133 | static void imx_pd_encoder_mode_set(struct drm_encoder *encoder, | ||
134 | struct drm_display_mode *mode, | ||
135 | struct drm_display_mode *adjusted_mode) | ||
136 | { | ||
137 | } | ||
138 | |||
139 | static void imx_pd_encoder_disable(struct drm_encoder *encoder) | ||
140 | { | ||
141 | } | ||
142 | |||
143 | static struct drm_connector_funcs imx_pd_connector_funcs = { | ||
144 | .dpms = drm_helper_connector_dpms, | ||
145 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
146 | .detect = imx_pd_connector_detect, | ||
147 | .destroy = imx_drm_connector_destroy, | ||
148 | }; | ||
149 | |||
150 | static struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = { | ||
151 | .get_modes = imx_pd_connector_get_modes, | ||
152 | .best_encoder = imx_pd_connector_best_encoder, | ||
153 | }; | ||
154 | |||
155 | static struct drm_encoder_funcs imx_pd_encoder_funcs = { | ||
156 | .destroy = imx_drm_encoder_destroy, | ||
157 | }; | ||
158 | |||
159 | static struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = { | ||
160 | .dpms = imx_pd_encoder_dpms, | ||
161 | .mode_fixup = imx_pd_encoder_mode_fixup, | ||
162 | .prepare = imx_pd_encoder_prepare, | ||
163 | .commit = imx_pd_encoder_commit, | ||
164 | .mode_set = imx_pd_encoder_mode_set, | ||
165 | .disable = imx_pd_encoder_disable, | ||
166 | }; | ||
167 | |||
168 | static int imx_pd_register(struct drm_device *drm, | ||
169 | struct imx_parallel_display *imxpd) | ||
170 | { | ||
171 | int ret; | ||
172 | |||
173 | ret = imx_drm_encoder_parse_of(drm, &imxpd->encoder, | ||
174 | imxpd->dev->of_node); | ||
175 | if (ret) | ||
176 | return ret; | ||
177 | |||
178 | /* set the connector's dpms to OFF so that | ||
179 | * drm_helper_connector_dpms() won't return | ||
180 | * immediately since the current state is ON | ||
181 | * at this point. | ||
182 | */ | ||
183 | imxpd->connector.dpms = DRM_MODE_DPMS_OFF; | ||
184 | |||
185 | drm_encoder_helper_add(&imxpd->encoder, &imx_pd_encoder_helper_funcs); | ||
186 | drm_encoder_init(drm, &imxpd->encoder, &imx_pd_encoder_funcs, | ||
187 | DRM_MODE_ENCODER_NONE); | ||
188 | |||
189 | drm_connector_helper_add(&imxpd->connector, | ||
190 | &imx_pd_connector_helper_funcs); | ||
191 | drm_connector_init(drm, &imxpd->connector, &imx_pd_connector_funcs, | ||
192 | DRM_MODE_CONNECTOR_VGA); | ||
193 | |||
194 | if (imxpd->panel) | ||
195 | drm_panel_attach(imxpd->panel, &imxpd->connector); | ||
196 | |||
197 | drm_mode_connector_attach_encoder(&imxpd->connector, &imxpd->encoder); | ||
198 | |||
199 | imxpd->connector.encoder = &imxpd->encoder; | ||
200 | |||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static int imx_pd_bind(struct device *dev, struct device *master, void *data) | ||
205 | { | ||
206 | struct drm_device *drm = data; | ||
207 | struct device_node *np = dev->of_node; | ||
208 | struct device_node *panel_node; | ||
209 | const u8 *edidp; | ||
210 | struct imx_parallel_display *imxpd; | ||
211 | int ret; | ||
212 | const char *fmt; | ||
213 | |||
214 | imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL); | ||
215 | if (!imxpd) | ||
216 | return -ENOMEM; | ||
217 | |||
218 | edidp = of_get_property(np, "edid", &imxpd->edid_len); | ||
219 | if (edidp) | ||
220 | imxpd->edid = kmemdup(edidp, imxpd->edid_len, GFP_KERNEL); | ||
221 | |||
222 | ret = of_property_read_string(np, "interface-pix-fmt", &fmt); | ||
223 | if (!ret) { | ||
224 | if (!strcmp(fmt, "rgb24")) | ||
225 | imxpd->interface_pix_fmt = V4L2_PIX_FMT_RGB24; | ||
226 | else if (!strcmp(fmt, "rgb565")) | ||
227 | imxpd->interface_pix_fmt = V4L2_PIX_FMT_RGB565; | ||
228 | else if (!strcmp(fmt, "bgr666")) | ||
229 | imxpd->interface_pix_fmt = V4L2_PIX_FMT_BGR666; | ||
230 | else if (!strcmp(fmt, "lvds666")) | ||
231 | imxpd->interface_pix_fmt = | ||
232 | v4l2_fourcc('L', 'V', 'D', '6'); | ||
233 | } | ||
234 | |||
235 | panel_node = of_parse_phandle(np, "fsl,panel", 0); | ||
236 | if (panel_node) | ||
237 | imxpd->panel = of_drm_find_panel(panel_node); | ||
238 | |||
239 | imxpd->dev = dev; | ||
240 | |||
241 | ret = imx_pd_register(drm, imxpd); | ||
242 | if (ret) | ||
243 | return ret; | ||
244 | |||
245 | dev_set_drvdata(dev, imxpd); | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | static void imx_pd_unbind(struct device *dev, struct device *master, | ||
251 | void *data) | ||
252 | { | ||
253 | struct imx_parallel_display *imxpd = dev_get_drvdata(dev); | ||
254 | |||
255 | imxpd->encoder.funcs->destroy(&imxpd->encoder); | ||
256 | imxpd->connector.funcs->destroy(&imxpd->connector); | ||
257 | } | ||
258 | |||
259 | static const struct component_ops imx_pd_ops = { | ||
260 | .bind = imx_pd_bind, | ||
261 | .unbind = imx_pd_unbind, | ||
262 | }; | ||
263 | |||
264 | static int imx_pd_probe(struct platform_device *pdev) | ||
265 | { | ||
266 | return component_add(&pdev->dev, &imx_pd_ops); | ||
267 | } | ||
268 | |||
269 | static int imx_pd_remove(struct platform_device *pdev) | ||
270 | { | ||
271 | component_del(&pdev->dev, &imx_pd_ops); | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static const struct of_device_id imx_pd_dt_ids[] = { | ||
276 | { .compatible = "fsl,imx-parallel-display", }, | ||
277 | { /* sentinel */ } | ||
278 | }; | ||
279 | MODULE_DEVICE_TABLE(of, imx_pd_dt_ids); | ||
280 | |||
281 | static struct platform_driver imx_pd_driver = { | ||
282 | .probe = imx_pd_probe, | ||
283 | .remove = imx_pd_remove, | ||
284 | .driver = { | ||
285 | .of_match_table = imx_pd_dt_ids, | ||
286 | .name = "imx-parallel-display", | ||
287 | }, | ||
288 | }; | ||
289 | |||
290 | module_platform_driver(imx_pd_driver); | ||
291 | |||
292 | MODULE_DESCRIPTION("i.MX parallel display driver"); | ||
293 | MODULE_AUTHOR("Sascha Hauer, Pengutronix"); | ||
294 | MODULE_LICENSE("GPL"); | ||
295 | MODULE_ALIAS("platform:imx-parallel-display"); | ||