diff options
Diffstat (limited to 'drivers/gpu/drm/imx')
| -rw-r--r-- | drivers/gpu/drm/imx/Kconfig | 54 | ||||
| -rw-r--r-- | drivers/gpu/drm/imx/Makefile | 12 | ||||
| -rw-r--r-- | drivers/gpu/drm/imx/imx-drm-core.c | 703 | ||||
| -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 | 610 | ||||
| -rw-r--r-- | drivers/gpu/drm/imx/imx-tve.c | 731 | ||||
| -rw-r--r-- | drivers/gpu/drm/imx/ipuv3-crtc.c | 513 | ||||
| -rw-r--r-- | drivers/gpu/drm/imx/ipuv3-plane.c | 394 | ||||
| -rw-r--r-- | drivers/gpu/drm/imx/ipuv3-plane.h | 57 | ||||
| -rw-r--r-- | drivers/gpu/drm/imx/parallel-display.c | 298 | 
12 files changed, 6226 insertions, 0 deletions
diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig new file mode 100644 index 000000000000..ab31848e92cf --- /dev/null +++ b/drivers/gpu/drm/imx/Kconfig  | |||
| @@ -0,0 +1,54 @@ | |||
| 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 | depends on IMX_IPUV3_CORE | ||
| 10 | help | ||
| 11 | enable i.MX graphics support | ||
| 12 | |||
| 13 | config DRM_IMX_FB_HELPER | ||
| 14 | tristate "provide legacy framebuffer /dev/fb0" | ||
| 15 | select DRM_KMS_CMA_HELPER | ||
| 16 | depends on DRM_IMX | ||
| 17 | help | ||
| 18 | The DRM framework can provide a legacy /dev/fb0 framebuffer | ||
| 19 | for your device. This is necessary to get a framebuffer console | ||
| 20 | and also for applications using the legacy framebuffer API | ||
| 21 | |||
| 22 | config DRM_IMX_PARALLEL_DISPLAY | ||
| 23 | tristate "Support for parallel displays" | ||
| 24 | select DRM_PANEL | ||
| 25 | depends on DRM_IMX | ||
| 26 | select VIDEOMODE_HELPERS | ||
| 27 | |||
| 28 | config DRM_IMX_TVE | ||
| 29 | tristate "Support for TV and VGA displays" | ||
| 30 | depends on DRM_IMX | ||
| 31 | select REGMAP_MMIO | ||
| 32 | help | ||
| 33 | Choose this to enable the internal Television Encoder (TVe) | ||
| 34 | found on i.MX53 processors. | ||
| 35 | |||
| 36 | config DRM_IMX_LDB | ||
| 37 | tristate "Support for LVDS displays" | ||
| 38 | depends on DRM_IMX && MFD_SYSCON | ||
| 39 | help | ||
| 40 | Choose this to enable the internal LVDS Display Bridge (LDB) | ||
| 41 | found on i.MX53 and i.MX6 processors. | ||
| 42 | |||
| 43 | config DRM_IMX_IPUV3 | ||
| 44 | tristate | ||
| 45 | depends on DRM_IMX | ||
| 46 | depends on IMX_IPUV3_CORE | ||
| 47 | default y if DRM_IMX=y | ||
| 48 | default m if DRM_IMX=m | ||
| 49 | |||
| 50 | config DRM_IMX_HDMI | ||
| 51 | tristate "Freescale i.MX DRM HDMI" | ||
| 52 | depends on DRM_IMX | ||
| 53 | help | ||
| 54 | 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..b250130debc8 --- /dev/null +++ b/drivers/gpu/drm/imx/imx-drm-core.c  | |||
| @@ -0,0 +1,703 @@ | |||
| 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_component { | ||
| 34 | struct device_node *of_node; | ||
| 35 | struct list_head list; | ||
| 36 | }; | ||
| 37 | |||
| 38 | struct imx_drm_device { | ||
| 39 | struct drm_device *drm; | ||
| 40 | struct imx_drm_crtc *crtc[MAX_CRTC]; | ||
| 41 | int pipes; | ||
| 42 | struct drm_fbdev_cma *fbhelper; | ||
| 43 | }; | ||
| 44 | |||
| 45 | struct imx_drm_crtc { | ||
| 46 | struct drm_crtc *crtc; | ||
| 47 | int pipe; | ||
| 48 | struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; | ||
| 49 | struct device_node *port; | ||
| 50 | }; | ||
| 51 | |||
| 52 | static int legacyfb_depth = 16; | ||
| 53 | module_param(legacyfb_depth, int, 0444); | ||
| 54 | |||
| 55 | int imx_drm_crtc_id(struct imx_drm_crtc *crtc) | ||
| 56 | { | ||
| 57 | return crtc->pipe; | ||
| 58 | } | ||
| 59 | EXPORT_SYMBOL_GPL(imx_drm_crtc_id); | ||
| 60 | |||
| 61 | static void imx_drm_driver_lastclose(struct drm_device *drm) | ||
| 62 | { | ||
| 63 | #if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) | ||
| 64 | struct imx_drm_device *imxdrm = drm->dev_private; | ||
| 65 | |||
| 66 | if (imxdrm->fbhelper) | ||
| 67 | drm_fbdev_cma_restore_mode(imxdrm->fbhelper); | ||
| 68 | #endif | ||
| 69 | } | ||
| 70 | |||
| 71 | static int imx_drm_driver_unload(struct drm_device *drm) | ||
| 72 | { | ||
| 73 | #if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) | ||
| 74 | struct imx_drm_device *imxdrm = drm->dev_private; | ||
| 75 | #endif | ||
| 76 | |||
| 77 | drm_kms_helper_poll_fini(drm); | ||
| 78 | |||
| 79 | #if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) | ||
| 80 | if (imxdrm->fbhelper) | ||
| 81 | drm_fbdev_cma_fini(imxdrm->fbhelper); | ||
| 82 | #endif | ||
| 83 | |||
| 84 | component_unbind_all(drm->dev, drm); | ||
| 85 | |||
| 86 | drm_vblank_cleanup(drm); | ||
| 87 | drm_mode_config_cleanup(drm); | ||
| 88 | |||
| 89 | platform_set_drvdata(drm->platformdev, NULL); | ||
| 90 | |||
| 91 | return 0; | ||
| 92 | } | ||
| 93 | |||
| 94 | static struct imx_drm_crtc *imx_drm_find_crtc(struct drm_crtc *crtc) | ||
| 95 | { | ||
| 96 | struct imx_drm_device *imxdrm = crtc->dev->dev_private; | ||
| 97 | unsigned i; | ||
| 98 | |||
| 99 | for (i = 0; i < MAX_CRTC; i++) | ||
| 100 | if (imxdrm->crtc[i] && imxdrm->crtc[i]->crtc == crtc) | ||
| 101 | return imxdrm->crtc[i]; | ||
| 102 | |||
| 103 | return NULL; | ||
| 104 | } | ||
| 105 | |||
| 106 | int imx_drm_panel_format_pins(struct drm_encoder *encoder, | ||
| 107 | u32 interface_pix_fmt, int hsync_pin, int vsync_pin) | ||
| 108 | { | ||
| 109 | struct imx_drm_crtc_helper_funcs *helper; | ||
| 110 | struct imx_drm_crtc *imx_crtc; | ||
| 111 | |||
| 112 | imx_crtc = imx_drm_find_crtc(encoder->crtc); | ||
| 113 | if (!imx_crtc) | ||
| 114 | return -EINVAL; | ||
| 115 | |||
| 116 | helper = &imx_crtc->imx_drm_helper_funcs; | ||
| 117 | if (helper->set_interface_pix_fmt) | ||
| 118 | return helper->set_interface_pix_fmt(encoder->crtc, | ||
| 119 | encoder->encoder_type, interface_pix_fmt, | ||
| 120 | hsync_pin, vsync_pin); | ||
| 121 | return 0; | ||
| 122 | } | ||
| 123 | EXPORT_SYMBOL_GPL(imx_drm_panel_format_pins); | ||
| 124 | |||
| 125 | int imx_drm_panel_format(struct drm_encoder *encoder, u32 interface_pix_fmt) | ||
| 126 | { | ||
| 127 | return imx_drm_panel_format_pins(encoder, interface_pix_fmt, 2, 3); | ||
| 128 | } | ||
| 129 | EXPORT_SYMBOL_GPL(imx_drm_panel_format); | ||
| 130 | |||
| 131 | int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc) | ||
| 132 | { | ||
| 133 | return drm_vblank_get(imx_drm_crtc->crtc->dev, imx_drm_crtc->pipe); | ||
| 134 | } | ||
| 135 | EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_get); | ||
| 136 | |||
| 137 | void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc) | ||
| 138 | { | ||
| 139 | drm_vblank_put(imx_drm_crtc->crtc->dev, imx_drm_crtc->pipe); | ||
| 140 | } | ||
| 141 | EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_put); | ||
| 142 | |||
| 143 | void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc) | ||
| 144 | { | ||
| 145 | drm_handle_vblank(imx_drm_crtc->crtc->dev, imx_drm_crtc->pipe); | ||
| 146 | } | ||
| 147 | EXPORT_SYMBOL_GPL(imx_drm_handle_vblank); | ||
| 148 | |||
| 149 | static int imx_drm_enable_vblank(struct drm_device *drm, int crtc) | ||
| 150 | { | ||
| 151 | struct imx_drm_device *imxdrm = drm->dev_private; | ||
| 152 | struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[crtc]; | ||
| 153 | int ret; | ||
| 154 | |||
| 155 | if (!imx_drm_crtc) | ||
| 156 | return -EINVAL; | ||
| 157 | |||
| 158 | if (!imx_drm_crtc->imx_drm_helper_funcs.enable_vblank) | ||
| 159 | return -ENOSYS; | ||
| 160 | |||
| 161 | ret = imx_drm_crtc->imx_drm_helper_funcs.enable_vblank( | ||
| 162 | imx_drm_crtc->crtc); | ||
| 163 | |||
| 164 | return ret; | ||
| 165 | } | ||
| 166 | |||
| 167 | static void imx_drm_disable_vblank(struct drm_device *drm, int crtc) | ||
| 168 | { | ||
| 169 | struct imx_drm_device *imxdrm = drm->dev_private; | ||
| 170 | struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[crtc]; | ||
| 171 | |||
| 172 | if (!imx_drm_crtc) | ||
| 173 | return; | ||
| 174 | |||
| 175 | if (!imx_drm_crtc->imx_drm_helper_funcs.disable_vblank) | ||
| 176 | return; | ||
| 177 | |||
| 178 | imx_drm_crtc->imx_drm_helper_funcs.disable_vblank(imx_drm_crtc->crtc); | ||
| 179 | } | ||
| 180 | |||
| 181 | static void imx_drm_driver_preclose(struct drm_device *drm, | ||
| 182 | struct drm_file *file) | ||
| 183 | { | ||
| 184 | int i; | ||
| 185 | |||
| 186 | if (!file->is_master) | ||
| 187 | return; | ||
| 188 | |||
| 189 | for (i = 0; i < MAX_CRTC; i++) | ||
| 190 | imx_drm_disable_vblank(drm, i); | ||
| 191 | } | ||
| 192 | |||
| 193 | static const struct file_operations imx_drm_driver_fops = { | ||
| 194 | .owner = THIS_MODULE, | ||
| 195 | .open = drm_open, | ||
| 196 | .release = drm_release, | ||
| 197 | .unlocked_ioctl = drm_ioctl, | ||
| 198 | .mmap = drm_gem_cma_mmap, | ||
| 199 | .poll = drm_poll, | ||
| 200 | .read = drm_read, | ||
| 201 | .llseek = noop_llseek, | ||
| 202 | }; | ||
| 203 | |||
| 204 | void imx_drm_connector_destroy(struct drm_connector *connector) | ||
| 205 | { | ||
| 206 | drm_connector_unregister(connector); | ||
| 207 | drm_connector_cleanup(connector); | ||
| 208 | } | ||
| 209 | EXPORT_SYMBOL_GPL(imx_drm_connector_destroy); | ||
| 210 | |||
| 211 | void imx_drm_encoder_destroy(struct drm_encoder *encoder) | ||
| 212 | { | ||
| 213 | drm_encoder_cleanup(encoder); | ||
| 214 | } | ||
| 215 | EXPORT_SYMBOL_GPL(imx_drm_encoder_destroy); | ||
| 216 | |||
| 217 | static void imx_drm_output_poll_changed(struct drm_device *drm) | ||
| 218 | { | ||
| 219 | #if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) | ||
| 220 | struct imx_drm_device *imxdrm = drm->dev_private; | ||
| 221 | |||
| 222 | drm_fbdev_cma_hotplug_event(imxdrm->fbhelper); | ||
| 223 | #endif | ||
| 224 | } | ||
| 225 | |||
| 226 | static struct drm_mode_config_funcs imx_drm_mode_config_funcs = { | ||
| 227 | .fb_create = drm_fb_cma_create, | ||
| 228 | .output_poll_changed = imx_drm_output_poll_changed, | ||
| 229 | }; | ||
| 230 | |||
| 231 | /* | ||
| 232 | * Main DRM initialisation. This binds, initialises and registers | ||
| 233 | * with DRM the subcomponents of the driver. | ||
| 234 | */ | ||
| 235 | static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) | ||
| 236 | { | ||
| 237 | struct imx_drm_device *imxdrm; | ||
| 238 | struct drm_connector *connector; | ||
| 239 | int ret; | ||
| 240 | |||
| 241 | imxdrm = devm_kzalloc(drm->dev, sizeof(*imxdrm), GFP_KERNEL); | ||
| 242 | if (!imxdrm) | ||
| 243 | return -ENOMEM; | ||
| 244 | |||
| 245 | imxdrm->drm = drm; | ||
| 246 | |||
| 247 | drm->dev_private = imxdrm; | ||
| 248 | |||
| 249 | /* | ||
| 250 | * enable drm irq mode. | ||
| 251 | * - with irq_enabled = true, we can use the vblank feature. | ||
| 252 | * | ||
| 253 | * P.S. note that we wouldn't use drm irq handler but | ||
| 254 | * just specific driver own one instead because | ||
| 255 | * drm framework supports only one irq handler and | ||
| 256 | * drivers can well take care of their interrupts | ||
| 257 | */ | ||
| 258 | drm->irq_enabled = true; | ||
| 259 | |||
| 260 | /* | ||
| 261 | * set max width and height as default value(4096x4096). | ||
| 262 | * this value would be used to check framebuffer size limitation | ||
| 263 | * at drm_mode_addfb(). | ||
| 264 | */ | ||
| 265 | drm->mode_config.min_width = 64; | ||
| 266 | drm->mode_config.min_height = 64; | ||
| 267 | drm->mode_config.max_width = 4096; | ||
| 268 | drm->mode_config.max_height = 4096; | ||
| 269 | drm->mode_config.funcs = &imx_drm_mode_config_funcs; | ||
| 270 | |||
| 271 | drm_mode_config_init(drm); | ||
| 272 | |||
| 273 | ret = drm_vblank_init(drm, MAX_CRTC); | ||
| 274 | if (ret) | ||
| 275 | goto err_kms; | ||
| 276 | |||
| 277 | /* | ||
| 278 | * with vblank_disable_allowed = true, vblank interrupt will be | ||
| 279 | * disabled by drm timer once a current process gives up ownership | ||
| 280 | * of vblank event. (after drm_vblank_put function is called) | ||
| 281 | */ | ||
| 282 | drm->vblank_disable_allowed = true; | ||
| 283 | |||
| 284 | platform_set_drvdata(drm->platformdev, drm); | ||
| 285 | |||
| 286 | /* Now try and bind all our sub-components */ | ||
| 287 | ret = component_bind_all(drm->dev, drm); | ||
| 288 | if (ret) | ||
| 289 | goto err_vblank; | ||
| 290 | |||
| 291 | /* | ||
| 292 | * All components are now added, we can publish the connector sysfs | ||
| 293 | * entries to userspace. This will generate hotplug events and so | ||
| 294 | * userspace will expect to be able to access DRM at this point. | ||
| 295 | */ | ||
| 296 | list_for_each_entry(connector, &drm->mode_config.connector_list, head) { | ||
| 297 | ret = drm_connector_register(connector); | ||
| 298 | if (ret) { | ||
| 299 | dev_err(drm->dev, | ||
| 300 | "[CONNECTOR:%d:%s] drm_connector_register failed: %d\n", | ||
| 301 | connector->base.id, | ||
| 302 | connector->name, ret); | ||
| 303 | goto err_unbind; | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | /* | ||
| 308 | * All components are now initialised, so setup the fb helper. | ||
| 309 | * The fb helper takes copies of key hardware information, so the | ||
| 310 | * crtcs/connectors/encoders must not change after this point. | ||
| 311 | */ | ||
| 312 | #if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) | ||
| 313 | if (legacyfb_depth != 16 && legacyfb_depth != 32) { | ||
| 314 | dev_warn(drm->dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n"); | ||
| 315 | legacyfb_depth = 16; | ||
| 316 | } | ||
| 317 | imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, | ||
| 318 | drm->mode_config.num_crtc, MAX_CRTC); | ||
| 319 | if (IS_ERR(imxdrm->fbhelper)) { | ||
| 320 | ret = PTR_ERR(imxdrm->fbhelper); | ||
| 321 | imxdrm->fbhelper = NULL; | ||
| 322 | goto err_unbind; | ||
| 323 | } | ||
| 324 | #endif | ||
| 325 | |||
| 326 | drm_kms_helper_poll_init(drm); | ||
| 327 | |||
| 328 | return 0; | ||
| 329 | |||
| 330 | err_unbind: | ||
| 331 | component_unbind_all(drm->dev, drm); | ||
| 332 | err_vblank: | ||
| 333 | drm_vblank_cleanup(drm); | ||
| 334 | err_kms: | ||
| 335 | drm_mode_config_cleanup(drm); | ||
| 336 | |||
| 337 | return ret; | ||
| 338 | } | ||
| 339 | |||
| 340 | /* | ||
| 341 | * imx_drm_add_crtc - add a new crtc | ||
| 342 | */ | ||
| 343 | int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, | ||
| 344 | struct imx_drm_crtc **new_crtc, | ||
| 345 | const struct imx_drm_crtc_helper_funcs *imx_drm_helper_funcs, | ||
| 346 | struct device_node *port) | ||
| 347 | { | ||
| 348 | struct imx_drm_device *imxdrm = drm->dev_private; | ||
| 349 | struct imx_drm_crtc *imx_drm_crtc; | ||
| 350 | int ret; | ||
| 351 | |||
| 352 | /* | ||
| 353 | * The vblank arrays are dimensioned by MAX_CRTC - we can't | ||
| 354 | * pass IDs greater than this to those functions. | ||
| 355 | */ | ||
| 356 | if (imxdrm->pipes >= MAX_CRTC) | ||
| 357 | return -EINVAL; | ||
| 358 | |||
| 359 | if (imxdrm->drm->open_count) | ||
| 360 | return -EBUSY; | ||
| 361 | |||
| 362 | imx_drm_crtc = kzalloc(sizeof(*imx_drm_crtc), GFP_KERNEL); | ||
| 363 | if (!imx_drm_crtc) | ||
| 364 | return -ENOMEM; | ||
| 365 | |||
| 366 | imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs; | ||
| 367 | imx_drm_crtc->pipe = imxdrm->pipes++; | ||
| 368 | imx_drm_crtc->port = port; | ||
| 369 | imx_drm_crtc->crtc = crtc; | ||
| 370 | |||
| 371 | imxdrm->crtc[imx_drm_crtc->pipe] = imx_drm_crtc; | ||
| 372 | |||
| 373 | *new_crtc = imx_drm_crtc; | ||
| 374 | |||
| 375 | ret = drm_mode_crtc_set_gamma_size(imx_drm_crtc->crtc, 256); | ||
| 376 | if (ret) | ||
| 377 | goto err_register; | ||
| 378 | |||
| 379 | drm_crtc_helper_add(crtc, | ||
| 380 | imx_drm_crtc->imx_drm_helper_funcs.crtc_helper_funcs); | ||
| 381 | |||
| 382 | drm_crtc_init(drm, crtc, | ||
| 383 | imx_drm_crtc->imx_drm_helper_funcs.crtc_funcs); | ||
| 384 | |||
| 385 | return 0; | ||
| 386 | |||
| 387 | err_register: | ||
| 388 | imxdrm->crtc[imx_drm_crtc->pipe] = NULL; | ||
| 389 | kfree(imx_drm_crtc); | ||
| 390 | return ret; | ||
| 391 | } | ||
| 392 | EXPORT_SYMBOL_GPL(imx_drm_add_crtc); | ||
| 393 | |||
| 394 | /* | ||
| 395 | * imx_drm_remove_crtc - remove a crtc | ||
| 396 | */ | ||
| 397 | int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc) | ||
| 398 | { | ||
| 399 | struct imx_drm_device *imxdrm = imx_drm_crtc->crtc->dev->dev_private; | ||
| 400 | |||
| 401 | drm_crtc_cleanup(imx_drm_crtc->crtc); | ||
| 402 | |||
| 403 | imxdrm->crtc[imx_drm_crtc->pipe] = NULL; | ||
| 404 | |||
| 405 | kfree(imx_drm_crtc); | ||
| 406 | |||
| 407 | return 0; | ||
| 408 | } | ||
| 409 | EXPORT_SYMBOL_GPL(imx_drm_remove_crtc); | ||
| 410 | |||
| 411 | /* | ||
| 412 | * Find the DRM CRTC possible mask for the connected endpoint. | ||
| 413 | * | ||
| 414 | * The encoder possible masks are defined by their position in the | ||
| 415 | * mode_config crtc_list. This means that CRTCs must not be added | ||
| 416 | * or removed once the DRM device has been fully initialised. | ||
| 417 | */ | ||
| 418 | static uint32_t imx_drm_find_crtc_mask(struct imx_drm_device *imxdrm, | ||
| 419 | struct device_node *endpoint) | ||
| 420 | { | ||
| 421 | struct device_node *port; | ||
| 422 | unsigned i; | ||
| 423 | |||
| 424 | port = of_graph_get_remote_port(endpoint); | ||
| 425 | if (!port) | ||
| 426 | return 0; | ||
| 427 | of_node_put(port); | ||
| 428 | |||
| 429 | for (i = 0; i < MAX_CRTC; i++) { | ||
| 430 | struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[i]; | ||
| 431 | |||
| 432 | if (imx_drm_crtc && imx_drm_crtc->port == port) | ||
| 433 | return drm_crtc_mask(imx_drm_crtc->crtc); | ||
| 434 | } | ||
| 435 | |||
| 436 | return 0; | ||
| 437 | } | ||
| 438 | |||
| 439 | static struct device_node *imx_drm_of_get_next_endpoint( | ||
| 440 | const struct device_node *parent, struct device_node *prev) | ||
| 441 | { | ||
| 442 | struct device_node *node = of_graph_get_next_endpoint(parent, prev); | ||
| 443 | |||
| 444 | of_node_put(prev); | ||
| 445 | return node; | ||
| 446 | } | ||
| 447 | |||
| 448 | int imx_drm_encoder_parse_of(struct drm_device *drm, | ||
| 449 | struct drm_encoder *encoder, struct device_node *np) | ||
| 450 | { | ||
| 451 | struct imx_drm_device *imxdrm = drm->dev_private; | ||
| 452 | struct device_node *ep = NULL; | ||
| 453 | uint32_t crtc_mask = 0; | ||
| 454 | int i; | ||
| 455 | |||
| 456 | for (i = 0; ; i++) { | ||
| 457 | u32 mask; | ||
| 458 | |||
| 459 | ep = imx_drm_of_get_next_endpoint(np, ep); | ||
| 460 | if (!ep) | ||
| 461 | break; | ||
| 462 | |||
| 463 | mask = imx_drm_find_crtc_mask(imxdrm, ep); | ||
| 464 | |||
| 465 | /* | ||
| 466 | * If we failed to find the CRTC(s) which this encoder is | ||
| 467 | * supposed to be connected to, it's because the CRTC has | ||
| 468 | * not been registered yet. Defer probing, and hope that | ||
| 469 | * the required CRTC is added later. | ||
| 470 | */ | ||
| 471 | if (mask == 0) | ||
| 472 | return -EPROBE_DEFER; | ||
| 473 | |||
| 474 | crtc_mask |= mask; | ||
| 475 | } | ||
| 476 | |||
| 477 | of_node_put(ep); | ||
| 478 | if (i == 0) | ||
| 479 | return -ENOENT; | ||
| 480 | |||
| 481 | encoder->possible_crtcs = crtc_mask; | ||
| 482 | |||
| 483 | /* FIXME: this is the mask of outputs which can clone this output. */ | ||
| 484 | encoder->possible_clones = ~0; | ||
| 485 | |||
| 486 | return 0; | ||
| 487 | } | ||
| 488 | EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of); | ||
| 489 | |||
| 490 | /* | ||
| 491 | * @node: device tree node containing encoder input ports | ||
| 492 | * @encoder: drm_encoder | ||
| 493 | */ | ||
| 494 | int imx_drm_encoder_get_mux_id(struct device_node *node, | ||
| 495 | struct drm_encoder *encoder) | ||
| 496 | { | ||
| 497 | struct imx_drm_crtc *imx_crtc = imx_drm_find_crtc(encoder->crtc); | ||
| 498 | struct device_node *ep = NULL; | ||
| 499 | struct of_endpoint endpoint; | ||
| 500 | struct device_node *port; | ||
| 501 | int ret; | ||
| 502 | |||
| 503 | if (!node || !imx_crtc) | ||
| 504 | return -EINVAL; | ||
| 505 | |||
| 506 | do { | ||
| 507 | ep = imx_drm_of_get_next_endpoint(node, ep); | ||
| 508 | if (!ep) | ||
| 509 | break; | ||
| 510 | |||
| 511 | port = of_graph_get_remote_port(ep); | ||
| 512 | of_node_put(port); | ||
| 513 | if (port == imx_crtc->port) { | ||
| 514 | ret = of_graph_parse_endpoint(ep, &endpoint); | ||
| 515 | return ret ? ret : endpoint.port; | ||
| 516 | } | ||
| 517 | } while (ep); | ||
| 518 | |||
| 519 | return -EINVAL; | ||
| 520 | } | ||
| 521 | EXPORT_SYMBOL_GPL(imx_drm_encoder_get_mux_id); | ||
| 522 | |||
| 523 | static const struct drm_ioctl_desc imx_drm_ioctls[] = { | ||
| 524 | /* none so far */ | ||
| 525 | }; | ||
| 526 | |||
| 527 | static struct drm_driver imx_drm_driver = { | ||
| 528 | .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, | ||
| 529 | .load = imx_drm_driver_load, | ||
| 530 | .unload = imx_drm_driver_unload, | ||
| 531 | .lastclose = imx_drm_driver_lastclose, | ||
| 532 | .preclose = imx_drm_driver_preclose, | ||
| 533 | .set_busid = drm_platform_set_busid, | ||
| 534 | .gem_free_object = drm_gem_cma_free_object, | ||
| 535 | .gem_vm_ops = &drm_gem_cma_vm_ops, | ||
| 536 | .dumb_create = drm_gem_cma_dumb_create, | ||
| 537 | .dumb_map_offset = drm_gem_cma_dumb_map_offset, | ||
| 538 | .dumb_destroy = drm_gem_dumb_destroy, | ||
| 539 | |||
| 540 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | ||
| 541 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | ||
| 542 | .gem_prime_import = drm_gem_prime_import, | ||
| 543 | .gem_prime_export = drm_gem_prime_export, | ||
| 544 | .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, | ||
| 545 | .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, | ||
| 546 | .gem_prime_vmap = drm_gem_cma_prime_vmap, | ||
| 547 | .gem_prime_vunmap = drm_gem_cma_prime_vunmap, | ||
| 548 | .gem_prime_mmap = drm_gem_cma_prime_mmap, | ||
| 549 | .get_vblank_counter = drm_vblank_count, | ||
| 550 | .enable_vblank = imx_drm_enable_vblank, | ||
| 551 | .disable_vblank = imx_drm_disable_vblank, | ||
| 552 | .ioctls = imx_drm_ioctls, | ||
| 553 | .num_ioctls = ARRAY_SIZE(imx_drm_ioctls), | ||
| 554 | .fops = &imx_drm_driver_fops, | ||
| 555 | .name = "imx-drm", | ||
| 556 | .desc = "i.MX DRM graphics", | ||
| 557 | .date = "20120507", | ||
| 558 | .major = 1, | ||
| 559 | .minor = 0, | ||
| 560 | .patchlevel = 0, | ||
| 561 | }; | ||
| 562 | |||
| 563 | static int compare_of(struct device *dev, void *data) | ||
| 564 | { | ||
| 565 | struct device_node *np = data; | ||
| 566 | |||
| 567 | /* Special case for LDB, one device for two channels */ | ||
| 568 | if (of_node_cmp(np->name, "lvds-channel") == 0) { | ||
| 569 | np = of_get_parent(np); | ||
| 570 | of_node_put(np); | ||
| 571 | } | ||
| 572 | |||
| 573 | return dev->of_node == np; | ||
| 574 | } | ||
| 575 | |||
| 576 | static int imx_drm_bind(struct device *dev) | ||
| 577 | { | ||
| 578 | return drm_platform_init(&imx_drm_driver, to_platform_device(dev)); | ||
| 579 | } | ||
| 580 | |||
| 581 | static void imx_drm_unbind(struct device *dev) | ||
| 582 | { | ||
| 583 | drm_put_dev(dev_get_drvdata(dev)); | ||
| 584 | } | ||
| 585 | |||
| 586 | static const struct component_master_ops imx_drm_ops = { | ||
| 587 | .bind = imx_drm_bind, | ||
| 588 | .unbind = imx_drm_unbind, | ||
| 589 | }; | ||
| 590 | |||
| 591 | static int imx_drm_platform_probe(struct platform_device *pdev) | ||
| 592 | { | ||
| 593 | struct device_node *ep, *port, *remote; | ||
| 594 | struct component_match *match = NULL; | ||
| 595 | int ret; | ||
| 596 | int i; | ||
| 597 | |||
| 598 | /* | ||
| 599 | * Bind the IPU display interface ports first, so that | ||
| 600 | * imx_drm_encoder_parse_of called from encoder .bind callbacks | ||
| 601 | * works as expected. | ||
| 602 | */ | ||
| 603 | for (i = 0; ; i++) { | ||
| 604 | port = of_parse_phandle(pdev->dev.of_node, "ports", i); | ||
| 605 | if (!port) | ||
| 606 | break; | ||
| 607 | |||
| 608 | component_match_add(&pdev->dev, &match, compare_of, port); | ||
| 609 | } | ||
| 610 | |||
| 611 | if (i == 0) { | ||
| 612 | dev_err(&pdev->dev, "missing 'ports' property\n"); | ||
| 613 | return -ENODEV; | ||
| 614 | } | ||
| 615 | |||
| 616 | /* Then bind all encoders */ | ||
| 617 | for (i = 0; ; i++) { | ||
| 618 | port = of_parse_phandle(pdev->dev.of_node, "ports", i); | ||
| 619 | if (!port) | ||
| 620 | break; | ||
| 621 | |||
| 622 | for_each_child_of_node(port, ep) { | ||
| 623 | remote = of_graph_get_remote_port_parent(ep); | ||
| 624 | if (!remote || !of_device_is_available(remote)) { | ||
| 625 | of_node_put(remote); | ||
| 626 | continue; | ||
| 627 | } else if (!of_device_is_available(remote->parent)) { | ||
| 628 | dev_warn(&pdev->dev, "parent device of %s is not available\n", | ||
| 629 | remote->full_name); | ||
| 630 | of_node_put(remote); | ||
| 631 | continue; | ||
| 632 | } | ||
| 633 | |||
| 634 | component_match_add(&pdev->dev, &match, compare_of, | ||
| 635 | remote); | ||
| 636 | of_node_put(remote); | ||
| 637 | } | ||
| 638 | of_node_put(port); | ||
| 639 | } | ||
| 640 | |||
| 641 | ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); | ||
| 642 | if (ret) | ||
| 643 | return ret; | ||
| 644 | |||
| 645 | return component_master_add_with_match(&pdev->dev, &imx_drm_ops, match); | ||
| 646 | } | ||
| 647 | |||
| 648 | static int imx_drm_platform_remove(struct platform_device *pdev) | ||
| 649 | { | ||
| 650 | component_master_del(&pdev->dev, &imx_drm_ops); | ||
| 651 | return 0; | ||
| 652 | } | ||
| 653 | |||
| 654 | #ifdef CONFIG_PM_SLEEP | ||
| 655 | static int imx_drm_suspend(struct device *dev) | ||
| 656 | { | ||
| 657 | struct drm_device *drm_dev = dev_get_drvdata(dev); | ||
| 658 | |||
| 659 | /* The drm_dev is NULL before .load hook is called */ | ||
| 660 | if (drm_dev == NULL) | ||
| 661 | return 0; | ||
| 662 | |||
| 663 | drm_kms_helper_poll_disable(drm_dev); | ||
| 664 | |||
| 665 | return 0; | ||
| 666 | } | ||
| 667 | |||
| 668 | static int imx_drm_resume(struct device *dev) | ||
| 669 | { | ||
| 670 | struct drm_device *drm_dev = dev_get_drvdata(dev); | ||
| 671 | |||
| 672 | if (drm_dev == NULL) | ||
| 673 | return 0; | ||
| 674 | |||
| 675 | drm_helper_resume_force_mode(drm_dev); | ||
| 676 | drm_kms_helper_poll_enable(drm_dev); | ||
| 677 | |||
| 678 | return 0; | ||
| 679 | } | ||
| 680 | #endif | ||
| 681 | |||
| 682 | static SIMPLE_DEV_PM_OPS(imx_drm_pm_ops, imx_drm_suspend, imx_drm_resume); | ||
| 683 | |||
| 684 | static const struct of_device_id imx_drm_dt_ids[] = { | ||
| 685 | { .compatible = "fsl,imx-display-subsystem", }, | ||
| 686 | { /* sentinel */ }, | ||
| 687 | }; | ||
| 688 | MODULE_DEVICE_TABLE(of, imx_drm_dt_ids); | ||
| 689 | |||
| 690 | static struct platform_driver imx_drm_pdrv = { | ||
| 691 | .probe = imx_drm_platform_probe, | ||
| 692 | .remove = imx_drm_platform_remove, | ||
| 693 | .driver = { | ||
| 694 | .name = "imx-drm", | ||
| 695 | .pm = &imx_drm_pm_ops, | ||
| 696 | .of_match_table = imx_drm_dt_ids, | ||
| 697 | }, | ||
| 698 | }; | ||
| 699 | module_platform_driver(imx_drm_pdrv); | ||
| 700 | |||
| 701 | MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); | ||
| 702 | MODULE_DESCRIPTION("i.MX drm driver core"); | ||
| 703 | 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..c60460043e24 --- /dev/null +++ b/drivers/gpu/drm/imx/imx-ldb.c  | |||
| @@ -0,0 +1,610 @@ | |||
| 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 | |||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/clk.h> | ||
| 18 | #include <linux/component.h> | ||
| 19 | #include <drm/drmP.h> | ||
| 20 | #include <drm/drm_fb_helper.h> | ||
| 21 | #include <drm/drm_crtc_helper.h> | ||
| 22 | #include <linux/mfd/syscon.h> | ||
| 23 | #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> | ||
| 24 | #include <linux/of_address.h> | ||
| 25 | #include <linux/of_device.h> | ||
| 26 | #include <video/of_videomode.h> | ||
| 27 | #include <linux/regmap.h> | ||
| 28 | #include <linux/videodev2.h> | ||
| 29 | |||
| 30 | #include "imx-drm.h" | ||
| 31 | |||
| 32 | #define DRIVER_NAME "imx-ldb" | ||
| 33 | |||
| 34 | #define LDB_CH0_MODE_EN_TO_DI0 (1 << 0) | ||
| 35 | #define LDB_CH0_MODE_EN_TO_DI1 (3 << 0) | ||
| 36 | #define LDB_CH0_MODE_EN_MASK (3 << 0) | ||
| 37 | #define LDB_CH1_MODE_EN_TO_DI0 (1 << 2) | ||
| 38 | #define LDB_CH1_MODE_EN_TO_DI1 (3 << 2) | ||
| 39 | #define LDB_CH1_MODE_EN_MASK (3 << 2) | ||
| 40 | #define LDB_SPLIT_MODE_EN (1 << 4) | ||
| 41 | #define LDB_DATA_WIDTH_CH0_24 (1 << 5) | ||
| 42 | #define LDB_BIT_MAP_CH0_JEIDA (1 << 6) | ||
| 43 | #define LDB_DATA_WIDTH_CH1_24 (1 << 7) | ||
| 44 | #define LDB_BIT_MAP_CH1_JEIDA (1 << 8) | ||
| 45 | #define LDB_DI0_VS_POL_ACT_LOW (1 << 9) | ||
| 46 | #define LDB_DI1_VS_POL_ACT_LOW (1 << 10) | ||
| 47 | #define LDB_BGREF_RMODE_INT (1 << 15) | ||
| 48 | |||
| 49 | #define con_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, connector) | ||
| 50 | #define enc_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, encoder) | ||
| 51 | |||
| 52 | struct imx_ldb; | ||
| 53 | |||
| 54 | struct imx_ldb_channel { | ||
| 55 | struct imx_ldb *ldb; | ||
| 56 | struct drm_connector connector; | ||
| 57 | struct drm_encoder encoder; | ||
| 58 | struct device_node *child; | ||
| 59 | int chno; | ||
| 60 | void *edid; | ||
| 61 | int edid_len; | ||
| 62 | struct drm_display_mode mode; | ||
| 63 | int mode_valid; | ||
| 64 | }; | ||
| 65 | |||
| 66 | struct bus_mux { | ||
| 67 | int reg; | ||
| 68 | int shift; | ||
| 69 | int mask; | ||
| 70 | }; | ||
| 71 | |||
| 72 | struct imx_ldb { | ||
| 73 | struct regmap *regmap; | ||
| 74 | struct device *dev; | ||
| 75 | struct imx_ldb_channel channel[2]; | ||
| 76 | struct clk *clk[2]; /* our own clock */ | ||
| 77 | struct clk *clk_sel[4]; /* parent of display clock */ | ||
| 78 | struct clk *clk_pll[2]; /* upstream clock we can adjust */ | ||
| 79 | u32 ldb_ctrl; | ||
| 80 | const struct bus_mux *lvds_mux; | ||
| 81 | }; | ||
| 82 | |||
| 83 | static enum drm_connector_status imx_ldb_connector_detect( | ||
| 84 | struct drm_connector *connector, bool force) | ||
| 85 | { | ||
| 86 | return connector_status_connected; | ||
| 87 | } | ||
| 88 | |||
| 89 | static int imx_ldb_connector_get_modes(struct drm_connector *connector) | ||
| 90 | { | ||
| 91 | struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector); | ||
| 92 | int num_modes = 0; | ||
| 93 | |||
| 94 | if (imx_ldb_ch->edid) { | ||
| 95 | drm_mode_connector_update_edid_property(connector, | ||
| 96 | imx_ldb_ch->edid); | ||
| 97 | num_modes = drm_add_edid_modes(connector, imx_ldb_ch->edid); | ||
| 98 | } | ||
| 99 | |||
| 100 | if (imx_ldb_ch->mode_valid) { | ||
| 101 | struct drm_display_mode *mode; | ||
| 102 | |||
| 103 | mode = drm_mode_create(connector->dev); | ||
| 104 | if (!mode) | ||
| 105 | return -EINVAL; | ||
| 106 | drm_mode_copy(mode, &imx_ldb_ch->mode); | ||
| 107 | mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; | ||
| 108 | drm_mode_probed_add(connector, mode); | ||
| 109 | num_modes++; | ||
| 110 | } | ||
| 111 | |||
| 112 | return num_modes; | ||
| 113 | } | ||
| 114 | |||
| 115 | static struct drm_encoder *imx_ldb_connector_best_encoder( | ||
| 116 | struct drm_connector *connector) | ||
| 117 | { | ||
| 118 | struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector); | ||
| 119 | |||
| 120 | return &imx_ldb_ch->encoder; | ||
| 121 | } | ||
| 122 | |||
| 123 | static void imx_ldb_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
| 124 | { | ||
| 125 | } | ||
| 126 | |||
| 127 | static bool imx_ldb_encoder_mode_fixup(struct drm_encoder *encoder, | ||
| 128 | const struct drm_display_mode *mode, | ||
| 129 | struct drm_display_mode *adjusted_mode) | ||
| 130 | { | ||
| 131 | return true; | ||
| 132 | } | ||
| 133 | |||
| 134 | static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno, | ||
| 135 | unsigned long serial_clk, unsigned long di_clk) | ||
| 136 | { | ||
| 137 | int ret; | ||
| 138 | |||
| 139 | dev_dbg(ldb->dev, "%s: now: %ld want: %ld\n", __func__, | ||
| 140 | clk_get_rate(ldb->clk_pll[chno]), serial_clk); | ||
| 141 | clk_set_rate(ldb->clk_pll[chno], serial_clk); | ||
| 142 | |||
| 143 | dev_dbg(ldb->dev, "%s after: %ld\n", __func__, | ||
| 144 | clk_get_rate(ldb->clk_pll[chno])); | ||
| 145 | |||
| 146 | dev_dbg(ldb->dev, "%s: now: %ld want: %ld\n", __func__, | ||
| 147 | clk_get_rate(ldb->clk[chno]), | ||
| 148 | (long int)di_clk); | ||
| 149 | clk_set_rate(ldb->clk[chno], di_clk); | ||
| 150 | |||
| 151 | dev_dbg(ldb->dev, "%s after: %ld\n", __func__, | ||
| 152 | clk_get_rate(ldb->clk[chno])); | ||
| 153 | |||
| 154 | /* set display clock mux to LDB input clock */ | ||
| 155 | ret = clk_set_parent(ldb->clk_sel[mux], ldb->clk[chno]); | ||
| 156 | if (ret) | ||
| 157 | dev_err(ldb->dev, | ||
| 158 | "unable to set di%d parent clock to ldb_di%d\n", mux, | ||
| 159 | chno); | ||
| 160 | } | ||
| 161 | |||
| 162 | static void imx_ldb_encoder_prepare(struct drm_encoder *encoder) | ||
| 163 | { | ||
| 164 | struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); | ||
| 165 | struct imx_ldb *ldb = imx_ldb_ch->ldb; | ||
| 166 | struct drm_display_mode *mode = &encoder->crtc->mode; | ||
| 167 | u32 pixel_fmt; | ||
| 168 | unsigned long serial_clk; | ||
| 169 | unsigned long di_clk = mode->clock * 1000; | ||
| 170 | int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder); | ||
| 171 | |||
| 172 | if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { | ||
| 173 | /* dual channel LVDS mode */ | ||
| 174 | serial_clk = 3500UL * mode->clock; | ||
| 175 | imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk); | ||
| 176 | imx_ldb_set_clock(ldb, mux, 1, serial_clk, di_clk); | ||
| 177 | } else { | ||
| 178 | serial_clk = 7000UL * mode->clock; | ||
| 179 | imx_ldb_set_clock(ldb, mux, imx_ldb_ch->chno, serial_clk, | ||
| 180 | di_clk); | ||
| 181 | } | ||
| 182 | |||
| 183 | switch (imx_ldb_ch->chno) { | ||
| 184 | case 0: | ||
| 185 | pixel_fmt = (ldb->ldb_ctrl & LDB_DATA_WIDTH_CH0_24) ? | ||
| 186 | V4L2_PIX_FMT_RGB24 : V4L2_PIX_FMT_BGR666; | ||
| 187 | break; | ||
| 188 | case 1: | ||
| 189 | pixel_fmt = (ldb->ldb_ctrl & LDB_DATA_WIDTH_CH1_24) ? | ||
| 190 | V4L2_PIX_FMT_RGB24 : V4L2_PIX_FMT_BGR666; | ||
| 191 | break; | ||
| 192 | default: | ||
| 193 | dev_err(ldb->dev, "unable to config di%d panel format\n", | ||
| 194 | imx_ldb_ch->chno); | ||
| 195 | pixel_fmt = V4L2_PIX_FMT_RGB24; | ||
| 196 | } | ||
| 197 | |||
| 198 | imx_drm_panel_format(encoder, pixel_fmt); | ||
| 199 | } | ||
| 200 | |||
| 201 | static void imx_ldb_encoder_commit(struct drm_encoder *encoder) | ||
| 202 | { | ||
| 203 | struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); | ||
| 204 | struct imx_ldb *ldb = imx_ldb_ch->ldb; | ||
| 205 | int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; | ||
| 206 | int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder); | ||
| 207 | |||
| 208 | if (dual) { | ||
| 209 | clk_prepare_enable(ldb->clk[0]); | ||
| 210 | clk_prepare_enable(ldb->clk[1]); | ||
| 211 | } | ||
| 212 | |||
| 213 | if (imx_ldb_ch == &ldb->channel[0] || dual) { | ||
| 214 | ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK; | ||
| 215 | if (mux == 0 || ldb->lvds_mux) | ||
| 216 | ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI0; | ||
| 217 | else if (mux == 1) | ||
| 218 | ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI1; | ||
| 219 | } | ||
| 220 | if (imx_ldb_ch == &ldb->channel[1] || dual) { | ||
| 221 | ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK; | ||
| 222 | if (mux == 1 || ldb->lvds_mux) | ||
| 223 | ldb->ldb_ctrl |= LDB_CH1_MODE_EN_TO_DI1; | ||
| 224 | else if (mux == 0) | ||
| 225 | ldb->ldb_ctrl |= LDB_CH1_MODE_EN_TO_DI0; | ||
| 226 | } | ||
| 227 | |||
| 228 | if (ldb->lvds_mux) { | ||
| 229 | const struct bus_mux *lvds_mux = NULL; | ||
| 230 | |||
| 231 | if (imx_ldb_ch == &ldb->channel[0]) | ||
| 232 | lvds_mux = &ldb->lvds_mux[0]; | ||
| 233 | else if (imx_ldb_ch == &ldb->channel[1]) | ||
| 234 | lvds_mux = &ldb->lvds_mux[1]; | ||
| 235 | |||
| 236 | regmap_update_bits(ldb->regmap, lvds_mux->reg, lvds_mux->mask, | ||
| 237 | mux << lvds_mux->shift); | ||
| 238 | } | ||
| 239 | |||
| 240 | regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl); | ||
| 241 | } | ||
| 242 | |||
| 243 | static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder, | ||
| 244 | struct drm_display_mode *mode, | ||
| 245 | struct drm_display_mode *adjusted_mode) | ||
| 246 | { | ||
| 247 | struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); | ||
| 248 | struct imx_ldb *ldb = imx_ldb_ch->ldb; | ||
| 249 | int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; | ||
| 250 | |||
| 251 | if (mode->clock > 170000) { | ||
| 252 | dev_warn(ldb->dev, | ||
| 253 | "%s: mode exceeds 170 MHz pixel clock\n", __func__); | ||
| 254 | } | ||
| 255 | if (mode->clock > 85000 && !dual) { | ||
| 256 | dev_warn(ldb->dev, | ||
| 257 | "%s: mode exceeds 85 MHz pixel clock\n", __func__); | ||
| 258 | } | ||
| 259 | |||
| 260 | /* FIXME - assumes straight connections DI0 --> CH0, DI1 --> CH1 */ | ||
| 261 | if (imx_ldb_ch == &ldb->channel[0]) { | ||
| 262 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | ||
| 263 | ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW; | ||
| 264 | else if (mode->flags & DRM_MODE_FLAG_PVSYNC) | ||
| 265 | ldb->ldb_ctrl &= ~LDB_DI0_VS_POL_ACT_LOW; | ||
| 266 | } | ||
| 267 | if (imx_ldb_ch == &ldb->channel[1]) { | ||
| 268 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | ||
| 269 | ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW; | ||
| 270 | else if (mode->flags & DRM_MODE_FLAG_PVSYNC) | ||
| 271 | ldb->ldb_ctrl &= ~LDB_DI1_VS_POL_ACT_LOW; | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | static void imx_ldb_encoder_disable(struct drm_encoder *encoder) | ||
| 276 | { | ||
| 277 | struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); | ||
| 278 | struct imx_ldb *ldb = imx_ldb_ch->ldb; | ||
| 279 | |||
| 280 | /* | ||
| 281 | * imx_ldb_encoder_disable is called by | ||
| 282 | * drm_helper_disable_unused_functions without | ||
| 283 | * the encoder being enabled before. | ||
| 284 | */ | ||
| 285 | if (imx_ldb_ch == &ldb->channel[0] && | ||
| 286 | (ldb->ldb_ctrl & LDB_CH0_MODE_EN_MASK) == 0) | ||
| 287 | return; | ||
| 288 | else if (imx_ldb_ch == &ldb->channel[1] && | ||
| 289 | (ldb->ldb_ctrl & LDB_CH1_MODE_EN_MASK) == 0) | ||
| 290 | return; | ||
| 291 | |||
| 292 | if (imx_ldb_ch == &ldb->channel[0]) | ||
| 293 | ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK; | ||
| 294 | else if (imx_ldb_ch == &ldb->channel[1]) | ||
| 295 | ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK; | ||
| 296 | |||
| 297 | regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl); | ||
| 298 | |||
| 299 | if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { | ||
| 300 | clk_disable_unprepare(ldb->clk[0]); | ||
| 301 | clk_disable_unprepare(ldb->clk[1]); | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | static struct drm_connector_funcs imx_ldb_connector_funcs = { | ||
| 306 | .dpms = drm_helper_connector_dpms, | ||
| 307 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
| 308 | .detect = imx_ldb_connector_detect, | ||
| 309 | .destroy = imx_drm_connector_destroy, | ||
| 310 | }; | ||
| 311 | |||
| 312 | static struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = { | ||
| 313 | .get_modes = imx_ldb_connector_get_modes, | ||
| 314 | .best_encoder = imx_ldb_connector_best_encoder, | ||
| 315 | }; | ||
| 316 | |||
| 317 | static struct drm_encoder_funcs imx_ldb_encoder_funcs = { | ||
| 318 | .destroy = imx_drm_encoder_destroy, | ||
| 319 | }; | ||
| 320 | |||
| 321 | static struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = { | ||
| 322 | .dpms = imx_ldb_encoder_dpms, | ||
| 323 | .mode_fixup = imx_ldb_encoder_mode_fixup, | ||
| 324 | .prepare = imx_ldb_encoder_prepare, | ||
| 325 | .commit = imx_ldb_encoder_commit, | ||
| 326 | .mode_set = imx_ldb_encoder_mode_set, | ||
| 327 | .disable = imx_ldb_encoder_disable, | ||
| 328 | }; | ||
| 329 | |||
| 330 | static int imx_ldb_get_clk(struct imx_ldb *ldb, int chno) | ||
| 331 | { | ||
| 332 | char clkname[16]; | ||
| 333 | |||
| 334 | snprintf(clkname, sizeof(clkname), "di%d", chno); | ||
| 335 | ldb->clk[chno] = devm_clk_get(ldb->dev, clkname); | ||
| 336 | if (IS_ERR(ldb->clk[chno])) | ||
| 337 | return PTR_ERR(ldb->clk[chno]); | ||
| 338 | |||
| 339 | snprintf(clkname, sizeof(clkname), "di%d_pll", chno); | ||
| 340 | ldb->clk_pll[chno] = devm_clk_get(ldb->dev, clkname); | ||
| 341 | |||
| 342 | return PTR_ERR_OR_ZERO(ldb->clk_pll[chno]); | ||
| 343 | } | ||
| 344 | |||
| 345 | static int imx_ldb_register(struct drm_device *drm, | ||
| 346 | struct imx_ldb_channel *imx_ldb_ch) | ||
| 347 | { | ||
| 348 | struct imx_ldb *ldb = imx_ldb_ch->ldb; | ||
| 349 | int ret; | ||
| 350 | |||
| 351 | ret = imx_drm_encoder_parse_of(drm, &imx_ldb_ch->encoder, | ||
| 352 | imx_ldb_ch->child); | ||
| 353 | if (ret) | ||
| 354 | return ret; | ||
| 355 | |||
| 356 | ret = imx_ldb_get_clk(ldb, imx_ldb_ch->chno); | ||
| 357 | if (ret) | ||
| 358 | return ret; | ||
| 359 | |||
| 360 | if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { | ||
| 361 | ret = imx_ldb_get_clk(ldb, 1); | ||
| 362 | if (ret) | ||
| 363 | return ret; | ||
| 364 | } | ||
| 365 | |||
| 366 | drm_encoder_helper_add(&imx_ldb_ch->encoder, | ||
| 367 | &imx_ldb_encoder_helper_funcs); | ||
| 368 | drm_encoder_init(drm, &imx_ldb_ch->encoder, &imx_ldb_encoder_funcs, | ||
| 369 | DRM_MODE_ENCODER_LVDS); | ||
| 370 | |||
| 371 | drm_connector_helper_add(&imx_ldb_ch->connector, | ||
| 372 | &imx_ldb_connector_helper_funcs); | ||
| 373 | drm_connector_init(drm, &imx_ldb_ch->connector, | ||
| 374 | &imx_ldb_connector_funcs, DRM_MODE_CONNECTOR_LVDS); | ||
| 375 | |||
| 376 | drm_mode_connector_attach_encoder(&imx_ldb_ch->connector, | ||
| 377 | &imx_ldb_ch->encoder); | ||
| 378 | |||
| 379 | return 0; | ||
| 380 | } | ||
| 381 | |||
| 382 | enum { | ||
| 383 | LVDS_BIT_MAP_SPWG, | ||
| 384 | LVDS_BIT_MAP_JEIDA | ||
| 385 | }; | ||
| 386 | |||
| 387 | static const char * const imx_ldb_bit_mappings[] = { | ||
| 388 | [LVDS_BIT_MAP_SPWG] = "spwg", | ||
| 389 | [LVDS_BIT_MAP_JEIDA] = "jeida", | ||
| 390 | }; | ||
| 391 | |||
| 392 | static const int of_get_data_mapping(struct device_node *np) | ||
| 393 | { | ||
| 394 | const char *bm; | ||
| 395 | int ret, i; | ||
| 396 | |||
| 397 | ret = of_property_read_string(np, "fsl,data-mapping", &bm); | ||
| 398 | if (ret < 0) | ||
| 399 | return ret; | ||
| 400 | |||
| 401 | for (i = 0; i < ARRAY_SIZE(imx_ldb_bit_mappings); i++) | ||
| 402 | if (!strcasecmp(bm, imx_ldb_bit_mappings[i])) | ||
| 403 | return i; | ||
| 404 | |||
| 405 | return -EINVAL; | ||
| 406 | } | ||
| 407 | |||
| 408 | static struct bus_mux imx6q_lvds_mux[2] = { | ||
| 409 | { | ||
| 410 | .reg = IOMUXC_GPR3, | ||
| 411 | .shift = 6, | ||
| 412 | .mask = IMX6Q_GPR3_LVDS0_MUX_CTL_MASK, | ||
| 413 | }, { | ||
| 414 | .reg = IOMUXC_GPR3, | ||
| 415 | .shift = 8, | ||
| 416 | .mask = IMX6Q_GPR3_LVDS1_MUX_CTL_MASK, | ||
| 417 | } | ||
| 418 | }; | ||
| 419 | |||
| 420 | /* | ||
| 421 | * For a device declaring compatible = "fsl,imx6q-ldb", "fsl,imx53-ldb", | ||
| 422 | * of_match_device will walk through this list and take the first entry | ||
| 423 | * matching any of its compatible values. Therefore, the more generic | ||
| 424 | * entries (in this case fsl,imx53-ldb) need to be ordered last. | ||
| 425 | */ | ||
| 426 | static const struct of_device_id imx_ldb_dt_ids[] = { | ||
| 427 | { .compatible = "fsl,imx6q-ldb", .data = imx6q_lvds_mux, }, | ||
| 428 | { .compatible = "fsl,imx53-ldb", .data = NULL, }, | ||
| 429 | { } | ||
| 430 | }; | ||
| 431 | MODULE_DEVICE_TABLE(of, imx_ldb_dt_ids); | ||
| 432 | |||
| 433 | static int imx_ldb_bind(struct device *dev, struct device *master, void *data) | ||
| 434 | { | ||
| 435 | struct drm_device *drm = data; | ||
| 436 | struct device_node *np = dev->of_node; | ||
| 437 | const struct of_device_id *of_id = | ||
| 438 | of_match_device(imx_ldb_dt_ids, dev); | ||
| 439 | struct device_node *child; | ||
| 440 | const u8 *edidp; | ||
| 441 | struct imx_ldb *imx_ldb; | ||
| 442 | int datawidth; | ||
| 443 | int mapping; | ||
| 444 | int dual; | ||
| 445 | int ret; | ||
| 446 | int i; | ||
| 447 | |||
| 448 | imx_ldb = devm_kzalloc(dev, sizeof(*imx_ldb), GFP_KERNEL); | ||
| 449 | if (!imx_ldb) | ||
| 450 | return -ENOMEM; | ||
| 451 | |||
| 452 | imx_ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr"); | ||
| 453 | if (IS_ERR(imx_ldb->regmap)) { | ||
| 454 | dev_err(dev, "failed to get parent regmap\n"); | ||
| 455 | return PTR_ERR(imx_ldb->regmap); | ||
| 456 | } | ||
| 457 | |||
| 458 | imx_ldb->dev = dev; | ||
| 459 | |||
| 460 | if (of_id) | ||
| 461 | imx_ldb->lvds_mux = of_id->data; | ||
| 462 | |||
| 463 | dual = of_property_read_bool(np, "fsl,dual-channel"); | ||
| 464 | if (dual) | ||
| 465 | imx_ldb->ldb_ctrl |= LDB_SPLIT_MODE_EN; | ||
| 466 | |||
| 467 | /* | ||
| 468 | * There are three different possible clock mux configurations: | ||
| 469 | * i.MX53: ipu1_di0_sel, ipu1_di1_sel | ||
| 470 | * i.MX6q: ipu1_di0_sel, ipu1_di1_sel, ipu2_di0_sel, ipu2_di1_sel | ||
| 471 | * i.MX6dl: ipu1_di0_sel, ipu1_di1_sel, lcdif_sel | ||
| 472 | * Map them all to di0_sel...di3_sel. | ||
| 473 | */ | ||
| 474 | for (i = 0; i < 4; i++) { | ||
| 475 | char clkname[16]; | ||
| 476 | |||
| 477 | sprintf(clkname, "di%d_sel", i); | ||
| 478 | imx_ldb->clk_sel[i] = devm_clk_get(imx_ldb->dev, clkname); | ||
| 479 | if (IS_ERR(imx_ldb->clk_sel[i])) { | ||
| 480 | ret = PTR_ERR(imx_ldb->clk_sel[i]); | ||
| 481 | imx_ldb->clk_sel[i] = NULL; | ||
| 482 | break; | ||
| 483 | } | ||
| 484 | } | ||
| 485 | if (i == 0) | ||
| 486 | return ret; | ||
| 487 | |||
| 488 | for_each_child_of_node(np, child) { | ||
| 489 | struct imx_ldb_channel *channel; | ||
| 490 | |||
| 491 | ret = of_property_read_u32(child, "reg", &i); | ||
| 492 | if (ret || i < 0 || i > 1) | ||
| 493 | return -EINVAL; | ||
| 494 | |||
| 495 | if (dual && i > 0) { | ||
| 496 | dev_warn(dev, "dual-channel mode, ignoring second output\n"); | ||
| 497 | continue; | ||
| 498 | } | ||
| 499 | |||
| 500 | if (!of_device_is_available(child)) | ||
| 501 | continue; | ||
| 502 | |||
| 503 | channel = &imx_ldb->channel[i]; | ||
| 504 | channel->ldb = imx_ldb; | ||
| 505 | channel->chno = i; | ||
| 506 | channel->child = child; | ||
| 507 | |||
| 508 | edidp = of_get_property(child, "edid", &channel->edid_len); | ||
| 509 | if (edidp) { | ||
| 510 | channel->edid = kmemdup(edidp, channel->edid_len, | ||
| 511 | GFP_KERNEL); | ||
| 512 | } else { | ||
| 513 | ret = of_get_drm_display_mode(child, &channel->mode, 0); | ||
| 514 | if (!ret) | ||
| 515 | channel->mode_valid = 1; | ||
| 516 | } | ||
| 517 | |||
| 518 | ret = of_property_read_u32(child, "fsl,data-width", &datawidth); | ||
| 519 | if (ret) | ||
| 520 | datawidth = 0; | ||
| 521 | else if (datawidth != 18 && datawidth != 24) | ||
| 522 | return -EINVAL; | ||
| 523 | |||
| 524 | mapping = of_get_data_mapping(child); | ||
| 525 | switch (mapping) { | ||
| 526 | case LVDS_BIT_MAP_SPWG: | ||
| 527 | if (datawidth == 24) { | ||
| 528 | if (i == 0 || dual) | ||
| 529 | imx_ldb->ldb_ctrl |= | ||
| 530 | LDB_DATA_WIDTH_CH0_24; | ||
| 531 | if (i == 1 || dual) | ||
| 532 | imx_ldb->ldb_ctrl |= | ||
| 533 | LDB_DATA_WIDTH_CH1_24; | ||
| 534 | } | ||
| 535 | break; | ||
| 536 | case LVDS_BIT_MAP_JEIDA: | ||
| 537 | if (datawidth == 18) { | ||
| 538 | dev_err(dev, "JEIDA standard only supported in 24 bit\n"); | ||
| 539 | return -EINVAL; | ||
| 540 | } | ||
| 541 | if (i == 0 || dual) | ||
| 542 | imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 | | ||
| 543 | LDB_BIT_MAP_CH0_JEIDA; | ||
| 544 | if (i == 1 || dual) | ||
| 545 | imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 | | ||
| 546 | LDB_BIT_MAP_CH1_JEIDA; | ||
| 547 | break; | ||
| 548 | default: | ||
| 549 | dev_err(dev, "data mapping not specified or invalid\n"); | ||
| 550 | return -EINVAL; | ||
| 551 | } | ||
| 552 | |||
| 553 | ret = imx_ldb_register(drm, channel); | ||
| 554 | if (ret) | ||
| 555 | return ret; | ||
| 556 | } | ||
| 557 | |||
| 558 | dev_set_drvdata(dev, imx_ldb); | ||
| 559 | |||
| 560 | return 0; | ||
| 561 | } | ||
| 562 | |||
| 563 | static void imx_ldb_unbind(struct device *dev, struct device *master, | ||
| 564 | void *data) | ||
| 565 | { | ||
| 566 | struct imx_ldb *imx_ldb = dev_get_drvdata(dev); | ||
| 567 | int i; | ||
| 568 | |||
| 569 | for (i = 0; i < 2; i++) { | ||
| 570 | struct imx_ldb_channel *channel = &imx_ldb->channel[i]; | ||
| 571 | |||
| 572 | if (!channel->connector.funcs) | ||
| 573 | continue; | ||
| 574 | |||
| 575 | channel->connector.funcs->destroy(&channel->connector); | ||
| 576 | channel->encoder.funcs->destroy(&channel->encoder); | ||
| 577 | } | ||
| 578 | } | ||
| 579 | |||
| 580 | static const struct component_ops imx_ldb_ops = { | ||
| 581 | .bind = imx_ldb_bind, | ||
| 582 | .unbind = imx_ldb_unbind, | ||
| 583 | }; | ||
| 584 | |||
| 585 | static int imx_ldb_probe(struct platform_device *pdev) | ||
| 586 | { | ||
| 587 | return component_add(&pdev->dev, &imx_ldb_ops); | ||
| 588 | } | ||
| 589 | |||
| 590 | static int imx_ldb_remove(struct platform_device *pdev) | ||
| 591 | { | ||
| 592 | component_del(&pdev->dev, &imx_ldb_ops); | ||
| 593 | return 0; | ||
| 594 | } | ||
| 595 | |||
| 596 | static struct platform_driver imx_ldb_driver = { | ||
| 597 | .probe = imx_ldb_probe, | ||
| 598 | .remove = imx_ldb_remove, | ||
| 599 | .driver = { | ||
| 600 | .of_match_table = imx_ldb_dt_ids, | ||
| 601 | .name = DRIVER_NAME, | ||
| 602 | }, | ||
| 603 | }; | ||
| 604 | |||
| 605 | module_platform_driver(imx_ldb_driver); | ||
| 606 | |||
| 607 | MODULE_DESCRIPTION("i.MX LVDS driver"); | ||
| 608 | MODULE_AUTHOR("Sascha Hauer, Pengutronix"); | ||
| 609 | MODULE_LICENSE("GPL"); | ||
| 610 | 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..a729f4f7074c --- /dev/null +++ b/drivers/gpu/drm/imx/imx-tve.c  | |||
| @@ -0,0 +1,731 @@ | |||
| 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 | |||
| 16 | #include <linux/clk.h> | ||
| 17 | #include <linux/clk-provider.h> | ||
| 18 | #include <linux/component.h> | ||
| 19 | #include <linux/module.h> | ||
| 20 | #include <linux/i2c.h> | ||
| 21 | #include <linux/regmap.h> | ||
| 22 | #include <linux/regulator/consumer.h> | ||
| 23 | #include <linux/spinlock.h> | ||
| 24 | #include <linux/videodev2.h> | ||
| 25 | #include <drm/drmP.h> | ||
| 26 | #include <drm/drm_fb_helper.h> | ||
| 27 | #include <drm/drm_crtc_helper.h> | ||
| 28 | #include <video/imx-ipu-v3.h> | ||
| 29 | |||
| 30 | #include "imx-drm.h" | ||
| 31 | |||
| 32 | #define TVE_COM_CONF_REG 0x00 | ||
| 33 | #define TVE_TVDAC0_CONT_REG 0x28 | ||
| 34 | #define TVE_TVDAC1_CONT_REG 0x2c | ||
| 35 | #define TVE_TVDAC2_CONT_REG 0x30 | ||
| 36 | #define TVE_CD_CONT_REG 0x34 | ||
| 37 | #define TVE_INT_CONT_REG 0x64 | ||
| 38 | #define TVE_STAT_REG 0x68 | ||
| 39 | #define TVE_TST_MODE_REG 0x6c | ||
| 40 | #define TVE_MV_CONT_REG 0xdc | ||
| 41 | |||
| 42 | /* TVE_COM_CONF_REG */ | ||
| 43 | #define TVE_SYNC_CH_2_EN BIT(22) | ||
| 44 | #define TVE_SYNC_CH_1_EN BIT(21) | ||
| 45 | #define TVE_SYNC_CH_0_EN BIT(20) | ||
| 46 | #define TVE_TV_OUT_MODE_MASK (0x7 << 12) | ||
| 47 | #define TVE_TV_OUT_DISABLE (0x0 << 12) | ||
| 48 | #define TVE_TV_OUT_CVBS_0 (0x1 << 12) | ||
| 49 | #define TVE_TV_OUT_CVBS_2 (0x2 << 12) | ||
| 50 | #define TVE_TV_OUT_CVBS_0_2 (0x3 << 12) | ||
| 51 | #define TVE_TV_OUT_SVIDEO_0_1 (0x4 << 12) | ||
| 52 | #define TVE_TV_OUT_SVIDEO_0_1_CVBS2_2 (0x5 << 12) | ||
| 53 | #define TVE_TV_OUT_YPBPR (0x6 << 12) | ||
| 54 | #define TVE_TV_OUT_RGB (0x7 << 12) | ||
| 55 | #define TVE_TV_STAND_MASK (0xf << 8) | ||
| 56 | #define TVE_TV_STAND_HD_1080P30 (0xc << 8) | ||
| 57 | #define TVE_P2I_CONV_EN BIT(7) | ||
| 58 | #define TVE_INP_VIDEO_FORM BIT(6) | ||
| 59 | #define TVE_INP_YCBCR_422 (0x0 << 6) | ||
| 60 | #define TVE_INP_YCBCR_444 (0x1 << 6) | ||
| 61 | #define TVE_DATA_SOURCE_MASK (0x3 << 4) | ||
| 62 | #define TVE_DATA_SOURCE_BUS1 (0x0 << 4) | ||
| 63 | #define TVE_DATA_SOURCE_BUS2 (0x1 << 4) | ||
| 64 | #define TVE_DATA_SOURCE_EXT (0x2 << 4) | ||
| 65 | #define TVE_DATA_SOURCE_TESTGEN (0x3 << 4) | ||
| 66 | #define TVE_IPU_CLK_EN_OFS 3 | ||
| 67 | #define TVE_IPU_CLK_EN BIT(3) | ||
| 68 | #define TVE_DAC_SAMP_RATE_OFS 1 | ||
| 69 | #define TVE_DAC_SAMP_RATE_WIDTH 2 | ||
| 70 | #define TVE_DAC_SAMP_RATE_MASK (0x3 << 1) | ||
| 71 | #define TVE_DAC_FULL_RATE (0x0 << 1) | ||
| 72 | #define TVE_DAC_DIV2_RATE (0x1 << 1) | ||
| 73 | #define TVE_DAC_DIV4_RATE (0x2 << 1) | ||
| 74 | #define TVE_EN BIT(0) | ||
| 75 | |||
| 76 | /* TVE_TVDACx_CONT_REG */ | ||
| 77 | #define TVE_TVDAC_GAIN_MASK (0x3f << 0) | ||
| 78 | |||
| 79 | /* TVE_CD_CONT_REG */ | ||
| 80 | #define TVE_CD_CH_2_SM_EN BIT(22) | ||
| 81 | #define TVE_CD_CH_1_SM_EN BIT(21) | ||
| 82 | #define TVE_CD_CH_0_SM_EN BIT(20) | ||
| 83 | #define TVE_CD_CH_2_LM_EN BIT(18) | ||
| 84 | #define TVE_CD_CH_1_LM_EN BIT(17) | ||
| 85 | #define TVE_CD_CH_0_LM_EN BIT(16) | ||
| 86 | #define TVE_CD_CH_2_REF_LVL BIT(10) | ||
| 87 | #define TVE_CD_CH_1_REF_LVL BIT(9) | ||
| 88 | #define TVE_CD_CH_0_REF_LVL BIT(8) | ||
| 89 | #define TVE_CD_EN BIT(0) | ||
| 90 | |||
| 91 | /* TVE_INT_CONT_REG */ | ||
| 92 | #define TVE_FRAME_END_IEN BIT(13) | ||
| 93 | #define TVE_CD_MON_END_IEN BIT(2) | ||
| 94 | #define TVE_CD_SM_IEN BIT(1) | ||
| 95 | #define TVE_CD_LM_IEN BIT(0) | ||
| 96 | |||
| 97 | /* TVE_TST_MODE_REG */ | ||
| 98 | #define TVE_TVDAC_TEST_MODE_MASK (0x7 << 0) | ||
| 99 | |||
| 100 | #define con_to_tve(x) container_of(x, struct imx_tve, connector) | ||
| 101 | #define enc_to_tve(x) container_of(x, struct imx_tve, encoder) | ||
| 102 | |||
| 103 | enum { | ||
| 104 | TVE_MODE_TVOUT, | ||
| 105 | TVE_MODE_VGA, | ||
| 106 | }; | ||
| 107 | |||
| 108 | struct imx_tve { | ||
| 109 | struct drm_connector connector; | ||
| 110 | struct drm_encoder encoder; | ||
| 111 | struct device *dev; | ||
| 112 | spinlock_t lock; /* register lock */ | ||
| 113 | bool enabled; | ||
| 114 | int mode; | ||
| 115 | |||
| 116 | struct regmap *regmap; | ||
| 117 | struct regulator *dac_reg; | ||
| 118 | struct i2c_adapter *ddc; | ||
| 119 | struct clk *clk; | ||
| 120 | struct clk *di_sel_clk; | ||
| 121 | struct clk_hw clk_hw_di; | ||
| 122 | struct clk *di_clk; | ||
| 123 | int vsync_pin; | ||
| 124 | int hsync_pin; | ||
| 125 | }; | ||
| 126 | |||
| 127 | static void tve_lock(void *__tve) | ||
| 128 | __acquires(&tve->lock) | ||
| 129 | { | ||
| 130 | struct imx_tve *tve = __tve; | ||
| 131 | |||
| 132 | spin_lock(&tve->lock); | ||
| 133 | } | ||
| 134 | |||
| 135 | static void tve_unlock(void *__tve) | ||
| 136 | __releases(&tve->lock) | ||
| 137 | { | ||
| 138 | struct imx_tve *tve = __tve; | ||
| 139 | |||
| 140 | spin_unlock(&tve->lock); | ||
| 141 | } | ||
| 142 | |||
| 143 | static void tve_enable(struct imx_tve *tve) | ||
| 144 | { | ||
| 145 | int ret; | ||
| 146 | |||
| 147 | if (!tve->enabled) { | ||
| 148 | tve->enabled = true; | ||
| 149 | clk_prepare_enable(tve->clk); | ||
| 150 | ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, | ||
| 151 | TVE_IPU_CLK_EN | TVE_EN, | ||
| 152 | TVE_IPU_CLK_EN | TVE_EN); | ||
| 153 | } | ||
| 154 | |||
| 155 | /* clear interrupt status register */ | ||
| 156 | regmap_write(tve->regmap, TVE_STAT_REG, 0xffffffff); | ||
| 157 | |||
| 158 | /* cable detection irq disabled in VGA mode, enabled in TVOUT mode */ | ||
| 159 | if (tve->mode == TVE_MODE_VGA) | ||
| 160 | regmap_write(tve->regmap, TVE_INT_CONT_REG, 0); | ||
| 161 | else | ||
| 162 | regmap_write(tve->regmap, TVE_INT_CONT_REG, | ||
| 163 | TVE_CD_SM_IEN | | ||
| 164 | TVE_CD_LM_IEN | | ||
| 165 | TVE_CD_MON_END_IEN); | ||
| 166 | } | ||
| 167 | |||
| 168 | static void tve_disable(struct imx_tve *tve) | ||
| 169 | { | ||
| 170 | int ret; | ||
| 171 | |||
| 172 | if (tve->enabled) { | ||
| 173 | tve->enabled = false; | ||
| 174 | ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, | ||
| 175 | TVE_IPU_CLK_EN | TVE_EN, 0); | ||
| 176 | clk_disable_unprepare(tve->clk); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | static int tve_setup_tvout(struct imx_tve *tve) | ||
| 181 | { | ||
| 182 | return -ENOTSUPP; | ||
| 183 | } | ||
| 184 | |||
| 185 | static int tve_setup_vga(struct imx_tve *tve) | ||
| 186 | { | ||
| 187 | unsigned int mask; | ||
| 188 | unsigned int val; | ||
| 189 | int ret; | ||
| 190 | |||
| 191 | /* set gain to (1 + 10/128) to provide 0.7V peak-to-peak amplitude */ | ||
| 192 | ret = regmap_update_bits(tve->regmap, TVE_TVDAC0_CONT_REG, | ||
| 193 | TVE_TVDAC_GAIN_MASK, 0x0a); | ||
| 194 | ret = regmap_update_bits(tve->regmap, TVE_TVDAC1_CONT_REG, | ||
| 195 | TVE_TVDAC_GAIN_MASK, 0x0a); | ||
| 196 | ret = regmap_update_bits(tve->regmap, TVE_TVDAC2_CONT_REG, | ||
| 197 | TVE_TVDAC_GAIN_MASK, 0x0a); | ||
| 198 | |||
| 199 | /* set configuration register */ | ||
| 200 | mask = TVE_DATA_SOURCE_MASK | TVE_INP_VIDEO_FORM; | ||
| 201 | val = TVE_DATA_SOURCE_BUS2 | TVE_INP_YCBCR_444; | ||
| 202 | mask |= TVE_TV_STAND_MASK | TVE_P2I_CONV_EN; | ||
| 203 | val |= TVE_TV_STAND_HD_1080P30 | 0; | ||
| 204 | mask |= TVE_TV_OUT_MODE_MASK | TVE_SYNC_CH_0_EN; | ||
| 205 | val |= TVE_TV_OUT_RGB | TVE_SYNC_CH_0_EN; | ||
| 206 | ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, mask, val); | ||
| 207 | if (ret < 0) { | ||
| 208 | dev_err(tve->dev, "failed to set configuration: %d\n", ret); | ||
| 209 | return ret; | ||
| 210 | } | ||
| 211 | |||
| 212 | /* set test mode (as documented) */ | ||
| 213 | ret = regmap_update_bits(tve->regmap, TVE_TST_MODE_REG, | ||
| 214 | TVE_TVDAC_TEST_MODE_MASK, 1); | ||
| 215 | |||
| 216 | return 0; | ||
| 217 | } | ||
| 218 | |||
| 219 | static enum drm_connector_status imx_tve_connector_detect( | ||
| 220 | struct drm_connector *connector, bool force) | ||
| 221 | { | ||
| 222 | return connector_status_connected; | ||
| 223 | } | ||
| 224 | |||
| 225 | static int imx_tve_connector_get_modes(struct drm_connector *connector) | ||
| 226 | { | ||
| 227 | struct imx_tve *tve = con_to_tve(connector); | ||
| 228 | struct edid *edid; | ||
| 229 | int ret = 0; | ||
| 230 | |||
| 231 | if (!tve->ddc) | ||
| 232 | return 0; | ||
| 233 | |||
| 234 | edid = drm_get_edid(connector, tve->ddc); | ||
| 235 | if (edid) { | ||
| 236 | drm_mode_connector_update_edid_property(connector, edid); | ||
| 237 | ret = drm_add_edid_modes(connector, edid); | ||
| 238 | kfree(edid); | ||
| 239 | } | ||
| 240 | |||
| 241 | return ret; | ||
| 242 | } | ||
| 243 | |||
| 244 | static int imx_tve_connector_mode_valid(struct drm_connector *connector, | ||
| 245 | struct drm_display_mode *mode) | ||
| 246 | { | ||
| 247 | struct imx_tve *tve = con_to_tve(connector); | ||
| 248 | unsigned long rate; | ||
| 249 | |||
| 250 | /* pixel clock with 2x oversampling */ | ||
| 251 | rate = clk_round_rate(tve->clk, 2000UL * mode->clock) / 2000; | ||
| 252 | if (rate == mode->clock) | ||
| 253 | return MODE_OK; | ||
| 254 | |||
| 255 | /* pixel clock without oversampling */ | ||
| 256 | rate = clk_round_rate(tve->clk, 1000UL * mode->clock) / 1000; | ||
| 257 | if (rate == mode->clock) | ||
| 258 | return MODE_OK; | ||
| 259 | |||
| 260 | dev_warn(tve->dev, "ignoring mode %dx%d\n", | ||
| 261 | mode->hdisplay, mode->vdisplay); | ||
| 262 | |||
| 263 | return MODE_BAD; | ||
| 264 | } | ||
| 265 | |||
| 266 | static struct drm_encoder *imx_tve_connector_best_encoder( | ||
| 267 | struct drm_connector *connector) | ||
| 268 | { | ||
| 269 | struct imx_tve *tve = con_to_tve(connector); | ||
| 270 | |||
| 271 | return &tve->encoder; | ||
| 272 | } | ||
| 273 | |||
| 274 | static void imx_tve_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
| 275 | { | ||
| 276 | struct imx_tve *tve = enc_to_tve(encoder); | ||
| 277 | int ret; | ||
| 278 | |||
| 279 | ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, | ||
| 280 | TVE_TV_OUT_MODE_MASK, TVE_TV_OUT_DISABLE); | ||
| 281 | if (ret < 0) | ||
| 282 | dev_err(tve->dev, "failed to disable TVOUT: %d\n", ret); | ||
| 283 | } | ||
| 284 | |||
| 285 | static bool imx_tve_encoder_mode_fixup(struct drm_encoder *encoder, | ||
| 286 | const struct drm_display_mode *mode, | ||
| 287 | struct drm_display_mode *adjusted_mode) | ||
| 288 | { | ||
| 289 | return true; | ||
| 290 | } | ||
| 291 | |||
| 292 | static void imx_tve_encoder_prepare(struct drm_encoder *encoder) | ||
| 293 | { | ||
| 294 | struct imx_tve *tve = enc_to_tve(encoder); | ||
| 295 | |||
| 296 | tve_disable(tve); | ||
| 297 | |||
| 298 | switch (tve->mode) { | ||
| 299 | case TVE_MODE_VGA: | ||
| 300 | imx_drm_panel_format_pins(encoder, IPU_PIX_FMT_GBR24, | ||
| 301 | tve->hsync_pin, tve->vsync_pin); | ||
| 302 | break; | ||
| 303 | case TVE_MODE_TVOUT: | ||
| 304 | imx_drm_panel_format(encoder, V4L2_PIX_FMT_YUV444); | ||
| 305 | break; | ||
| 306 | } | ||
| 307 | } | ||
| 308 | |||
| 309 | static void imx_tve_encoder_mode_set(struct drm_encoder *encoder, | ||
| 310 | struct drm_display_mode *mode, | ||
| 311 | struct drm_display_mode *adjusted_mode) | ||
| 312 | { | ||
| 313 | struct imx_tve *tve = enc_to_tve(encoder); | ||
| 314 | unsigned long rounded_rate; | ||
| 315 | unsigned long rate; | ||
| 316 | int div = 1; | ||
| 317 | int ret; | ||
| 318 | |||
| 319 | /* | ||
| 320 | * FIXME | ||
| 321 | * we should try 4k * mode->clock first, | ||
| 322 | * and enable 4x oversampling for lower resolutions | ||
| 323 | */ | ||
| 324 | rate = 2000UL * mode->clock; | ||
| 325 | clk_set_rate(tve->clk, rate); | ||
| 326 | rounded_rate = clk_get_rate(tve->clk); | ||
| 327 | if (rounded_rate >= rate) | ||
| 328 | div = 2; | ||
| 329 | clk_set_rate(tve->di_clk, rounded_rate / div); | ||
| 330 | |||
| 331 | ret = clk_set_parent(tve->di_sel_clk, tve->di_clk); | ||
| 332 | if (ret < 0) { | ||
| 333 | dev_err(tve->dev, "failed to set di_sel parent to tve_di: %d\n", | ||
| 334 | ret); | ||
| 335 | } | ||
| 336 | |||
| 337 | if (tve->mode == TVE_MODE_VGA) | ||
| 338 | tve_setup_vga(tve); | ||
| 339 | else | ||
| 340 | tve_setup_tvout(tve); | ||
| 341 | } | ||
| 342 | |||
| 343 | static void imx_tve_encoder_commit(struct drm_encoder *encoder) | ||
| 344 | { | ||
| 345 | struct imx_tve *tve = enc_to_tve(encoder); | ||
| 346 | |||
| 347 | tve_enable(tve); | ||
| 348 | } | ||
| 349 | |||
| 350 | static void imx_tve_encoder_disable(struct drm_encoder *encoder) | ||
| 351 | { | ||
| 352 | struct imx_tve *tve = enc_to_tve(encoder); | ||
| 353 | |||
| 354 | tve_disable(tve); | ||
| 355 | } | ||
| 356 | |||
| 357 | static struct drm_connector_funcs imx_tve_connector_funcs = { | ||
| 358 | .dpms = drm_helper_connector_dpms, | ||
| 359 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
| 360 | .detect = imx_tve_connector_detect, | ||
| 361 | .destroy = imx_drm_connector_destroy, | ||
| 362 | }; | ||
| 363 | |||
| 364 | static struct drm_connector_helper_funcs imx_tve_connector_helper_funcs = { | ||
| 365 | .get_modes = imx_tve_connector_get_modes, | ||
| 366 | .best_encoder = imx_tve_connector_best_encoder, | ||
| 367 | .mode_valid = imx_tve_connector_mode_valid, | ||
| 368 | }; | ||
| 369 | |||
| 370 | static struct drm_encoder_funcs imx_tve_encoder_funcs = { | ||
| 371 | .destroy = imx_drm_encoder_destroy, | ||
| 372 | }; | ||
| 373 | |||
| 374 | static struct drm_encoder_helper_funcs imx_tve_encoder_helper_funcs = { | ||
| 375 | .dpms = imx_tve_encoder_dpms, | ||
| 376 | .mode_fixup = imx_tve_encoder_mode_fixup, | ||
| 377 | .prepare = imx_tve_encoder_prepare, | ||
| 378 | .mode_set = imx_tve_encoder_mode_set, | ||
| 379 | .commit = imx_tve_encoder_commit, | ||
| 380 | .disable = imx_tve_encoder_disable, | ||
| 381 | }; | ||
| 382 | |||
| 383 | static irqreturn_t imx_tve_irq_handler(int irq, void *data) | ||
| 384 | { | ||
| 385 | struct imx_tve *tve = data; | ||
| 386 | unsigned int val; | ||
| 387 | |||
| 388 | regmap_read(tve->regmap, TVE_STAT_REG, &val); | ||
| 389 | |||
| 390 | /* clear interrupt status register */ | ||
| 391 | regmap_write(tve->regmap, TVE_STAT_REG, 0xffffffff); | ||
| 392 | |||
| 393 | return IRQ_HANDLED; | ||
| 394 | } | ||
| 395 | |||
| 396 | static unsigned long clk_tve_di_recalc_rate(struct clk_hw *hw, | ||
| 397 | unsigned long parent_rate) | ||
| 398 | { | ||
| 399 | struct imx_tve *tve = container_of(hw, struct imx_tve, clk_hw_di); | ||
| 400 | unsigned int val; | ||
| 401 | int ret; | ||
| 402 | |||
| 403 | ret = regmap_read(tve->regmap, TVE_COM_CONF_REG, &val); | ||
| 404 | if (ret < 0) | ||
| 405 | return 0; | ||
| 406 | |||
| 407 | switch (val & TVE_DAC_SAMP_RATE_MASK) { | ||
| 408 | case TVE_DAC_DIV4_RATE: | ||
| 409 | return parent_rate / 4; | ||
| 410 | case TVE_DAC_DIV2_RATE: | ||
| 411 | return parent_rate / 2; | ||
| 412 | case TVE_DAC_FULL_RATE: | ||
| 413 | default: | ||
| 414 | return parent_rate; | ||
| 415 | } | ||
| 416 | |||
| 417 | return 0; | ||
| 418 | } | ||
| 419 | |||
| 420 | static long clk_tve_di_round_rate(struct clk_hw *hw, unsigned long rate, | ||
| 421 | unsigned long *prate) | ||
| 422 | { | ||
| 423 | unsigned long div; | ||
| 424 | |||
| 425 | div = *prate / rate; | ||
| 426 | if (div >= 4) | ||
| 427 | return *prate / 4; | ||
| 428 | else if (div >= 2) | ||
| 429 | return *prate / 2; | ||
| 430 | return *prate; | ||
| 431 | } | ||
| 432 | |||
| 433 | static int clk_tve_di_set_rate(struct clk_hw *hw, unsigned long rate, | ||
| 434 | unsigned long parent_rate) | ||
| 435 | { | ||
| 436 | struct imx_tve *tve = container_of(hw, struct imx_tve, clk_hw_di); | ||
| 437 | unsigned long div; | ||
| 438 | u32 val; | ||
| 439 | int ret; | ||
| 440 | |||
| 441 | div = parent_rate / rate; | ||
| 442 | if (div >= 4) | ||
| 443 | val = TVE_DAC_DIV4_RATE; | ||
| 444 | else if (div >= 2) | ||
| 445 | val = TVE_DAC_DIV2_RATE; | ||
| 446 | else | ||
| 447 | val = TVE_DAC_FULL_RATE; | ||
| 448 | |||
| 449 | ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, | ||
| 450 | TVE_DAC_SAMP_RATE_MASK, val); | ||
| 451 | |||
| 452 | if (ret < 0) { | ||
| 453 | dev_err(tve->dev, "failed to set divider: %d\n", ret); | ||
| 454 | return ret; | ||
| 455 | } | ||
| 456 | |||
| 457 | return 0; | ||
| 458 | } | ||
| 459 | |||
| 460 | static struct clk_ops clk_tve_di_ops = { | ||
| 461 | .round_rate = clk_tve_di_round_rate, | ||
| 462 | .set_rate = clk_tve_di_set_rate, | ||
| 463 | .recalc_rate = clk_tve_di_recalc_rate, | ||
| 464 | }; | ||
| 465 | |||
| 466 | static int tve_clk_init(struct imx_tve *tve, void __iomem *base) | ||
| 467 | { | ||
| 468 | const char *tve_di_parent[1]; | ||
| 469 | struct clk_init_data init = { | ||
| 470 | .name = "tve_di", | ||
| 471 | .ops = &clk_tve_di_ops, | ||
| 472 | .num_parents = 1, | ||
| 473 | .flags = 0, | ||
| 474 | }; | ||
| 475 | |||
| 476 | tve_di_parent[0] = __clk_get_name(tve->clk); | ||
| 477 | init.parent_names = (const char **)&tve_di_parent; | ||
| 478 | |||
| 479 | tve->clk_hw_di.init = &init; | ||
| 480 | tve->di_clk = clk_register(tve->dev, &tve->clk_hw_di); | ||
| 481 | if (IS_ERR(tve->di_clk)) { | ||
| 482 | dev_err(tve->dev, "failed to register TVE output clock: %ld\n", | ||
| 483 | PTR_ERR(tve->di_clk)); | ||
| 484 | return PTR_ERR(tve->di_clk); | ||
| 485 | } | ||
| 486 | |||
| 487 | return 0; | ||
| 488 | } | ||
| 489 | |||
| 490 | static int imx_tve_register(struct drm_device *drm, struct imx_tve *tve) | ||
| 491 | { | ||
| 492 | int encoder_type; | ||
| 493 | int ret; | ||
| 494 | |||
| 495 | encoder_type = tve->mode == TVE_MODE_VGA ? | ||
| 496 | DRM_MODE_ENCODER_DAC : DRM_MODE_ENCODER_TVDAC; | ||
| 497 | |||
| 498 | ret = imx_drm_encoder_parse_of(drm, &tve->encoder, | ||
| 499 | tve->dev->of_node); | ||
| 500 | if (ret) | ||
| 501 | return ret; | ||
| 502 | |||
| 503 | drm_encoder_helper_add(&tve->encoder, &imx_tve_encoder_helper_funcs); | ||
| 504 | drm_encoder_init(drm, &tve->encoder, &imx_tve_encoder_funcs, | ||
| 505 | encoder_type); | ||
| 506 | |||
| 507 | drm_connector_helper_add(&tve->connector, | ||
| 508 | &imx_tve_connector_helper_funcs); | ||
| 509 | drm_connector_init(drm, &tve->connector, &imx_tve_connector_funcs, | ||
| 510 | DRM_MODE_CONNECTOR_VGA); | ||
| 511 | |||
| 512 | drm_mode_connector_attach_encoder(&tve->connector, &tve->encoder); | ||
| 513 | |||
| 514 | return 0; | ||
| 515 | } | ||
| 516 | |||
| 517 | static bool imx_tve_readable_reg(struct device *dev, unsigned int reg) | ||
| 518 | { | ||
| 519 | return (reg % 4 == 0) && (reg <= 0xdc); | ||
| 520 | } | ||
| 521 | |||
| 522 | static struct regmap_config tve_regmap_config = { | ||
| 523 | .reg_bits = 32, | ||
| 524 | .val_bits = 32, | ||
| 525 | .reg_stride = 4, | ||
| 526 | |||
| 527 | .readable_reg = imx_tve_readable_reg, | ||
| 528 | |||
| 529 | .lock = tve_lock, | ||
| 530 | .unlock = tve_unlock, | ||
| 531 | |||
| 532 | .max_register = 0xdc, | ||
| 533 | }; | ||
| 534 | |||
| 535 | static const char * const imx_tve_modes[] = { | ||
| 536 | [TVE_MODE_TVOUT] = "tvout", | ||
| 537 | [TVE_MODE_VGA] = "vga", | ||
| 538 | }; | ||
| 539 | |||
| 540 | static const int of_get_tve_mode(struct device_node *np) | ||
| 541 | { | ||
| 542 | const char *bm; | ||
| 543 | int ret, i; | ||
| 544 | |||
| 545 | ret = of_property_read_string(np, "fsl,tve-mode", &bm); | ||
| 546 | if (ret < 0) | ||
| 547 | return ret; | ||
| 548 | |||
| 549 | for (i = 0; i < ARRAY_SIZE(imx_tve_modes); i++) | ||
| 550 | if (!strcasecmp(bm, imx_tve_modes[i])) | ||
| 551 | return i; | ||
| 552 | |||
| 553 | return -EINVAL; | ||
| 554 | } | ||
| 555 | |||
| 556 | static int imx_tve_bind(struct device *dev, struct device *master, void *data) | ||
| 557 | { | ||
| 558 | struct platform_device *pdev = to_platform_device(dev); | ||
| 559 | struct drm_device *drm = data; | ||
| 560 | struct device_node *np = dev->of_node; | ||
| 561 | struct device_node *ddc_node; | ||
| 562 | struct imx_tve *tve; | ||
| 563 | struct resource *res; | ||
| 564 | void __iomem *base; | ||
| 565 | unsigned int val; | ||
| 566 | int irq; | ||
| 567 | int ret; | ||
| 568 | |||
| 569 | tve = devm_kzalloc(dev, sizeof(*tve), GFP_KERNEL); | ||
| 570 | if (!tve) | ||
| 571 | return -ENOMEM; | ||
| 572 | |||
| 573 | tve->dev = dev; | ||
| 574 | spin_lock_init(&tve->lock); | ||
| 575 | |||
| 576 | ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); | ||
| 577 | if (ddc_node) { | ||
| 578 | tve->ddc = of_find_i2c_adapter_by_node(ddc_node); | ||
| 579 | of_node_put(ddc_node); | ||
| 580 | } | ||
| 581 | |||
| 582 | tve->mode = of_get_tve_mode(np); | ||
| 583 | if (tve->mode != TVE_MODE_VGA) { | ||
| 584 | dev_err(dev, "only VGA mode supported, currently\n"); | ||
| 585 | return -EINVAL; | ||
| 586 | } | ||
| 587 | |||
| 588 | if (tve->mode == TVE_MODE_VGA) { | ||
| 589 | ret = of_property_read_u32(np, "fsl,hsync-pin", | ||
| 590 | &tve->hsync_pin); | ||
| 591 | |||
| 592 | if (ret < 0) { | ||
| 593 | dev_err(dev, "failed to get vsync pin\n"); | ||
| 594 | return ret; | ||
| 595 | } | ||
| 596 | |||
| 597 | ret |= of_property_read_u32(np, "fsl,vsync-pin", | ||
| 598 | &tve->vsync_pin); | ||
| 599 | |||
| 600 | if (ret < 0) { | ||
| 601 | dev_err(dev, "failed to get vsync pin\n"); | ||
| 602 | return ret; | ||
| 603 | } | ||
| 604 | } | ||
| 605 | |||
| 606 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 607 | base = devm_ioremap_resource(dev, res); | ||
| 608 | if (IS_ERR(base)) | ||
| 609 | return PTR_ERR(base); | ||
| 610 | |||
| 611 | tve_regmap_config.lock_arg = tve; | ||
| 612 | tve->regmap = devm_regmap_init_mmio_clk(dev, "tve", base, | ||
| 613 | &tve_regmap_config); | ||
| 614 | if (IS_ERR(tve->regmap)) { | ||
| 615 | dev_err(dev, "failed to init regmap: %ld\n", | ||
| 616 | PTR_ERR(tve->regmap)); | ||
| 617 | return PTR_ERR(tve->regmap); | ||
| 618 | } | ||
| 619 | |||
| 620 | irq = platform_get_irq(pdev, 0); | ||
| 621 | if (irq < 0) { | ||
| 622 | dev_err(dev, "failed to get irq\n"); | ||
| 623 | return irq; | ||
| 624 | } | ||
| 625 | |||
| 626 | ret = devm_request_threaded_irq(dev, irq, NULL, | ||
| 627 | imx_tve_irq_handler, IRQF_ONESHOT, | ||
| 628 | "imx-tve", tve); | ||
| 629 | if (ret < 0) { | ||
| 630 | dev_err(dev, "failed to request irq: %d\n", ret); | ||
| 631 | return ret; | ||
| 632 | } | ||
| 633 | |||
| 634 | tve->dac_reg = devm_regulator_get(dev, "dac"); | ||
| 635 | if (!IS_ERR(tve->dac_reg)) { | ||
| 636 | regulator_set_voltage(tve->dac_reg, 2750000, 2750000); | ||
| 637 | ret = regulator_enable(tve->dac_reg); | ||
| 638 | if (ret) | ||
| 639 | return ret; | ||
| 640 | } | ||
| 641 | |||
| 642 | tve->clk = devm_clk_get(dev, "tve"); | ||
| 643 | if (IS_ERR(tve->clk)) { | ||
| 644 | dev_err(dev, "failed to get high speed tve clock: %ld\n", | ||
| 645 | PTR_ERR(tve->clk)); | ||
| 646 | return PTR_ERR(tve->clk); | ||
| 647 | } | ||
| 648 | |||
| 649 | /* this is the IPU DI clock input selector, can be parented to tve_di */ | ||
| 650 | tve->di_sel_clk = devm_clk_get(dev, "di_sel"); | ||
| 651 | if (IS_ERR(tve->di_sel_clk)) { | ||
| 652 | dev_err(dev, "failed to get ipu di mux clock: %ld\n", | ||
| 653 | PTR_ERR(tve->di_sel_clk)); | ||
| 654 | return PTR_ERR(tve->di_sel_clk); | ||
| 655 | } | ||
| 656 | |||
| 657 | ret = tve_clk_init(tve, base); | ||
| 658 | if (ret < 0) | ||
| 659 | return ret; | ||
| 660 | |||
| 661 | ret = regmap_read(tve->regmap, TVE_COM_CONF_REG, &val); | ||
| 662 | if (ret < 0) { | ||
| 663 | dev_err(dev, "failed to read configuration register: %d\n", | ||
| 664 | ret); | ||
| 665 | return ret; | ||
| 666 | } | ||
| 667 | if (val != 0x00100000) { | ||
| 668 | dev_err(dev, "configuration register default value indicates this is not a TVEv2\n"); | ||
| 669 | return -ENODEV; | ||
| 670 | } | ||
| 671 | |||
| 672 | /* disable cable detection for VGA mode */ | ||
| 673 | ret = regmap_write(tve->regmap, TVE_CD_CONT_REG, 0); | ||
| 674 | |||
| 675 | ret = imx_tve_register(drm, tve); | ||
| 676 | if (ret) | ||
| 677 | return ret; | ||
| 678 | |||
| 679 | dev_set_drvdata(dev, tve); | ||
| 680 | |||
| 681 | return 0; | ||
| 682 | } | ||
| 683 | |||
| 684 | static void imx_tve_unbind(struct device *dev, struct device *master, | ||
| 685 | void *data) | ||
| 686 | { | ||
| 687 | struct imx_tve *tve = dev_get_drvdata(dev); | ||
| 688 | |||
| 689 | tve->connector.funcs->destroy(&tve->connector); | ||
| 690 | tve->encoder.funcs->destroy(&tve->encoder); | ||
| 691 | |||
| 692 | if (!IS_ERR(tve->dac_reg)) | ||
| 693 | regulator_disable(tve->dac_reg); | ||
| 694 | } | ||
| 695 | |||
| 696 | static const struct component_ops imx_tve_ops = { | ||
| 697 | .bind = imx_tve_bind, | ||
| 698 | .unbind = imx_tve_unbind, | ||
| 699 | }; | ||
| 700 | |||
| 701 | static int imx_tve_probe(struct platform_device *pdev) | ||
| 702 | { | ||
| 703 | return component_add(&pdev->dev, &imx_tve_ops); | ||
| 704 | } | ||
| 705 | |||
| 706 | static int imx_tve_remove(struct platform_device *pdev) | ||
| 707 | { | ||
| 708 | component_del(&pdev->dev, &imx_tve_ops); | ||
| 709 | return 0; | ||
| 710 | } | ||
| 711 | |||
| 712 | static const struct of_device_id imx_tve_dt_ids[] = { | ||
| 713 | { .compatible = "fsl,imx53-tve", }, | ||
| 714 | { /* sentinel */ } | ||
| 715 | }; | ||
| 716 | |||
| 717 | static struct platform_driver imx_tve_driver = { | ||
| 718 | .probe = imx_tve_probe, | ||
| 719 | .remove = imx_tve_remove, | ||
| 720 | .driver = { | ||
| 721 | .of_match_table = imx_tve_dt_ids, | ||
| 722 | .name = "imx-tve", | ||
| 723 | }, | ||
| 724 | }; | ||
| 725 | |||
| 726 | module_platform_driver(imx_tve_driver); | ||
| 727 | |||
| 728 | MODULE_DESCRIPTION("i.MX Television Encoder driver"); | ||
| 729 | MODULE_AUTHOR("Philipp Zabel, Pengutronix"); | ||
| 730 | MODULE_LICENSE("GPL"); | ||
| 731 | 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..ebee59cb96d8 --- /dev/null +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c  | |||
| @@ -0,0 +1,513 @@ | |||
| 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 | #include <linux/component.h> | ||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/export.h> | ||
| 18 | #include <linux/device.h> | ||
| 19 | #include <linux/platform_device.h> | ||
| 20 | #include <drm/drmP.h> | ||
| 21 | #include <drm/drm_crtc_helper.h> | ||
| 22 | #include <linux/fb.h> | ||
| 23 | #include <linux/clk.h> | ||
| 24 | #include <linux/errno.h> | ||
| 25 | #include <drm/drm_gem_cma_helper.h> | ||
| 26 | #include <drm/drm_fb_cma_helper.h> | ||
| 27 | |||
| 28 | #include <video/imx-ipu-v3.h> | ||
| 29 | #include "imx-drm.h" | ||
| 30 | #include "ipuv3-plane.h" | ||
| 31 | |||
| 32 | #define DRIVER_DESC "i.MX IPUv3 Graphics" | ||
| 33 | |||
| 34 | struct ipu_crtc { | ||
| 35 | struct device *dev; | ||
| 36 | struct drm_crtc base; | ||
| 37 | struct imx_drm_crtc *imx_crtc; | ||
| 38 | |||
| 39 | /* plane[0] is the full plane, plane[1] is the partial plane */ | ||
| 40 | struct ipu_plane *plane[2]; | ||
| 41 | |||
| 42 | struct ipu_dc *dc; | ||
| 43 | struct ipu_di *di; | ||
| 44 | int enabled; | ||
| 45 | struct drm_pending_vblank_event *page_flip_event; | ||
| 46 | struct drm_framebuffer *newfb; | ||
| 47 | int irq; | ||
| 48 | u32 interface_pix_fmt; | ||
| 49 | unsigned long di_clkflags; | ||
| 50 | int di_hsync_pin; | ||
| 51 | int di_vsync_pin; | ||
| 52 | }; | ||
| 53 | |||
| 54 | #define to_ipu_crtc(x) container_of(x, struct ipu_crtc, base) | ||
| 55 | |||
| 56 | static void ipu_fb_enable(struct ipu_crtc *ipu_crtc) | ||
| 57 | { | ||
| 58 | struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); | ||
| 59 | |||
| 60 | if (ipu_crtc->enabled) | ||
| 61 | return; | ||
| 62 | |||
| 63 | ipu_dc_enable(ipu); | ||
| 64 | ipu_plane_enable(ipu_crtc->plane[0]); | ||
| 65 | /* Start DC channel and DI after IDMAC */ | ||
| 66 | ipu_dc_enable_channel(ipu_crtc->dc); | ||
| 67 | ipu_di_enable(ipu_crtc->di); | ||
| 68 | |||
| 69 | ipu_crtc->enabled = 1; | ||
| 70 | } | ||
| 71 | |||
| 72 | static void ipu_fb_disable(struct ipu_crtc *ipu_crtc) | ||
| 73 | { | ||
| 74 | struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); | ||
| 75 | |||
| 76 | if (!ipu_crtc->enabled) | ||
| 77 | return; | ||
| 78 | |||
| 79 | /* Stop DC channel and DI before IDMAC */ | ||
| 80 | ipu_dc_disable_channel(ipu_crtc->dc); | ||
| 81 | ipu_di_disable(ipu_crtc->di); | ||
| 82 | ipu_plane_disable(ipu_crtc->plane[0]); | ||
| 83 | ipu_dc_disable(ipu); | ||
| 84 | |||
| 85 | ipu_crtc->enabled = 0; | ||
| 86 | } | ||
| 87 | |||
| 88 | static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode) | ||
| 89 | { | ||
| 90 | struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); | ||
| 91 | |||
| 92 | dev_dbg(ipu_crtc->dev, "%s mode: %d\n", __func__, mode); | ||
| 93 | |||
| 94 | switch (mode) { | ||
| 95 | case DRM_MODE_DPMS_ON: | ||
| 96 | ipu_fb_enable(ipu_crtc); | ||
| 97 | break; | ||
| 98 | case DRM_MODE_DPMS_STANDBY: | ||
| 99 | case DRM_MODE_DPMS_SUSPEND: | ||
| 100 | case DRM_MODE_DPMS_OFF: | ||
| 101 | ipu_fb_disable(ipu_crtc); | ||
| 102 | break; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | static int ipu_page_flip(struct drm_crtc *crtc, | ||
| 107 | struct drm_framebuffer *fb, | ||
| 108 | struct drm_pending_vblank_event *event, | ||
| 109 | uint32_t page_flip_flags) | ||
| 110 | { | ||
| 111 | struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); | ||
| 112 | int ret; | ||
| 113 | |||
| 114 | if (ipu_crtc->newfb) | ||
| 115 | return -EBUSY; | ||
| 116 | |||
| 117 | ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc); | ||
| 118 | if (ret) { | ||
| 119 | dev_dbg(ipu_crtc->dev, "failed to acquire vblank counter\n"); | ||
| 120 | list_del(&event->base.link); | ||
| 121 | |||
| 122 | return ret; | ||
| 123 | } | ||
| 124 | |||
| 125 | ipu_crtc->newfb = fb; | ||
| 126 | ipu_crtc->page_flip_event = event; | ||
| 127 | crtc->primary->fb = fb; | ||
| 128 | |||
| 129 | return 0; | ||
| 130 | } | ||
| 131 | |||
| 132 | static const struct drm_crtc_funcs ipu_crtc_funcs = { | ||
| 133 | .set_config = drm_crtc_helper_set_config, | ||
| 134 | .destroy = drm_crtc_cleanup, | ||
| 135 | .page_flip = ipu_page_flip, | ||
| 136 | }; | ||
| 137 | |||
| 138 | static int ipu_crtc_mode_set(struct drm_crtc *crtc, | ||
| 139 | struct drm_display_mode *orig_mode, | ||
| 140 | struct drm_display_mode *mode, | ||
| 141 | int x, int y, | ||
| 142 | struct drm_framebuffer *old_fb) | ||
| 143 | { | ||
| 144 | struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); | ||
| 145 | int ret; | ||
| 146 | struct ipu_di_signal_cfg sig_cfg = {}; | ||
| 147 | u32 out_pixel_fmt; | ||
| 148 | |||
| 149 | dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__, | ||
| 150 | mode->hdisplay); | ||
| 151 | dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__, | ||
| 152 | mode->vdisplay); | ||
| 153 | |||
| 154 | out_pixel_fmt = ipu_crtc->interface_pix_fmt; | ||
| 155 | |||
| 156 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) | ||
| 157 | sig_cfg.interlaced = 1; | ||
| 158 | if (mode->flags & DRM_MODE_FLAG_PHSYNC) | ||
| 159 | sig_cfg.Hsync_pol = 1; | ||
| 160 | if (mode->flags & DRM_MODE_FLAG_PVSYNC) | ||
| 161 | sig_cfg.Vsync_pol = 1; | ||
| 162 | |||
| 163 | sig_cfg.enable_pol = 1; | ||
| 164 | sig_cfg.clk_pol = 0; | ||
| 165 | sig_cfg.width = mode->hdisplay; | ||
| 166 | sig_cfg.height = mode->vdisplay; | ||
| 167 | sig_cfg.pixel_fmt = out_pixel_fmt; | ||
| 168 | sig_cfg.h_start_width = mode->htotal - mode->hsync_end; | ||
| 169 | sig_cfg.h_sync_width = mode->hsync_end - mode->hsync_start; | ||
| 170 | sig_cfg.h_end_width = mode->hsync_start - mode->hdisplay; | ||
| 171 | |||
| 172 | sig_cfg.v_start_width = mode->vtotal - mode->vsync_end; | ||
| 173 | sig_cfg.v_sync_width = mode->vsync_end - mode->vsync_start; | ||
| 174 | sig_cfg.v_end_width = mode->vsync_start - mode->vdisplay; | ||
| 175 | sig_cfg.pixelclock = mode->clock * 1000; | ||
| 176 | sig_cfg.clkflags = ipu_crtc->di_clkflags; | ||
| 177 | |||
| 178 | sig_cfg.v_to_h_sync = 0; | ||
| 179 | |||
| 180 | sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin; | ||
| 181 | sig_cfg.vsync_pin = ipu_crtc->di_vsync_pin; | ||
| 182 | |||
| 183 | ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, sig_cfg.interlaced, | ||
| 184 | out_pixel_fmt, mode->hdisplay); | ||
| 185 | if (ret) { | ||
| 186 | dev_err(ipu_crtc->dev, | ||
| 187 | "initializing display controller failed with %d\n", | ||
| 188 | ret); | ||
| 189 | return ret; | ||
| 190 | } | ||
| 191 | |||
| 192 | ret = ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg); | ||
| 193 | if (ret) { | ||
| 194 | dev_err(ipu_crtc->dev, | ||
| 195 | "initializing panel failed with %d\n", ret); | ||
| 196 | return ret; | ||
| 197 | } | ||
| 198 | |||
| 199 | return ipu_plane_mode_set(ipu_crtc->plane[0], crtc, mode, | ||
| 200 | crtc->primary->fb, | ||
| 201 | 0, 0, mode->hdisplay, mode->vdisplay, | ||
| 202 | x, y, mode->hdisplay, mode->vdisplay); | ||
| 203 | } | ||
| 204 | |||
| 205 | static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc) | ||
| 206 | { | ||
| 207 | unsigned long flags; | ||
| 208 | struct drm_device *drm = ipu_crtc->base.dev; | ||
| 209 | |||
| 210 | spin_lock_irqsave(&drm->event_lock, flags); | ||
| 211 | if (ipu_crtc->page_flip_event) | ||
| 212 | drm_send_vblank_event(drm, -1, ipu_crtc->page_flip_event); | ||
| 213 | ipu_crtc->page_flip_event = NULL; | ||
| 214 | imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc); | ||
| 215 | spin_unlock_irqrestore(&drm->event_lock, flags); | ||
| 216 | } | ||
| 217 | |||
| 218 | static irqreturn_t ipu_irq_handler(int irq, void *dev_id) | ||
| 219 | { | ||
| 220 | struct ipu_crtc *ipu_crtc = dev_id; | ||
| 221 | |||
| 222 | imx_drm_handle_vblank(ipu_crtc->imx_crtc); | ||
| 223 | |||
| 224 | if (ipu_crtc->newfb) { | ||
| 225 | struct ipu_plane *plane = ipu_crtc->plane[0]; | ||
| 226 | |||
| 227 | ipu_crtc->newfb = NULL; | ||
| 228 | ipu_plane_set_base(plane, ipu_crtc->base.primary->fb, | ||
| 229 | plane->x, plane->y); | ||
| 230 | ipu_crtc_handle_pageflip(ipu_crtc); | ||
| 231 | } | ||
| 232 | |||
| 233 | return IRQ_HANDLED; | ||
| 234 | } | ||
| 235 | |||
| 236 | static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc, | ||
| 237 | const struct drm_display_mode *mode, | ||
| 238 | struct drm_display_mode *adjusted_mode) | ||
| 239 | { | ||
| 240 | return true; | ||
| 241 | } | ||
| 242 | |||
| 243 | static void ipu_crtc_prepare(struct drm_crtc *crtc) | ||
| 244 | { | ||
| 245 | struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); | ||
| 246 | |||
| 247 | ipu_fb_disable(ipu_crtc); | ||
| 248 | } | ||
| 249 | |||
| 250 | static void ipu_crtc_commit(struct drm_crtc *crtc) | ||
| 251 | { | ||
| 252 | struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); | ||
| 253 | |||
| 254 | ipu_fb_enable(ipu_crtc); | ||
| 255 | } | ||
| 256 | |||
| 257 | static struct drm_crtc_helper_funcs ipu_helper_funcs = { | ||
| 258 | .dpms = ipu_crtc_dpms, | ||
| 259 | .mode_fixup = ipu_crtc_mode_fixup, | ||
| 260 | .mode_set = ipu_crtc_mode_set, | ||
| 261 | .prepare = ipu_crtc_prepare, | ||
| 262 | .commit = ipu_crtc_commit, | ||
| 263 | }; | ||
| 264 | |||
| 265 | static int ipu_enable_vblank(struct drm_crtc *crtc) | ||
| 266 | { | ||
| 267 | return 0; | ||
| 268 | } | ||
| 269 | |||
| 270 | static void ipu_disable_vblank(struct drm_crtc *crtc) | ||
| 271 | { | ||
| 272 | struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); | ||
| 273 | |||
| 274 | ipu_crtc->page_flip_event = NULL; | ||
| 275 | ipu_crtc->newfb = NULL; | ||
| 276 | } | ||
| 277 | |||
| 278 | static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type, | ||
| 279 | u32 pixfmt, int hsync_pin, int vsync_pin) | ||
| 280 | { | ||
| 281 | struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); | ||
| 282 | |||
| 283 | ipu_crtc->interface_pix_fmt = pixfmt; | ||
| 284 | ipu_crtc->di_hsync_pin = hsync_pin; | ||
| 285 | ipu_crtc->di_vsync_pin = vsync_pin; | ||
| 286 | |||
| 287 | switch (encoder_type) { | ||
| 288 | case DRM_MODE_ENCODER_DAC: | ||
| 289 | case DRM_MODE_ENCODER_TVDAC: | ||
| 290 | case DRM_MODE_ENCODER_LVDS: | ||
| 291 | ipu_crtc->di_clkflags = IPU_DI_CLKMODE_SYNC | | ||
| 292 | IPU_DI_CLKMODE_EXT; | ||
| 293 | break; | ||
| 294 | case DRM_MODE_ENCODER_TMDS: | ||
| 295 | case DRM_MODE_ENCODER_NONE: | ||
| 296 | ipu_crtc->di_clkflags = 0; | ||
| 297 | break; | ||
| 298 | } | ||
| 299 | |||
| 300 | return 0; | ||
| 301 | } | ||
| 302 | |||
| 303 | static const struct imx_drm_crtc_helper_funcs ipu_crtc_helper_funcs = { | ||
| 304 | .enable_vblank = ipu_enable_vblank, | ||
| 305 | .disable_vblank = ipu_disable_vblank, | ||
| 306 | .set_interface_pix_fmt = ipu_set_interface_pix_fmt, | ||
| 307 | .crtc_funcs = &ipu_crtc_funcs, | ||
| 308 | .crtc_helper_funcs = &ipu_helper_funcs, | ||
| 309 | }; | ||
| 310 | |||
| 311 | static void ipu_put_resources(struct ipu_crtc *ipu_crtc) | ||
| 312 | { | ||
| 313 | if (!IS_ERR_OR_NULL(ipu_crtc->dc)) | ||
| 314 | ipu_dc_put(ipu_crtc->dc); | ||
| 315 | if (!IS_ERR_OR_NULL(ipu_crtc->di)) | ||
| 316 | ipu_di_put(ipu_crtc->di); | ||
| 317 | } | ||
| 318 | |||
| 319 | static int ipu_get_resources(struct ipu_crtc *ipu_crtc, | ||
| 320 | struct ipu_client_platformdata *pdata) | ||
| 321 | { | ||
| 322 | struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); | ||
| 323 | int ret; | ||
| 324 | |||
| 325 | ipu_crtc->dc = ipu_dc_get(ipu, pdata->dc); | ||
| 326 | if (IS_ERR(ipu_crtc->dc)) { | ||
| 327 | ret = PTR_ERR(ipu_crtc->dc); | ||
| 328 | goto err_out; | ||
| 329 | } | ||
| 330 | |||
| 331 | ipu_crtc->di = ipu_di_get(ipu, pdata->di); | ||
| 332 | if (IS_ERR(ipu_crtc->di)) { | ||
| 333 | ret = PTR_ERR(ipu_crtc->di); | ||
| 334 | goto err_out; | ||
| 335 | } | ||
| 336 | |||
| 337 | return 0; | ||
| 338 | err_out: | ||
| 339 | ipu_put_resources(ipu_crtc); | ||
| 340 | |||
| 341 | return ret; | ||
| 342 | } | ||
| 343 | |||
| 344 | static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, | ||
| 345 | struct ipu_client_platformdata *pdata, struct drm_device *drm) | ||
| 346 | { | ||
| 347 | struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); | ||
| 348 | int dp = -EINVAL; | ||
| 349 | int ret; | ||
| 350 | int id; | ||
| 351 | |||
| 352 | ret = ipu_get_resources(ipu_crtc, pdata); | ||
| 353 | if (ret) { | ||
| 354 | dev_err(ipu_crtc->dev, "getting resources failed with %d.\n", | ||
| 355 | ret); | ||
| 356 | return ret; | ||
| 357 | } | ||
| 358 | |||
| 359 | ret = imx_drm_add_crtc(drm, &ipu_crtc->base, &ipu_crtc->imx_crtc, | ||
| 360 | &ipu_crtc_helper_funcs, ipu_crtc->dev->of_node); | ||
| 361 | if (ret) { | ||
| 362 | dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret); | ||
| 363 | goto err_put_resources; | ||
| 364 | } | ||
| 365 | |||
| 366 | if (pdata->dp >= 0) | ||
| 367 | dp = IPU_DP_FLOW_SYNC_BG; | ||
| 368 | id = imx_drm_crtc_id(ipu_crtc->imx_crtc); | ||
| 369 | ipu_crtc->plane[0] = ipu_plane_init(ipu_crtc->base.dev, ipu, | ||
| 370 | pdata->dma[0], dp, BIT(id), true); | ||
| 371 | ret = ipu_plane_get_resources(ipu_crtc->plane[0]); | ||
| 372 | if (ret) { | ||
| 373 | dev_err(ipu_crtc->dev, "getting plane 0 resources failed with %d.\n", | ||
| 374 | ret); | ||
| 375 | goto err_remove_crtc; | ||
| 376 | } | ||
| 377 | |||
| 378 | /* If this crtc is using the DP, add an overlay plane */ | ||
| 379 | if (pdata->dp >= 0 && pdata->dma[1] > 0) { | ||
| 380 | ipu_crtc->plane[1] = ipu_plane_init(ipu_crtc->base.dev, ipu, | ||
| 381 | pdata->dma[1], | ||
| 382 | IPU_DP_FLOW_SYNC_FG, | ||
| 383 | BIT(id), false); | ||
| 384 | if (IS_ERR(ipu_crtc->plane[1])) | ||
| 385 | ipu_crtc->plane[1] = NULL; | ||
| 386 | } | ||
| 387 | |||
| 388 | ipu_crtc->irq = ipu_plane_irq(ipu_crtc->plane[0]); | ||
| 389 | ret = devm_request_irq(ipu_crtc->dev, ipu_crtc->irq, ipu_irq_handler, 0, | ||
| 390 | "imx_drm", ipu_crtc); | ||
| 391 | if (ret < 0) { | ||
| 392 | dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret); | ||
| 393 | goto err_put_plane_res; | ||
| 394 | } | ||
| 395 | |||
| 396 | return 0; | ||
| 397 | |||
| 398 | err_put_plane_res: | ||
| 399 | ipu_plane_put_resources(ipu_crtc->plane[0]); | ||
| 400 | err_remove_crtc: | ||
| 401 | imx_drm_remove_crtc(ipu_crtc->imx_crtc); | ||
| 402 | err_put_resources: | ||
| 403 | ipu_put_resources(ipu_crtc); | ||
| 404 | |||
| 405 | return ret; | ||
| 406 | } | ||
| 407 | |||
| 408 | static struct device_node *ipu_drm_get_port_by_id(struct device_node *parent, | ||
| 409 | int port_id) | ||
| 410 | { | ||
| 411 | struct device_node *port; | ||
| 412 | int id, ret; | ||
| 413 | |||
| 414 | port = of_get_child_by_name(parent, "port"); | ||
| 415 | while (port) { | ||
| 416 | ret = of_property_read_u32(port, "reg", &id); | ||
| 417 | if (!ret && id == port_id) | ||
| 418 | return port; | ||
| 419 | |||
| 420 | do { | ||
| 421 | port = of_get_next_child(parent, port); | ||
| 422 | if (!port) | ||
| 423 | return NULL; | ||
| 424 | } while (of_node_cmp(port->name, "port")); | ||
| 425 | } | ||
| 426 | |||
| 427 | return NULL; | ||
| 428 | } | ||
| 429 | |||
| 430 | static int ipu_drm_bind(struct device *dev, struct device *master, void *data) | ||
| 431 | { | ||
| 432 | struct ipu_client_platformdata *pdata = dev->platform_data; | ||
| 433 | struct drm_device *drm = data; | ||
| 434 | struct ipu_crtc *ipu_crtc; | ||
| 435 | int ret; | ||
| 436 | |||
| 437 | ipu_crtc = devm_kzalloc(dev, sizeof(*ipu_crtc), GFP_KERNEL); | ||
| 438 | if (!ipu_crtc) | ||
| 439 | return -ENOMEM; | ||
| 440 | |||
| 441 | ipu_crtc->dev = dev; | ||
| 442 | |||
| 443 | ret = ipu_crtc_init(ipu_crtc, pdata, drm); | ||
| 444 | if (ret) | ||
| 445 | return ret; | ||
| 446 | |||
| 447 | dev_set_drvdata(dev, ipu_crtc); | ||
| 448 | |||
| 449 | return 0; | ||
| 450 | } | ||
| 451 | |||
| 452 | static void ipu_drm_unbind(struct device *dev, struct device *master, | ||
| 453 | void *data) | ||
| 454 | { | ||
| 455 | struct ipu_crtc *ipu_crtc = dev_get_drvdata(dev); | ||
| 456 | |||
| 457 | imx_drm_remove_crtc(ipu_crtc->imx_crtc); | ||
| 458 | |||
| 459 | ipu_plane_put_resources(ipu_crtc->plane[0]); | ||
| 460 | ipu_put_resources(ipu_crtc); | ||
| 461 | } | ||
| 462 | |||
| 463 | static const struct component_ops ipu_crtc_ops = { | ||
| 464 | .bind = ipu_drm_bind, | ||
| 465 | .unbind = ipu_drm_unbind, | ||
| 466 | }; | ||
| 467 | |||
| 468 | static int ipu_drm_probe(struct platform_device *pdev) | ||
| 469 | { | ||
| 470 | struct device *dev = &pdev->dev; | ||
| 471 | struct ipu_client_platformdata *pdata = dev->platform_data; | ||
| 472 | int ret; | ||
| 473 | |||
| 474 | if (!dev->platform_data) | ||
| 475 | return -EINVAL; | ||
| 476 | |||
| 477 | if (!dev->of_node) { | ||
| 478 | /* Associate crtc device with the corresponding DI port node */ | ||
| 479 | dev->of_node = ipu_drm_get_port_by_id(dev->parent->of_node, | ||
| 480 | pdata->di + 2); | ||
| 481 | if (!dev->of_node) { | ||
| 482 | dev_err(dev, "missing port@%d node in %s\n", | ||
| 483 | pdata->di + 2, dev->parent->of_node->full_name); | ||
| 484 | return -ENODEV; | ||
| 485 | } | ||
| 486 | } | ||
| 487 | |||
| 488 | ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); | ||
| 489 | if (ret) | ||
| 490 | return ret; | ||
| 491 | |||
| 492 | return component_add(dev, &ipu_crtc_ops); | ||
| 493 | } | ||
| 494 | |||
| 495 | static int ipu_drm_remove(struct platform_device *pdev) | ||
| 496 | { | ||
| 497 | component_del(&pdev->dev, &ipu_crtc_ops); | ||
| 498 | return 0; | ||
| 499 | } | ||
| 500 | |||
| 501 | static struct platform_driver ipu_drm_driver = { | ||
| 502 | .driver = { | ||
| 503 | .name = "imx-ipuv3-crtc", | ||
| 504 | }, | ||
| 505 | .probe = ipu_drm_probe, | ||
| 506 | .remove = ipu_drm_remove, | ||
| 507 | }; | ||
| 508 | module_platform_driver(ipu_drm_driver); | ||
| 509 | |||
| 510 | MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); | ||
| 511 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
| 512 | MODULE_LICENSE("GPL"); | ||
| 513 | 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..6987e16fe99b --- /dev/null +++ b/drivers/gpu/drm/imx/ipuv3-plane.c  | |||
| @@ -0,0 +1,394 @@ | |||
| 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 | int active; | ||
| 68 | |||
| 69 | cma_obj = drm_fb_cma_get_gem_obj(fb, 0); | ||
| 70 | if (!cma_obj) { | ||
| 71 | DRM_DEBUG_KMS("entry is null.\n"); | ||
| 72 | return -EFAULT; | ||
| 73 | } | ||
| 74 | |||
| 75 | dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d", | ||
| 76 | &cma_obj->paddr, x, y); | ||
| 77 | |||
| 78 | eba = cma_obj->paddr + fb->offsets[0] + | ||
| 79 | fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x; | ||
| 80 | |||
| 81 | if (ipu_plane->enabled) { | ||
| 82 | active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch); | ||
| 83 | ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba); | ||
| 84 | ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active); | ||
| 85 | } else { | ||
| 86 | ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba); | ||
| 87 | ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba); | ||
| 88 | } | ||
| 89 | |||
| 90 | /* cache offsets for subsequent pageflips */ | ||
| 91 | ipu_plane->x = x; | ||
| 92 | ipu_plane->y = y; | ||
| 93 | |||
| 94 | return 0; | ||
| 95 | } | ||
| 96 | |||
| 97 | int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc, | ||
| 98 | struct drm_display_mode *mode, | ||
| 99 | struct drm_framebuffer *fb, int crtc_x, int crtc_y, | ||
| 100 | unsigned int crtc_w, unsigned int crtc_h, | ||
| 101 | uint32_t src_x, uint32_t src_y, | ||
| 102 | uint32_t src_w, uint32_t src_h) | ||
| 103 | { | ||
| 104 | struct device *dev = ipu_plane->base.dev->dev; | ||
| 105 | int ret; | ||
| 106 | |||
| 107 | /* no scaling */ | ||
| 108 | if (src_w != crtc_w || src_h != crtc_h) | ||
| 109 | return -EINVAL; | ||
| 110 | |||
| 111 | /* clip to crtc bounds */ | ||
| 112 | if (crtc_x < 0) { | ||
| 113 | if (-crtc_x > crtc_w) | ||
| 114 | return -EINVAL; | ||
| 115 | src_x += -crtc_x; | ||
| 116 | src_w -= -crtc_x; | ||
| 117 | crtc_w -= -crtc_x; | ||
| 118 | crtc_x = 0; | ||
| 119 | } | ||
| 120 | if (crtc_y < 0) { | ||
| 121 | if (-crtc_y > crtc_h) | ||
| 122 | return -EINVAL; | ||
| 123 | src_y += -crtc_y; | ||
| 124 | src_h -= -crtc_y; | ||
| 125 | crtc_h -= -crtc_y; | ||
| 126 | crtc_y = 0; | ||
| 127 | } | ||
| 128 | if (crtc_x + crtc_w > mode->hdisplay) { | ||
| 129 | if (crtc_x > mode->hdisplay) | ||
| 130 | return -EINVAL; | ||
| 131 | crtc_w = mode->hdisplay - crtc_x; | ||
| 132 | src_w = crtc_w; | ||
| 133 | } | ||
| 134 | if (crtc_y + crtc_h > mode->vdisplay) { | ||
| 135 | if (crtc_y > mode->vdisplay) | ||
| 136 | return -EINVAL; | ||
| 137 | crtc_h = mode->vdisplay - crtc_y; | ||
| 138 | src_h = crtc_h; | ||
| 139 | } | ||
| 140 | /* full plane minimum width is 13 pixels */ | ||
| 141 | if (crtc_w < 13 && (ipu_plane->dp_flow != IPU_DP_FLOW_SYNC_FG)) | ||
| 142 | return -EINVAL; | ||
| 143 | if (crtc_h < 2) | ||
| 144 | return -EINVAL; | ||
| 145 | |||
| 146 | /* | ||
| 147 | * since we cannot touch active IDMAC channels, we do not support | ||
| 148 | * resizing the enabled plane or changing its format | ||
| 149 | */ | ||
| 150 | if (ipu_plane->enabled) { | ||
| 151 | if (src_w != ipu_plane->w || src_h != ipu_plane->h || | ||
| 152 | fb->pixel_format != ipu_plane->base.fb->pixel_format) | ||
| 153 | return -EINVAL; | ||
| 154 | |||
| 155 | return ipu_plane_set_base(ipu_plane, fb, src_x, src_y); | ||
| 156 | } | ||
| 157 | |||
| 158 | switch (ipu_plane->dp_flow) { | ||
| 159 | case IPU_DP_FLOW_SYNC_BG: | ||
| 160 | ret = ipu_dp_setup_channel(ipu_plane->dp, | ||
| 161 | IPUV3_COLORSPACE_RGB, | ||
| 162 | IPUV3_COLORSPACE_RGB); | ||
| 163 | if (ret) { | ||
| 164 | dev_err(dev, | ||
| 165 | "initializing display processor failed with %d\n", | ||
| 166 | ret); | ||
| 167 | return ret; | ||
| 168 | } | ||
| 169 | ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true); | ||
| 170 | break; | ||
| 171 | case IPU_DP_FLOW_SYNC_FG: | ||
| 172 | ipu_dp_setup_channel(ipu_plane->dp, | ||
| 173 | ipu_drm_fourcc_to_colorspace(fb->pixel_format), | ||
| 174 | IPUV3_COLORSPACE_UNKNOWN); | ||
| 175 | ipu_dp_set_window_pos(ipu_plane->dp, crtc_x, crtc_y); | ||
| 176 | /* Enable local alpha on partial plane */ | ||
| 177 | switch (fb->pixel_format) { | ||
| 178 | case DRM_FORMAT_ARGB8888: | ||
| 179 | case DRM_FORMAT_ABGR8888: | ||
| 180 | ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false); | ||
| 181 | break; | ||
| 182 | default: | ||
| 183 | break; | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | ret = ipu_dmfc_init_channel(ipu_plane->dmfc, crtc_w); | ||
| 188 | if (ret) { | ||
| 189 | dev_err(dev, "initializing dmfc channel failed with %d\n", ret); | ||
| 190 | return ret; | ||
| 191 | } | ||
| 192 | |||
| 193 | ret = ipu_dmfc_alloc_bandwidth(ipu_plane->dmfc, | ||
| 194 | calc_bandwidth(crtc_w, crtc_h, | ||
| 195 | calc_vref(mode)), 64); | ||
| 196 | if (ret) { | ||
| 197 | dev_err(dev, "allocating dmfc bandwidth failed with %d\n", ret); | ||
| 198 | return ret; | ||
| 199 | } | ||
| 200 | |||
| 201 | ipu_cpmem_zero(ipu_plane->ipu_ch); | ||
| 202 | ipu_cpmem_set_resolution(ipu_plane->ipu_ch, src_w, src_h); | ||
| 203 | ret = ipu_cpmem_set_fmt(ipu_plane->ipu_ch, fb->pixel_format); | ||
| 204 | if (ret < 0) { | ||
| 205 | dev_err(dev, "unsupported pixel format 0x%08x\n", | ||
| 206 | fb->pixel_format); | ||
| 207 | return ret; | ||
| 208 | } | ||
| 209 | ipu_cpmem_set_high_priority(ipu_plane->ipu_ch); | ||
| 210 | ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1); | ||
| 211 | ipu_cpmem_set_stride(ipu_plane->ipu_ch, fb->pitches[0]); | ||
| 212 | |||
| 213 | ret = ipu_plane_set_base(ipu_plane, fb, src_x, src_y); | ||
| 214 | if (ret < 0) | ||
| 215 | return ret; | ||
| 216 | |||
| 217 | ipu_plane->w = src_w; | ||
| 218 | ipu_plane->h = src_h; | ||
| 219 | |||
| 220 | return 0; | ||
| 221 | } | ||
| 222 | |||
| 223 | void ipu_plane_put_resources(struct ipu_plane *ipu_plane) | ||
| 224 | { | ||
| 225 | if (!IS_ERR_OR_NULL(ipu_plane->dp)) | ||
| 226 | ipu_dp_put(ipu_plane->dp); | ||
| 227 | if (!IS_ERR_OR_NULL(ipu_plane->dmfc)) | ||
| 228 | ipu_dmfc_put(ipu_plane->dmfc); | ||
| 229 | if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch)) | ||
| 230 | ipu_idmac_put(ipu_plane->ipu_ch); | ||
| 231 | } | ||
| 232 | |||
| 233 | int ipu_plane_get_resources(struct ipu_plane *ipu_plane) | ||
| 234 | { | ||
| 235 | int ret; | ||
| 236 | |||
| 237 | ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma); | ||
| 238 | if (IS_ERR(ipu_plane->ipu_ch)) { | ||
| 239 | ret = PTR_ERR(ipu_plane->ipu_ch); | ||
| 240 | DRM_ERROR("failed to get idmac channel: %d\n", ret); | ||
| 241 | return ret; | ||
| 242 | } | ||
| 243 | |||
| 244 | ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma); | ||
| 245 | if (IS_ERR(ipu_plane->dmfc)) { | ||
| 246 | ret = PTR_ERR(ipu_plane->dmfc); | ||
| 247 | DRM_ERROR("failed to get dmfc: ret %d\n", ret); | ||
| 248 | goto err_out; | ||
| 249 | } | ||
| 250 | |||
| 251 | if (ipu_plane->dp_flow >= 0) { | ||
| 252 | ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow); | ||
| 253 | if (IS_ERR(ipu_plane->dp)) { | ||
| 254 | ret = PTR_ERR(ipu_plane->dp); | ||
| 255 | DRM_ERROR("failed to get dp flow: %d\n", ret); | ||
| 256 | goto err_out; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | return 0; | ||
| 261 | err_out: | ||
| 262 | ipu_plane_put_resources(ipu_plane); | ||
| 263 | |||
| 264 | return ret; | ||
| 265 | } | ||
| 266 | |||
| 267 | void ipu_plane_enable(struct ipu_plane *ipu_plane) | ||
| 268 | { | ||
| 269 | if (ipu_plane->dp) | ||
| 270 | ipu_dp_enable(ipu_plane->ipu); | ||
| 271 | ipu_dmfc_enable_channel(ipu_plane->dmfc); | ||
| 272 | ipu_idmac_enable_channel(ipu_plane->ipu_ch); | ||
| 273 | if (ipu_plane->dp) | ||
| 274 | ipu_dp_enable_channel(ipu_plane->dp); | ||
| 275 | |||
| 276 | ipu_plane->enabled = true; | ||
| 277 | } | ||
| 278 | |||
| 279 | void ipu_plane_disable(struct ipu_plane *ipu_plane) | ||
| 280 | { | ||
| 281 | ipu_plane->enabled = false; | ||
| 282 | |||
| 283 | ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50); | ||
| 284 | |||
| 285 | if (ipu_plane->dp) | ||
| 286 | ipu_dp_disable_channel(ipu_plane->dp); | ||
| 287 | ipu_idmac_disable_channel(ipu_plane->ipu_ch); | ||
| 288 | ipu_dmfc_disable_channel(ipu_plane->dmfc); | ||
| 289 | if (ipu_plane->dp) | ||
| 290 | ipu_dp_disable(ipu_plane->ipu); | ||
| 291 | } | ||
| 292 | |||
| 293 | /* | ||
| 294 | * drm_plane API | ||
| 295 | */ | ||
| 296 | |||
| 297 | static int ipu_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, | ||
| 298 | struct drm_framebuffer *fb, int crtc_x, int crtc_y, | ||
| 299 | unsigned int crtc_w, unsigned int crtc_h, | ||
| 300 | uint32_t src_x, uint32_t src_y, | ||
| 301 | uint32_t src_w, uint32_t src_h) | ||
| 302 | { | ||
| 303 | struct ipu_plane *ipu_plane = to_ipu_plane(plane); | ||
| 304 | int ret = 0; | ||
| 305 | |||
| 306 | DRM_DEBUG_KMS("plane - %p\n", plane); | ||
| 307 | |||
| 308 | if (!ipu_plane->enabled) | ||
| 309 | ret = ipu_plane_get_resources(ipu_plane); | ||
| 310 | if (ret < 0) | ||
| 311 | return ret; | ||
| 312 | |||
| 313 | ret = ipu_plane_mode_set(ipu_plane, crtc, &crtc->hwmode, fb, | ||
| 314 | crtc_x, crtc_y, crtc_w, crtc_h, | ||
| 315 | src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16); | ||
| 316 | if (ret < 0) { | ||
| 317 | ipu_plane_put_resources(ipu_plane); | ||
| 318 | return ret; | ||
| 319 | } | ||
| 320 | |||
| 321 | if (crtc != plane->crtc) | ||
| 322 | dev_info(plane->dev->dev, "crtc change: %p -> %p\n", | ||
| 323 | plane->crtc, crtc); | ||
| 324 | plane->crtc = crtc; | ||
| 325 | |||
| 326 | if (!ipu_plane->enabled) | ||
| 327 | ipu_plane_enable(ipu_plane); | ||
| 328 | |||
| 329 | return 0; | ||
| 330 | } | ||
| 331 | |||
| 332 | static int ipu_disable_plane(struct drm_plane *plane) | ||
| 333 | { | ||
| 334 | struct ipu_plane *ipu_plane = to_ipu_plane(plane); | ||
| 335 | |||
| 336 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 337 | |||
| 338 | if (ipu_plane->enabled) | ||
| 339 | ipu_plane_disable(ipu_plane); | ||
| 340 | |||
| 341 | ipu_plane_put_resources(ipu_plane); | ||
| 342 | |||
| 343 | return 0; | ||
| 344 | } | ||
| 345 | |||
| 346 | static void ipu_plane_destroy(struct drm_plane *plane) | ||
| 347 | { | ||
| 348 | struct ipu_plane *ipu_plane = to_ipu_plane(plane); | ||
| 349 | |||
| 350 | DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); | ||
| 351 | |||
| 352 | ipu_disable_plane(plane); | ||
| 353 | drm_plane_cleanup(plane); | ||
| 354 | kfree(ipu_plane); | ||
| 355 | } | ||
| 356 | |||
| 357 | static struct drm_plane_funcs ipu_plane_funcs = { | ||
| 358 | .update_plane = ipu_update_plane, | ||
| 359 | .disable_plane = ipu_disable_plane, | ||
| 360 | .destroy = ipu_plane_destroy, | ||
| 361 | }; | ||
| 362 | |||
| 363 | struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu, | ||
| 364 | int dma, int dp, unsigned int possible_crtcs, | ||
| 365 | bool priv) | ||
| 366 | { | ||
| 367 | struct ipu_plane *ipu_plane; | ||
| 368 | int ret; | ||
| 369 | |||
| 370 | DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n", | ||
| 371 | dma, dp, possible_crtcs); | ||
| 372 | |||
| 373 | ipu_plane = kzalloc(sizeof(*ipu_plane), GFP_KERNEL); | ||
| 374 | if (!ipu_plane) { | ||
| 375 | DRM_ERROR("failed to allocate plane\n"); | ||
| 376 | return ERR_PTR(-ENOMEM); | ||
| 377 | } | ||
| 378 | |||
| 379 | ipu_plane->ipu = ipu; | ||
| 380 | ipu_plane->dma = dma; | ||
| 381 | ipu_plane->dp_flow = dp; | ||
| 382 | |||
| 383 | ret = drm_plane_init(dev, &ipu_plane->base, possible_crtcs, | ||
| 384 | &ipu_plane_funcs, ipu_plane_formats, | ||
| 385 | ARRAY_SIZE(ipu_plane_formats), | ||
| 386 | priv); | ||
| 387 | if (ret) { | ||
| 388 | DRM_ERROR("failed to initialize plane\n"); | ||
| 389 | kfree(ipu_plane); | ||
| 390 | return ERR_PTR(ret); | ||
| 391 | } | ||
| 392 | |||
| 393 | return ipu_plane; | ||
| 394 | } | ||
diff --git a/drivers/gpu/drm/imx/ipuv3-plane.h b/drivers/gpu/drm/imx/ipuv3-plane.h new file mode 100644 index 000000000000..af125fb40ef5 --- /dev/null +++ b/drivers/gpu/drm/imx/ipuv3-plane.h  | |||
| @@ -0,0 +1,57 @@ | |||
| 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 | int w; | ||
| 30 | int h; | ||
| 31 | |||
| 32 | bool enabled; | ||
| 33 | }; | ||
| 34 | |||
| 35 | struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu, | ||
| 36 | int dma, int dp, unsigned int possible_crtcs, | ||
| 37 | bool priv); | ||
| 38 | |||
| 39 | /* Init IDMAC, DMFC, DP */ | ||
| 40 | int ipu_plane_mode_set(struct ipu_plane *plane, struct drm_crtc *crtc, | ||
| 41 | struct drm_display_mode *mode, | ||
| 42 | struct drm_framebuffer *fb, int crtc_x, int crtc_y, | ||
| 43 | unsigned int crtc_w, unsigned int crtc_h, | ||
| 44 | uint32_t src_x, uint32_t src_y, uint32_t src_w, | ||
| 45 | uint32_t src_h); | ||
| 46 | |||
| 47 | void ipu_plane_enable(struct ipu_plane *plane); | ||
| 48 | void ipu_plane_disable(struct ipu_plane *plane); | ||
| 49 | int ipu_plane_set_base(struct ipu_plane *plane, struct drm_framebuffer *fb, | ||
| 50 | int x, int y); | ||
| 51 | |||
| 52 | int ipu_plane_get_resources(struct ipu_plane *plane); | ||
| 53 | void ipu_plane_put_resources(struct ipu_plane *plane); | ||
| 54 | |||
| 55 | int ipu_plane_irq(struct ipu_plane *plane); | ||
| 56 | |||
| 57 | #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..796c3c1c170a --- /dev/null +++ b/drivers/gpu/drm/imx/parallel-display.c  | |||
| @@ -0,0 +1,298 @@ | |||
| 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 | |||
| 16 | #include <linux/component.h> | ||
| 17 | #include <linux/module.h> | ||
| 18 | #include <drm/drmP.h> | ||
| 19 | #include <drm/drm_fb_helper.h> | ||
| 20 | #include <drm/drm_crtc_helper.h> | ||
| 21 | #include <drm/drm_panel.h> | ||
| 22 | #include <linux/videodev2.h> | ||
| 23 | #include <video/of_display_timing.h> | ||
| 24 | |||
| 25 | #include "imx-drm.h" | ||
| 26 | |||
| 27 | #define con_to_imxpd(x) container_of(x, struct imx_parallel_display, connector) | ||
| 28 | #define enc_to_imxpd(x) container_of(x, struct imx_parallel_display, encoder) | ||
| 29 | |||
| 30 | struct imx_parallel_display { | ||
| 31 | struct drm_connector connector; | ||
| 32 | struct drm_encoder encoder; | ||
| 33 | struct device *dev; | ||
| 34 | void *edid; | ||
| 35 | int edid_len; | ||
| 36 | u32 interface_pix_fmt; | ||
| 37 | int mode_valid; | ||
| 38 | struct drm_display_mode mode; | ||
| 39 | struct drm_panel *panel; | ||
| 40 | }; | ||
| 41 | |||
| 42 | static enum drm_connector_status imx_pd_connector_detect( | ||
| 43 | struct drm_connector *connector, bool force) | ||
| 44 | { | ||
| 45 | return connector_status_connected; | ||
| 46 | } | ||
| 47 | |||
| 48 | static int imx_pd_connector_get_modes(struct drm_connector *connector) | ||
| 49 | { | ||
| 50 | struct imx_parallel_display *imxpd = con_to_imxpd(connector); | ||
| 51 | struct device_node *np = imxpd->dev->of_node; | ||
| 52 | int num_modes = 0; | ||
| 53 | |||
| 54 | if (imxpd->panel && imxpd->panel->funcs && | ||
| 55 | imxpd->panel->funcs->get_modes) { | ||
| 56 | num_modes = imxpd->panel->funcs->get_modes(imxpd->panel); | ||
| 57 | if (num_modes > 0) | ||
| 58 | return num_modes; | ||
| 59 | } | ||
| 60 | |||
| 61 | if (imxpd->edid) { | ||
| 62 | drm_mode_connector_update_edid_property(connector, imxpd->edid); | ||
| 63 | num_modes = drm_add_edid_modes(connector, imxpd->edid); | ||
| 64 | } | ||
| 65 | |||
| 66 | if (imxpd->mode_valid) { | ||
| 67 | struct drm_display_mode *mode = drm_mode_create(connector->dev); | ||
| 68 | |||
| 69 | if (!mode) | ||
| 70 | return -EINVAL; | ||
| 71 | drm_mode_copy(mode, &imxpd->mode); | ||
| 72 | mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, | ||
| 73 | drm_mode_probed_add(connector, mode); | ||
| 74 | num_modes++; | ||
| 75 | } | ||
| 76 | |||
| 77 | if (np) { | ||
| 78 | struct drm_display_mode *mode = drm_mode_create(connector->dev); | ||
| 79 | |||
| 80 | if (!mode) | ||
| 81 | return -EINVAL; | ||
| 82 | of_get_drm_display_mode(np, &imxpd->mode, OF_USE_NATIVE_MODE); | ||
| 83 | drm_mode_copy(mode, &imxpd->mode); | ||
| 84 | mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, | ||
| 85 | drm_mode_probed_add(connector, mode); | ||
| 86 | num_modes++; | ||
| 87 | } | ||
| 88 | |||
| 89 | return num_modes; | ||
| 90 | } | ||
| 91 | |||
| 92 | static struct drm_encoder *imx_pd_connector_best_encoder( | ||
| 93 | struct drm_connector *connector) | ||
| 94 | { | ||
| 95 | struct imx_parallel_display *imxpd = con_to_imxpd(connector); | ||
| 96 | |||
| 97 | return &imxpd->encoder; | ||
| 98 | } | ||
| 99 | |||
| 100 | static void imx_pd_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
| 101 | { | ||
| 102 | struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); | ||
| 103 | |||
| 104 | if (mode != DRM_MODE_DPMS_ON) | ||
| 105 | drm_panel_disable(imxpd->panel); | ||
| 106 | else | ||
| 107 | drm_panel_enable(imxpd->panel); | ||
| 108 | } | ||
| 109 | |||
| 110 | static bool imx_pd_encoder_mode_fixup(struct drm_encoder *encoder, | ||
| 111 | const struct drm_display_mode *mode, | ||
| 112 | struct drm_display_mode *adjusted_mode) | ||
| 113 | { | ||
| 114 | return true; | ||
| 115 | } | ||
| 116 | |||
| 117 | static void imx_pd_encoder_prepare(struct drm_encoder *encoder) | ||
| 118 | { | ||
| 119 | struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); | ||
| 120 | |||
| 121 | imx_drm_panel_format(encoder, imxpd->interface_pix_fmt); | ||
| 122 | } | ||
| 123 | |||
| 124 | static void imx_pd_encoder_commit(struct drm_encoder *encoder) | ||
| 125 | { | ||
| 126 | struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); | ||
| 127 | |||
| 128 | drm_panel_prepare(imxpd->panel); | ||
| 129 | drm_panel_enable(imxpd->panel); | ||
| 130 | } | ||
| 131 | |||
| 132 | static void imx_pd_encoder_mode_set(struct drm_encoder *encoder, | ||
| 133 | struct drm_display_mode *mode, | ||
| 134 | struct drm_display_mode *adjusted_mode) | ||
| 135 | { | ||
| 136 | } | ||
| 137 | |||
| 138 | static void imx_pd_encoder_disable(struct drm_encoder *encoder) | ||
| 139 | { | ||
| 140 | struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); | ||
| 141 | |||
| 142 | drm_panel_disable(imxpd->panel); | ||
| 143 | drm_panel_unprepare(imxpd->panel); | ||
| 144 | } | ||
| 145 | |||
| 146 | static struct drm_connector_funcs imx_pd_connector_funcs = { | ||
| 147 | .dpms = drm_helper_connector_dpms, | ||
| 148 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
| 149 | .detect = imx_pd_connector_detect, | ||
| 150 | .destroy = imx_drm_connector_destroy, | ||
| 151 | }; | ||
| 152 | |||
| 153 | static struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = { | ||
| 154 | .get_modes = imx_pd_connector_get_modes, | ||
| 155 | .best_encoder = imx_pd_connector_best_encoder, | ||
| 156 | }; | ||
| 157 | |||
| 158 | static struct drm_encoder_funcs imx_pd_encoder_funcs = { | ||
| 159 | .destroy = imx_drm_encoder_destroy, | ||
| 160 | }; | ||
| 161 | |||
| 162 | static struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = { | ||
| 163 | .dpms = imx_pd_encoder_dpms, | ||
| 164 | .mode_fixup = imx_pd_encoder_mode_fixup, | ||
| 165 | .prepare = imx_pd_encoder_prepare, | ||
| 166 | .commit = imx_pd_encoder_commit, | ||
| 167 | .mode_set = imx_pd_encoder_mode_set, | ||
| 168 | .disable = imx_pd_encoder_disable, | ||
| 169 | }; | ||
| 170 | |||
| 171 | static int imx_pd_register(struct drm_device *drm, | ||
| 172 | struct imx_parallel_display *imxpd) | ||
| 173 | { | ||
| 174 | int ret; | ||
| 175 | |||
| 176 | ret = imx_drm_encoder_parse_of(drm, &imxpd->encoder, | ||
| 177 | imxpd->dev->of_node); | ||
| 178 | if (ret) | ||
| 179 | return ret; | ||
| 180 | |||
| 181 | /* set the connector's dpms to OFF so that | ||
| 182 | * drm_helper_connector_dpms() won't return | ||
| 183 | * immediately since the current state is ON | ||
| 184 | * at this point. | ||
| 185 | */ | ||
| 186 | imxpd->connector.dpms = DRM_MODE_DPMS_OFF; | ||
| 187 | |||
| 188 | drm_encoder_helper_add(&imxpd->encoder, &imx_pd_encoder_helper_funcs); | ||
| 189 | drm_encoder_init(drm, &imxpd->encoder, &imx_pd_encoder_funcs, | ||
| 190 | DRM_MODE_ENCODER_NONE); | ||
| 191 | |||
| 192 | drm_connector_helper_add(&imxpd->connector, | ||
| 193 | &imx_pd_connector_helper_funcs); | ||
| 194 | drm_connector_init(drm, &imxpd->connector, &imx_pd_connector_funcs, | ||
| 195 | DRM_MODE_CONNECTOR_VGA); | ||
| 196 | |||
| 197 | if (imxpd->panel) | ||
| 198 | drm_panel_attach(imxpd->panel, &imxpd->connector); | ||
| 199 | |||
| 200 | drm_mode_connector_attach_encoder(&imxpd->connector, &imxpd->encoder); | ||
| 201 | |||
| 202 | imxpd->connector.encoder = &imxpd->encoder; | ||
| 203 | |||
| 204 | return 0; | ||
| 205 | } | ||
| 206 | |||
| 207 | static int imx_pd_bind(struct device *dev, struct device *master, void *data) | ||
| 208 | { | ||
| 209 | struct drm_device *drm = data; | ||
| 210 | struct device_node *np = dev->of_node; | ||
| 211 | struct device_node *panel_node; | ||
| 212 | const u8 *edidp; | ||
| 213 | struct imx_parallel_display *imxpd; | ||
| 214 | int ret; | ||
| 215 | const char *fmt; | ||
| 216 | |||
| 217 | imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL); | ||
| 218 | if (!imxpd) | ||
| 219 | return -ENOMEM; | ||
| 220 | |||
| 221 | edidp = of_get_property(np, "edid", &imxpd->edid_len); | ||
| 222 | if (edidp) | ||
| 223 | imxpd->edid = kmemdup(edidp, imxpd->edid_len, GFP_KERNEL); | ||
| 224 | |||
| 225 | ret = of_property_read_string(np, "interface-pix-fmt", &fmt); | ||
| 226 | if (!ret) { | ||
| 227 | if (!strcmp(fmt, "rgb24")) | ||
| 228 | imxpd->interface_pix_fmt = V4L2_PIX_FMT_RGB24; | ||
| 229 | else if (!strcmp(fmt, "rgb565")) | ||
| 230 | imxpd->interface_pix_fmt = V4L2_PIX_FMT_RGB565; | ||
| 231 | else if (!strcmp(fmt, "bgr666")) | ||
| 232 | imxpd->interface_pix_fmt = V4L2_PIX_FMT_BGR666; | ||
| 233 | else if (!strcmp(fmt, "lvds666")) | ||
| 234 | imxpd->interface_pix_fmt = | ||
| 235 | v4l2_fourcc('L', 'V', 'D', '6'); | ||
| 236 | } | ||
| 237 | |||
| 238 | panel_node = of_parse_phandle(np, "fsl,panel", 0); | ||
| 239 | if (panel_node) | ||
| 240 | imxpd->panel = of_drm_find_panel(panel_node); | ||
| 241 | |||
| 242 | imxpd->dev = dev; | ||
| 243 | |||
| 244 | ret = imx_pd_register(drm, imxpd); | ||
| 245 | if (ret) | ||
| 246 | return ret; | ||
| 247 | |||
| 248 | dev_set_drvdata(dev, imxpd); | ||
| 249 | |||
| 250 | return 0; | ||
| 251 | } | ||
| 252 | |||
| 253 | static void imx_pd_unbind(struct device *dev, struct device *master, | ||
| 254 | void *data) | ||
| 255 | { | ||
| 256 | struct imx_parallel_display *imxpd = dev_get_drvdata(dev); | ||
| 257 | |||
| 258 | imxpd->encoder.funcs->destroy(&imxpd->encoder); | ||
| 259 | imxpd->connector.funcs->destroy(&imxpd->connector); | ||
| 260 | } | ||
| 261 | |||
| 262 | static const struct component_ops imx_pd_ops = { | ||
| 263 | .bind = imx_pd_bind, | ||
| 264 | .unbind = imx_pd_unbind, | ||
| 265 | }; | ||
| 266 | |||
| 267 | static int imx_pd_probe(struct platform_device *pdev) | ||
| 268 | { | ||
| 269 | return component_add(&pdev->dev, &imx_pd_ops); | ||
| 270 | } | ||
| 271 | |||
| 272 | static int imx_pd_remove(struct platform_device *pdev) | ||
| 273 | { | ||
| 274 | component_del(&pdev->dev, &imx_pd_ops); | ||
| 275 | return 0; | ||
| 276 | } | ||
| 277 | |||
| 278 | static const struct of_device_id imx_pd_dt_ids[] = { | ||
| 279 | { .compatible = "fsl,imx-parallel-display", }, | ||
| 280 | { /* sentinel */ } | ||
| 281 | }; | ||
| 282 | MODULE_DEVICE_TABLE(of, imx_pd_dt_ids); | ||
| 283 | |||
| 284 | static struct platform_driver imx_pd_driver = { | ||
| 285 | .probe = imx_pd_probe, | ||
| 286 | .remove = imx_pd_remove, | ||
| 287 | .driver = { | ||
| 288 | .of_match_table = imx_pd_dt_ids, | ||
| 289 | .name = "imx-parallel-display", | ||
| 290 | }, | ||
| 291 | }; | ||
| 292 | |||
| 293 | module_platform_driver(imx_pd_driver); | ||
| 294 | |||
| 295 | MODULE_DESCRIPTION("i.MX parallel display driver"); | ||
| 296 | MODULE_AUTHOR("Sascha Hauer, Pengutronix"); | ||
| 297 | MODULE_LICENSE("GPL"); | ||
| 298 | MODULE_ALIAS("platform:imx-parallel-display"); | ||
