1/* Copyright (C) 2011-2013 Freescale Semiconductor, Inc. 2 * 3 * derived from imx-hdmi.c(renamed to bridge/dw_hdmi.c now) 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 */ 9#include <linux/module.h> 10#include <linux/platform_device.h> 11#include <linux/component.h> 12#include <linux/mfd/syscon.h> 13#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> 14#include <drm/bridge/dw_hdmi.h> 15#include <video/imx-ipu-v3.h> 16#include <linux/regmap.h> 17#include <drm/drm_of.h> 18#include <drm/drmP.h> 19#include <drm/drm_crtc_helper.h> 20#include <drm/drm_edid.h> 21#include <drm/drm_encoder_slave.h> 22 23#include "imx-drm.h" 24 25struct imx_hdmi { 26 struct device *dev; 27 struct drm_encoder encoder; 28 struct regmap *regmap; 29}; 30 31static const struct dw_hdmi_mpll_config imx_mpll_cfg[] = { 32 { 33 45250000, { 34 { 0x01e0, 0x0000 }, 35 { 0x21e1, 0x0000 }, 36 { 0x41e2, 0x0000 } 37 }, 38 }, { 39 92500000, { 40 { 0x0140, 0x0005 }, 41 { 0x2141, 0x0005 }, 42 { 0x4142, 0x0005 }, 43 }, 44 }, { 45 148500000, { 46 { 0x00a0, 0x000a }, 47 { 0x20a1, 0x000a }, 48 { 0x40a2, 0x000a }, 49 }, 50 }, { 51 ~0UL, { 52 { 0x00a0, 0x000a }, 53 { 0x2001, 0x000f }, 54 { 0x4002, 0x000f }, 55 }, 56 } 57}; 58 59static const struct dw_hdmi_curr_ctrl imx_cur_ctr[] = { 60 /* pixelclk bpp8 bpp10 bpp12 */ 61 { 62 54000000, { 0x091c, 0x091c, 0x06dc }, 63 }, { 64 58400000, { 0x091c, 0x06dc, 0x06dc }, 65 }, { 66 72000000, { 0x06dc, 0x06dc, 0x091c }, 67 }, { 68 74250000, { 0x06dc, 0x0b5c, 0x091c }, 69 }, { 70 118800000, { 0x091c, 0x091c, 0x06dc }, 71 }, { 72 216000000, { 0x06dc, 0x0b5c, 0x091c }, 73 }, { 74 ~0UL, { 0x0000, 0x0000, 0x0000 }, 75 }, 76}; 77 78static const struct dw_hdmi_phy_config imx_phy_config[] = { 79 /*pixelclk symbol term vlev */ 80 { 148500000, 0x800d, 0x0005, 0x01ad}, 81 { ~0UL, 0x0000, 0x0000, 0x0000} 82}; 83 84static int dw_hdmi_imx_parse_dt(struct imx_hdmi *hdmi) 85{ 86 struct device_node *np = hdmi->dev->of_node; 87 88 hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr"); 89 if (IS_ERR(hdmi->regmap)) { 90 dev_err(hdmi->dev, "Unable to get gpr\n"); 91 return PTR_ERR(hdmi->regmap); 92 } 93 94 return 0; 95} 96 97static void dw_hdmi_imx_encoder_disable(struct drm_encoder *encoder) 98{ 99} 100 101static bool dw_hdmi_imx_encoder_mode_fixup(struct drm_encoder *encoder, 102 const struct drm_display_mode *mode, 103 struct drm_display_mode *adj_mode) 104{ 105 return true; 106} 107 108static void dw_hdmi_imx_encoder_mode_set(struct drm_encoder *encoder, 109 struct drm_display_mode *mode, 110 struct drm_display_mode *adj_mode) 111{ 112} 113 114static void dw_hdmi_imx_encoder_commit(struct drm_encoder *encoder) 115{ 116 struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); 117 int mux = imx_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder); 118 119 regmap_update_bits(hdmi->regmap, IOMUXC_GPR3, 120 IMX6Q_GPR3_HDMI_MUX_CTL_MASK, 121 mux << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT); 122} 123 124static void dw_hdmi_imx_encoder_prepare(struct drm_encoder *encoder) 125{ 126 imx_drm_set_bus_format(encoder, MEDIA_BUS_FMT_RGB888_1X24); 127} 128 129static struct drm_encoder_helper_funcs dw_hdmi_imx_encoder_helper_funcs = { 130 .mode_fixup = dw_hdmi_imx_encoder_mode_fixup, 131 .mode_set = dw_hdmi_imx_encoder_mode_set, 132 .prepare = dw_hdmi_imx_encoder_prepare, 133 .commit = dw_hdmi_imx_encoder_commit, 134 .disable = dw_hdmi_imx_encoder_disable, 135}; 136 137static struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = { 138 .destroy = drm_encoder_cleanup, 139}; 140 141static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con, 142 struct drm_display_mode *mode) 143{ 144 if (mode->clock < 13500) 145 return MODE_CLOCK_LOW; 146 if (mode->clock > 266000) 147 return MODE_CLOCK_HIGH; 148 149 return MODE_OK; 150} 151 152static enum drm_mode_status imx6dl_hdmi_mode_valid(struct drm_connector *con, 153 struct drm_display_mode *mode) 154{ 155 if (mode->clock < 13500) 156 return MODE_CLOCK_LOW; 157 if (mode->clock > 270000) 158 return MODE_CLOCK_HIGH; 159 160 return MODE_OK; 161} 162 163static struct dw_hdmi_plat_data imx6q_hdmi_drv_data = { 164 .mpll_cfg = imx_mpll_cfg, 165 .cur_ctr = imx_cur_ctr, 166 .phy_config = imx_phy_config, 167 .dev_type = IMX6Q_HDMI, 168 .mode_valid = imx6q_hdmi_mode_valid, 169}; 170 171static struct dw_hdmi_plat_data imx6dl_hdmi_drv_data = { 172 .mpll_cfg = imx_mpll_cfg, 173 .cur_ctr = imx_cur_ctr, 174 .phy_config = imx_phy_config, 175 .dev_type = IMX6DL_HDMI, 176 .mode_valid = imx6dl_hdmi_mode_valid, 177}; 178 179static const struct of_device_id dw_hdmi_imx_dt_ids[] = { 180 { .compatible = "fsl,imx6q-hdmi", 181 .data = &imx6q_hdmi_drv_data 182 }, { 183 .compatible = "fsl,imx6dl-hdmi", 184 .data = &imx6dl_hdmi_drv_data 185 }, 186 {}, 187}; 188MODULE_DEVICE_TABLE(of, dw_hdmi_imx_dt_ids); 189 190static int dw_hdmi_imx_bind(struct device *dev, struct device *master, 191 void *data) 192{ 193 struct platform_device *pdev = to_platform_device(dev); 194 const struct dw_hdmi_plat_data *plat_data; 195 const struct of_device_id *match; 196 struct drm_device *drm = data; 197 struct drm_encoder *encoder; 198 struct imx_hdmi *hdmi; 199 struct resource *iores; 200 int irq; 201 int ret; 202 203 if (!pdev->dev.of_node) 204 return -ENODEV; 205 206 hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); 207 if (!hdmi) 208 return -ENOMEM; 209 210 match = of_match_node(dw_hdmi_imx_dt_ids, pdev->dev.of_node); 211 plat_data = match->data; 212 hdmi->dev = &pdev->dev; 213 encoder = &hdmi->encoder; 214 215 irq = platform_get_irq(pdev, 0); 216 if (irq < 0) 217 return irq; 218 219 iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); 220 if (!iores) 221 return -ENXIO; 222 223 platform_set_drvdata(pdev, hdmi); 224 225 encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); 226 /* 227 * If we failed to find the CRTC(s) which this encoder is 228 * supposed to be connected to, it's because the CRTC has 229 * not been registered yet. Defer probing, and hope that 230 * the required CRTC is added later. 231 */ 232 if (encoder->possible_crtcs == 0) 233 return -EPROBE_DEFER; 234 235 ret = dw_hdmi_imx_parse_dt(hdmi); 236 if (ret < 0) 237 return ret; 238 239 drm_encoder_helper_add(encoder, &dw_hdmi_imx_encoder_helper_funcs); 240 drm_encoder_init(drm, encoder, &dw_hdmi_imx_encoder_funcs, 241 DRM_MODE_ENCODER_TMDS); 242 243 return dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data); 244} 245 246static void dw_hdmi_imx_unbind(struct device *dev, struct device *master, 247 void *data) 248{ 249 return dw_hdmi_unbind(dev, master, data); 250} 251 252static const struct component_ops dw_hdmi_imx_ops = { 253 .bind = dw_hdmi_imx_bind, 254 .unbind = dw_hdmi_imx_unbind, 255}; 256 257static int dw_hdmi_imx_probe(struct platform_device *pdev) 258{ 259 return component_add(&pdev->dev, &dw_hdmi_imx_ops); 260} 261 262static int dw_hdmi_imx_remove(struct platform_device *pdev) 263{ 264 component_del(&pdev->dev, &dw_hdmi_imx_ops); 265 266 return 0; 267} 268 269static struct platform_driver dw_hdmi_imx_platform_driver = { 270 .probe = dw_hdmi_imx_probe, 271 .remove = dw_hdmi_imx_remove, 272 .driver = { 273 .name = "dwhdmi-imx", 274 .of_match_table = dw_hdmi_imx_dt_ids, 275 }, 276}; 277 278module_platform_driver(dw_hdmi_imx_platform_driver); 279 280MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>"); 281MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>"); 282MODULE_DESCRIPTION("IMX6 Specific DW-HDMI Driver Extension"); 283MODULE_LICENSE("GPL"); 284MODULE_ALIAS("platform:dwhdmi-imx"); 285