1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
4 * derived from imx-hdmi.c(renamed to bridge/dw_hdmi.c now)
6 #include <linux/module.h>
7 #include <linux/platform_device.h>
8 #include <linux/component.h>
9 #include <linux/mfd/syscon.h>
10 #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
11 #include <drm/bridge/dw_hdmi.h>
12 #include <video/imx-ipu-v3.h>
13 #include <linux/regmap.h>
14 #include <drm/drm_of.h>
16 #include <drm/drm_atomic_helper.h>
17 #include <drm/drm_edid.h>
18 #include <drm/drm_encoder_slave.h>
24 struct drm_encoder encoder
;
26 struct regmap
*regmap
;
29 static inline struct imx_hdmi
*enc_to_imx_hdmi(struct drm_encoder
*e
)
31 return container_of(e
, struct imx_hdmi
, encoder
);
34 static const struct dw_hdmi_mpll_config imx_mpll_cfg
[] = {
68 static const struct dw_hdmi_curr_ctrl imx_cur_ctr
[] = {
69 /* pixelclk bpp8 bpp10 bpp12 */
71 54000000, { 0x091c, 0x091c, 0x06dc },
73 58400000, { 0x091c, 0x06dc, 0x06dc },
75 72000000, { 0x06dc, 0x06dc, 0x091c },
77 74250000, { 0x06dc, 0x0b5c, 0x091c },
79 118800000, { 0x091c, 0x091c, 0x06dc },
81 216000000, { 0x06dc, 0x0b5c, 0x091c },
83 ~0UL, { 0x0000, 0x0000, 0x0000 },
88 * Resistance term 133Ohm Cfg
92 static const struct dw_hdmi_phy_config imx_phy_config
[] = {
93 /*pixelclk symbol term vlev */
94 { 216000000, 0x800d, 0x0005, 0x01ad},
95 { ~0UL, 0x0000, 0x0000, 0x0000}
98 static int dw_hdmi_imx_parse_dt(struct imx_hdmi
*hdmi
)
100 struct device_node
*np
= hdmi
->dev
->of_node
;
102 hdmi
->regmap
= syscon_regmap_lookup_by_phandle(np
, "gpr");
103 if (IS_ERR(hdmi
->regmap
)) {
104 dev_err(hdmi
->dev
, "Unable to get gpr\n");
105 return PTR_ERR(hdmi
->regmap
);
111 static void dw_hdmi_imx_encoder_disable(struct drm_encoder
*encoder
)
115 static void dw_hdmi_imx_encoder_enable(struct drm_encoder
*encoder
)
117 struct imx_hdmi
*hdmi
= enc_to_imx_hdmi(encoder
);
118 int mux
= drm_of_encoder_active_port_id(hdmi
->dev
->of_node
, encoder
);
120 regmap_update_bits(hdmi
->regmap
, IOMUXC_GPR3
,
121 IMX6Q_GPR3_HDMI_MUX_CTL_MASK
,
122 mux
<< IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT
);
125 static int dw_hdmi_imx_atomic_check(struct drm_encoder
*encoder
,
126 struct drm_crtc_state
*crtc_state
,
127 struct drm_connector_state
*conn_state
)
129 struct imx_crtc_state
*imx_crtc_state
= to_imx_crtc_state(crtc_state
);
131 imx_crtc_state
->bus_format
= MEDIA_BUS_FMT_RGB888_1X24
;
132 imx_crtc_state
->di_hsync_pin
= 2;
133 imx_crtc_state
->di_vsync_pin
= 3;
138 static const struct drm_encoder_helper_funcs dw_hdmi_imx_encoder_helper_funcs
= {
139 .enable
= dw_hdmi_imx_encoder_enable
,
140 .disable
= dw_hdmi_imx_encoder_disable
,
141 .atomic_check
= dw_hdmi_imx_atomic_check
,
144 static const struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs
= {
145 .destroy
= drm_encoder_cleanup
,
148 static enum drm_mode_status
149 imx6q_hdmi_mode_valid(struct drm_connector
*con
,
150 const struct drm_display_mode
*mode
)
152 if (mode
->clock
< 13500)
153 return MODE_CLOCK_LOW
;
154 /* FIXME: Hardware is capable of 266MHz, but setup data is missing. */
155 if (mode
->clock
> 216000)
156 return MODE_CLOCK_HIGH
;
161 static enum drm_mode_status
162 imx6dl_hdmi_mode_valid(struct drm_connector
*con
,
163 const struct drm_display_mode
*mode
)
165 if (mode
->clock
< 13500)
166 return MODE_CLOCK_LOW
;
167 /* FIXME: Hardware is capable of 270MHz, but setup data is missing. */
168 if (mode
->clock
> 216000)
169 return MODE_CLOCK_HIGH
;
174 static struct dw_hdmi_plat_data imx6q_hdmi_drv_data
= {
175 .mpll_cfg
= imx_mpll_cfg
,
176 .cur_ctr
= imx_cur_ctr
,
177 .phy_config
= imx_phy_config
,
178 .mode_valid
= imx6q_hdmi_mode_valid
,
181 static struct dw_hdmi_plat_data imx6dl_hdmi_drv_data
= {
182 .mpll_cfg
= imx_mpll_cfg
,
183 .cur_ctr
= imx_cur_ctr
,
184 .phy_config
= imx_phy_config
,
185 .mode_valid
= imx6dl_hdmi_mode_valid
,
188 static const struct of_device_id dw_hdmi_imx_dt_ids
[] = {
189 { .compatible
= "fsl,imx6q-hdmi",
190 .data
= &imx6q_hdmi_drv_data
192 .compatible
= "fsl,imx6dl-hdmi",
193 .data
= &imx6dl_hdmi_drv_data
197 MODULE_DEVICE_TABLE(of
, dw_hdmi_imx_dt_ids
);
199 static int dw_hdmi_imx_bind(struct device
*dev
, struct device
*master
,
202 struct platform_device
*pdev
= to_platform_device(dev
);
203 const struct dw_hdmi_plat_data
*plat_data
;
204 const struct of_device_id
*match
;
205 struct drm_device
*drm
= data
;
206 struct drm_encoder
*encoder
;
207 struct imx_hdmi
*hdmi
;
210 if (!pdev
->dev
.of_node
)
213 hdmi
= devm_kzalloc(&pdev
->dev
, sizeof(*hdmi
), GFP_KERNEL
);
217 match
= of_match_node(dw_hdmi_imx_dt_ids
, pdev
->dev
.of_node
);
218 plat_data
= match
->data
;
219 hdmi
->dev
= &pdev
->dev
;
220 encoder
= &hdmi
->encoder
;
222 encoder
->possible_crtcs
= drm_of_find_possible_crtcs(drm
, dev
->of_node
);
224 * If we failed to find the CRTC(s) which this encoder is
225 * supposed to be connected to, it's because the CRTC has
226 * not been registered yet. Defer probing, and hope that
227 * the required CRTC is added later.
229 if (encoder
->possible_crtcs
== 0)
230 return -EPROBE_DEFER
;
232 ret
= dw_hdmi_imx_parse_dt(hdmi
);
236 drm_encoder_helper_add(encoder
, &dw_hdmi_imx_encoder_helper_funcs
);
237 drm_encoder_init(drm
, encoder
, &dw_hdmi_imx_encoder_funcs
,
238 DRM_MODE_ENCODER_TMDS
, NULL
);
240 platform_set_drvdata(pdev
, hdmi
);
242 hdmi
->hdmi
= dw_hdmi_bind(pdev
, encoder
, plat_data
);
245 * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
246 * which would have called the encoder cleanup. Do it manually.
248 if (IS_ERR(hdmi
->hdmi
)) {
249 ret
= PTR_ERR(hdmi
->hdmi
);
250 drm_encoder_cleanup(encoder
);
256 static void dw_hdmi_imx_unbind(struct device
*dev
, struct device
*master
,
259 struct imx_hdmi
*hdmi
= dev_get_drvdata(dev
);
261 dw_hdmi_unbind(hdmi
->hdmi
);
264 static const struct component_ops dw_hdmi_imx_ops
= {
265 .bind
= dw_hdmi_imx_bind
,
266 .unbind
= dw_hdmi_imx_unbind
,
269 static int dw_hdmi_imx_probe(struct platform_device
*pdev
)
271 return component_add(&pdev
->dev
, &dw_hdmi_imx_ops
);
274 static int dw_hdmi_imx_remove(struct platform_device
*pdev
)
276 component_del(&pdev
->dev
, &dw_hdmi_imx_ops
);
281 static struct platform_driver dw_hdmi_imx_platform_driver
= {
282 .probe
= dw_hdmi_imx_probe
,
283 .remove
= dw_hdmi_imx_remove
,
285 .name
= "dwhdmi-imx",
286 .of_match_table
= dw_hdmi_imx_dt_ids
,
290 module_platform_driver(dw_hdmi_imx_platform_driver
);
292 MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
293 MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
294 MODULE_DESCRIPTION("IMX6 Specific DW-HDMI Driver Extension");
295 MODULE_LICENSE("GPL");
296 MODULE_ALIAS("platform:dwhdmi-imx");