1 /* Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
3 * derived from imx-hdmi.c(renamed to bridge/dw_hdmi.c now)
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.
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>
19 #include <drm/drm_crtc_helper.h>
20 #include <drm/drm_edid.h>
21 #include <drm/drm_encoder_slave.h>
27 struct drm_encoder encoder
;
28 struct regmap
*regmap
;
31 static const struct dw_hdmi_mpll_config imx_mpll_cfg
[] = {
59 static const struct dw_hdmi_curr_ctrl imx_cur_ctr
[] = {
60 /* pixelclk bpp8 bpp10 bpp12 */
62 54000000, { 0x091c, 0x091c, 0x06dc },
64 58400000, { 0x091c, 0x06dc, 0x06dc },
66 72000000, { 0x06dc, 0x06dc, 0x091c },
68 74250000, { 0x06dc, 0x0b5c, 0x091c },
70 118800000, { 0x091c, 0x091c, 0x06dc },
72 216000000, { 0x06dc, 0x0b5c, 0x091c },
74 ~0UL, { 0x0000, 0x0000, 0x0000 },
78 static const struct dw_hdmi_phy_config imx_phy_config
[] = {
79 /*pixelclk symbol term vlev */
80 { 148500000, 0x800d, 0x0005, 0x01ad},
81 { ~0UL, 0x0000, 0x0000, 0x0000}
84 static int dw_hdmi_imx_parse_dt(struct imx_hdmi
*hdmi
)
86 struct device_node
*np
= hdmi
->dev
->of_node
;
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
);
97 static void dw_hdmi_imx_encoder_disable(struct drm_encoder
*encoder
)
101 static 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
)
108 static void dw_hdmi_imx_encoder_mode_set(struct drm_encoder
*encoder
,
109 struct drm_display_mode
*mode
,
110 struct drm_display_mode
*adj_mode
)
114 static void dw_hdmi_imx_encoder_commit(struct drm_encoder
*encoder
)
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
);
119 regmap_update_bits(hdmi
->regmap
, IOMUXC_GPR3
,
120 IMX6Q_GPR3_HDMI_MUX_CTL_MASK
,
121 mux
<< IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT
);
124 static void dw_hdmi_imx_encoder_prepare(struct drm_encoder
*encoder
)
126 imx_drm_set_bus_format(encoder
, MEDIA_BUS_FMT_RGB888_1X24
);
129 static 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
,
137 static struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs
= {
138 .destroy
= drm_encoder_cleanup
,
141 static enum drm_mode_status
imx6q_hdmi_mode_valid(struct drm_connector
*con
,
142 struct drm_display_mode
*mode
)
144 if (mode
->clock
< 13500)
145 return MODE_CLOCK_LOW
;
146 if (mode
->clock
> 266000)
147 return MODE_CLOCK_HIGH
;
152 static enum drm_mode_status
imx6dl_hdmi_mode_valid(struct drm_connector
*con
,
153 struct drm_display_mode
*mode
)
155 if (mode
->clock
< 13500)
156 return MODE_CLOCK_LOW
;
157 if (mode
->clock
> 270000)
158 return MODE_CLOCK_HIGH
;
163 static 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
,
171 static 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
,
179 static const struct of_device_id dw_hdmi_imx_dt_ids
[] = {
180 { .compatible
= "fsl,imx6q-hdmi",
181 .data
= &imx6q_hdmi_drv_data
183 .compatible
= "fsl,imx6dl-hdmi",
184 .data
= &imx6dl_hdmi_drv_data
188 MODULE_DEVICE_TABLE(of
, dw_hdmi_imx_dt_ids
);
190 static int dw_hdmi_imx_bind(struct device
*dev
, struct device
*master
,
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
;
203 if (!pdev
->dev
.of_node
)
206 hdmi
= devm_kzalloc(&pdev
->dev
, sizeof(*hdmi
), GFP_KERNEL
);
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
;
215 irq
= platform_get_irq(pdev
, 0);
219 iores
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
223 platform_set_drvdata(pdev
, hdmi
);
225 encoder
->possible_crtcs
= drm_of_find_possible_crtcs(drm
, dev
->of_node
);
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.
232 if (encoder
->possible_crtcs
== 0)
233 return -EPROBE_DEFER
;
235 ret
= dw_hdmi_imx_parse_dt(hdmi
);
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
);
243 return dw_hdmi_bind(dev
, master
, data
, encoder
, iores
, irq
, plat_data
);
246 static void dw_hdmi_imx_unbind(struct device
*dev
, struct device
*master
,
249 return dw_hdmi_unbind(dev
, master
, data
);
252 static const struct component_ops dw_hdmi_imx_ops
= {
253 .bind
= dw_hdmi_imx_bind
,
254 .unbind
= dw_hdmi_imx_unbind
,
257 static int dw_hdmi_imx_probe(struct platform_device
*pdev
)
259 return component_add(&pdev
->dev
, &dw_hdmi_imx_ops
);
262 static int dw_hdmi_imx_remove(struct platform_device
*pdev
)
264 component_del(&pdev
->dev
, &dw_hdmi_imx_ops
);
269 static struct platform_driver dw_hdmi_imx_platform_driver
= {
270 .probe
= dw_hdmi_imx_probe
,
271 .remove
= dw_hdmi_imx_remove
,
273 .name
= "dwhdmi-imx",
274 .of_match_table
= dw_hdmi_imx_dt_ids
,
278 module_platform_driver(dw_hdmi_imx_platform_driver
);
280 MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
281 MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
282 MODULE_DESCRIPTION("IMX6 Specific DW-HDMI Driver Extension");
283 MODULE_LICENSE("GPL");
284 MODULE_ALIAS("platform:dwhdmi-imx");