1*4b6cb2b6SLucas Stach // SPDX-License-Identifier: GPL-2.0
2*4b6cb2b6SLucas Stach /* Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
3*4b6cb2b6SLucas Stach  *
4*4b6cb2b6SLucas Stach  * derived from imx-hdmi.c(renamed to bridge/dw_hdmi.c now)
5*4b6cb2b6SLucas Stach  */
6*4b6cb2b6SLucas Stach 
7*4b6cb2b6SLucas Stach #include <linux/component.h>
8*4b6cb2b6SLucas Stach #include <linux/mfd/syscon.h>
9*4b6cb2b6SLucas Stach #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
10*4b6cb2b6SLucas Stach #include <linux/module.h>
11*4b6cb2b6SLucas Stach #include <linux/platform_device.h>
12*4b6cb2b6SLucas Stach #include <linux/regmap.h>
13*4b6cb2b6SLucas Stach 
14*4b6cb2b6SLucas Stach #include <video/imx-ipu-v3.h>
15*4b6cb2b6SLucas Stach 
16*4b6cb2b6SLucas Stach #include <drm/bridge/dw_hdmi.h>
17*4b6cb2b6SLucas Stach #include <drm/drm_atomic_helper.h>
18*4b6cb2b6SLucas Stach #include <drm/drm_bridge.h>
19*4b6cb2b6SLucas Stach #include <drm/drm_edid.h>
20*4b6cb2b6SLucas Stach #include <drm/drm_encoder.h>
21*4b6cb2b6SLucas Stach #include <drm/drm_managed.h>
22*4b6cb2b6SLucas Stach #include <drm/drm_of.h>
23*4b6cb2b6SLucas Stach #include <drm/drm_simple_kms_helper.h>
24*4b6cb2b6SLucas Stach 
25*4b6cb2b6SLucas Stach #include "imx-drm.h"
26*4b6cb2b6SLucas Stach 
27*4b6cb2b6SLucas Stach struct imx_hdmi;
28*4b6cb2b6SLucas Stach 
29*4b6cb2b6SLucas Stach struct imx_hdmi_encoder {
30*4b6cb2b6SLucas Stach 	struct drm_encoder encoder;
31*4b6cb2b6SLucas Stach 	struct imx_hdmi *hdmi;
32*4b6cb2b6SLucas Stach };
33*4b6cb2b6SLucas Stach 
34*4b6cb2b6SLucas Stach struct imx_hdmi {
35*4b6cb2b6SLucas Stach 	struct device *dev;
36*4b6cb2b6SLucas Stach 	struct drm_bridge *bridge;
37*4b6cb2b6SLucas Stach 	struct dw_hdmi *hdmi;
38*4b6cb2b6SLucas Stach 	struct regmap *regmap;
39*4b6cb2b6SLucas Stach };
40*4b6cb2b6SLucas Stach 
enc_to_imx_hdmi(struct drm_encoder * e)41*4b6cb2b6SLucas Stach static inline struct imx_hdmi *enc_to_imx_hdmi(struct drm_encoder *e)
42*4b6cb2b6SLucas Stach {
43*4b6cb2b6SLucas Stach 	return container_of(e, struct imx_hdmi_encoder, encoder)->hdmi;
44*4b6cb2b6SLucas Stach }
45*4b6cb2b6SLucas Stach 
46*4b6cb2b6SLucas Stach static const struct dw_hdmi_mpll_config imx_mpll_cfg[] = {
47*4b6cb2b6SLucas Stach 	{
48*4b6cb2b6SLucas Stach 		45250000, {
49*4b6cb2b6SLucas Stach 			{ 0x01e0, 0x0000 },
50*4b6cb2b6SLucas Stach 			{ 0x21e1, 0x0000 },
51*4b6cb2b6SLucas Stach 			{ 0x41e2, 0x0000 }
52*4b6cb2b6SLucas Stach 		},
53*4b6cb2b6SLucas Stach 	}, {
54*4b6cb2b6SLucas Stach 		92500000, {
55*4b6cb2b6SLucas Stach 			{ 0x0140, 0x0005 },
56*4b6cb2b6SLucas Stach 			{ 0x2141, 0x0005 },
57*4b6cb2b6SLucas Stach 			{ 0x4142, 0x0005 },
58*4b6cb2b6SLucas Stach 	},
59*4b6cb2b6SLucas Stach 	}, {
60*4b6cb2b6SLucas Stach 		148500000, {
61*4b6cb2b6SLucas Stach 			{ 0x00a0, 0x000a },
62*4b6cb2b6SLucas Stach 			{ 0x20a1, 0x000a },
63*4b6cb2b6SLucas Stach 			{ 0x40a2, 0x000a },
64*4b6cb2b6SLucas Stach 		},
65*4b6cb2b6SLucas Stach 	}, {
66*4b6cb2b6SLucas Stach 		216000000, {
67*4b6cb2b6SLucas Stach 			{ 0x00a0, 0x000a },
68*4b6cb2b6SLucas Stach 			{ 0x2001, 0x000f },
69*4b6cb2b6SLucas Stach 			{ 0x4002, 0x000f },
70*4b6cb2b6SLucas Stach 		},
71*4b6cb2b6SLucas Stach 	}, {
72*4b6cb2b6SLucas Stach 		~0UL, {
73*4b6cb2b6SLucas Stach 			{ 0x0000, 0x0000 },
74*4b6cb2b6SLucas Stach 			{ 0x0000, 0x0000 },
75*4b6cb2b6SLucas Stach 			{ 0x0000, 0x0000 },
76*4b6cb2b6SLucas Stach 		},
77*4b6cb2b6SLucas Stach 	}
78*4b6cb2b6SLucas Stach };
79*4b6cb2b6SLucas Stach 
80*4b6cb2b6SLucas Stach static const struct dw_hdmi_curr_ctrl imx_cur_ctr[] = {
81*4b6cb2b6SLucas Stach 	/*      pixelclk     bpp8    bpp10   bpp12 */
82*4b6cb2b6SLucas Stach 	{
83*4b6cb2b6SLucas Stach 		54000000, { 0x091c, 0x091c, 0x06dc },
84*4b6cb2b6SLucas Stach 	}, {
85*4b6cb2b6SLucas Stach 		58400000, { 0x091c, 0x06dc, 0x06dc },
86*4b6cb2b6SLucas Stach 	}, {
87*4b6cb2b6SLucas Stach 		72000000, { 0x06dc, 0x06dc, 0x091c },
88*4b6cb2b6SLucas Stach 	}, {
89*4b6cb2b6SLucas Stach 		74250000, { 0x06dc, 0x0b5c, 0x091c },
90*4b6cb2b6SLucas Stach 	}, {
91*4b6cb2b6SLucas Stach 		118800000, { 0x091c, 0x091c, 0x06dc },
92*4b6cb2b6SLucas Stach 	}, {
93*4b6cb2b6SLucas Stach 		216000000, { 0x06dc, 0x0b5c, 0x091c },
94*4b6cb2b6SLucas Stach 	}, {
95*4b6cb2b6SLucas Stach 		~0UL, { 0x0000, 0x0000, 0x0000 },
96*4b6cb2b6SLucas Stach 	},
97*4b6cb2b6SLucas Stach };
98*4b6cb2b6SLucas Stach 
99*4b6cb2b6SLucas Stach /*
100*4b6cb2b6SLucas Stach  * Resistance term 133Ohm Cfg
101*4b6cb2b6SLucas Stach  * PREEMP config 0.00
102*4b6cb2b6SLucas Stach  * TX/CK level 10
103*4b6cb2b6SLucas Stach  */
104*4b6cb2b6SLucas Stach static const struct dw_hdmi_phy_config imx_phy_config[] = {
105*4b6cb2b6SLucas Stach 	/*pixelclk   symbol   term   vlev */
106*4b6cb2b6SLucas Stach 	{ 216000000, 0x800d, 0x0005, 0x01ad},
107*4b6cb2b6SLucas Stach 	{ ~0UL,      0x0000, 0x0000, 0x0000}
108*4b6cb2b6SLucas Stach };
109*4b6cb2b6SLucas Stach 
dw_hdmi_imx_encoder_enable(struct drm_encoder * encoder)110*4b6cb2b6SLucas Stach static void dw_hdmi_imx_encoder_enable(struct drm_encoder *encoder)
111*4b6cb2b6SLucas Stach {
112*4b6cb2b6SLucas Stach 	struct imx_hdmi *hdmi = enc_to_imx_hdmi(encoder);
113*4b6cb2b6SLucas Stach 	int mux = drm_of_encoder_active_port_id(hdmi->dev->of_node, encoder);
114*4b6cb2b6SLucas Stach 
115*4b6cb2b6SLucas Stach 	regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
116*4b6cb2b6SLucas Stach 			   IMX6Q_GPR3_HDMI_MUX_CTL_MASK,
117*4b6cb2b6SLucas Stach 			   mux << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
118*4b6cb2b6SLucas Stach }
119*4b6cb2b6SLucas Stach 
dw_hdmi_imx_atomic_check(struct drm_encoder * encoder,struct drm_crtc_state * crtc_state,struct drm_connector_state * conn_state)120*4b6cb2b6SLucas Stach static int dw_hdmi_imx_atomic_check(struct drm_encoder *encoder,
121*4b6cb2b6SLucas Stach 				    struct drm_crtc_state *crtc_state,
122*4b6cb2b6SLucas Stach 				    struct drm_connector_state *conn_state)
123*4b6cb2b6SLucas Stach {
124*4b6cb2b6SLucas Stach 	struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
125*4b6cb2b6SLucas Stach 
126*4b6cb2b6SLucas Stach 	imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
127*4b6cb2b6SLucas Stach 	imx_crtc_state->di_hsync_pin = 2;
128*4b6cb2b6SLucas Stach 	imx_crtc_state->di_vsync_pin = 3;
129*4b6cb2b6SLucas Stach 
130*4b6cb2b6SLucas Stach 	return 0;
131*4b6cb2b6SLucas Stach }
132*4b6cb2b6SLucas Stach 
133*4b6cb2b6SLucas Stach static const struct drm_encoder_helper_funcs dw_hdmi_imx_encoder_helper_funcs = {
134*4b6cb2b6SLucas Stach 	.enable     = dw_hdmi_imx_encoder_enable,
135*4b6cb2b6SLucas Stach 	.atomic_check = dw_hdmi_imx_atomic_check,
136*4b6cb2b6SLucas Stach };
137*4b6cb2b6SLucas Stach 
138*4b6cb2b6SLucas Stach static enum drm_mode_status
imx6q_hdmi_mode_valid(struct dw_hdmi * hdmi,void * data,const struct drm_display_info * info,const struct drm_display_mode * mode)139*4b6cb2b6SLucas Stach imx6q_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,
140*4b6cb2b6SLucas Stach 		      const struct drm_display_info *info,
141*4b6cb2b6SLucas Stach 		      const struct drm_display_mode *mode)
142*4b6cb2b6SLucas Stach {
143*4b6cb2b6SLucas Stach 	if (mode->clock < 13500)
144*4b6cb2b6SLucas Stach 		return MODE_CLOCK_LOW;
145*4b6cb2b6SLucas Stach 	/* FIXME: Hardware is capable of 266MHz, but setup data is missing. */
146*4b6cb2b6SLucas Stach 	if (mode->clock > 216000)
147*4b6cb2b6SLucas Stach 		return MODE_CLOCK_HIGH;
148*4b6cb2b6SLucas Stach 
149*4b6cb2b6SLucas Stach 	return MODE_OK;
150*4b6cb2b6SLucas Stach }
151*4b6cb2b6SLucas Stach 
152*4b6cb2b6SLucas Stach static enum drm_mode_status
imx6dl_hdmi_mode_valid(struct dw_hdmi * hdmi,void * data,const struct drm_display_info * info,const struct drm_display_mode * mode)153*4b6cb2b6SLucas Stach imx6dl_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,
154*4b6cb2b6SLucas Stach 		       const struct drm_display_info *info,
155*4b6cb2b6SLucas Stach 		       const struct drm_display_mode *mode)
156*4b6cb2b6SLucas Stach {
157*4b6cb2b6SLucas Stach 	if (mode->clock < 13500)
158*4b6cb2b6SLucas Stach 		return MODE_CLOCK_LOW;
159*4b6cb2b6SLucas Stach 	/* FIXME: Hardware is capable of 270MHz, but setup data is missing. */
160*4b6cb2b6SLucas Stach 	if (mode->clock > 216000)
161*4b6cb2b6SLucas Stach 		return MODE_CLOCK_HIGH;
162*4b6cb2b6SLucas Stach 
163*4b6cb2b6SLucas Stach 	return MODE_OK;
164*4b6cb2b6SLucas Stach }
165*4b6cb2b6SLucas Stach 
166*4b6cb2b6SLucas Stach static struct dw_hdmi_plat_data imx6q_hdmi_drv_data = {
167*4b6cb2b6SLucas Stach 	.mpll_cfg   = imx_mpll_cfg,
168*4b6cb2b6SLucas Stach 	.cur_ctr    = imx_cur_ctr,
169*4b6cb2b6SLucas Stach 	.phy_config = imx_phy_config,
170*4b6cb2b6SLucas Stach 	.mode_valid = imx6q_hdmi_mode_valid,
171*4b6cb2b6SLucas Stach };
172*4b6cb2b6SLucas Stach 
173*4b6cb2b6SLucas Stach static struct dw_hdmi_plat_data imx6dl_hdmi_drv_data = {
174*4b6cb2b6SLucas Stach 	.mpll_cfg = imx_mpll_cfg,
175*4b6cb2b6SLucas Stach 	.cur_ctr  = imx_cur_ctr,
176*4b6cb2b6SLucas Stach 	.phy_config = imx_phy_config,
177*4b6cb2b6SLucas Stach 	.mode_valid = imx6dl_hdmi_mode_valid,
178*4b6cb2b6SLucas Stach };
179*4b6cb2b6SLucas Stach 
180*4b6cb2b6SLucas Stach static const struct of_device_id dw_hdmi_imx_dt_ids[] = {
181*4b6cb2b6SLucas Stach 	{ .compatible = "fsl,imx6q-hdmi",
182*4b6cb2b6SLucas Stach 	  .data = &imx6q_hdmi_drv_data
183*4b6cb2b6SLucas Stach 	}, {
184*4b6cb2b6SLucas Stach 	  .compatible = "fsl,imx6dl-hdmi",
185*4b6cb2b6SLucas Stach 	  .data = &imx6dl_hdmi_drv_data
186*4b6cb2b6SLucas Stach 	},
187*4b6cb2b6SLucas Stach 	{},
188*4b6cb2b6SLucas Stach };
189*4b6cb2b6SLucas Stach MODULE_DEVICE_TABLE(of, dw_hdmi_imx_dt_ids);
190*4b6cb2b6SLucas Stach 
dw_hdmi_imx_bind(struct device * dev,struct device * master,void * data)191*4b6cb2b6SLucas Stach static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
192*4b6cb2b6SLucas Stach 			    void *data)
193*4b6cb2b6SLucas Stach {
194*4b6cb2b6SLucas Stach 	struct drm_device *drm = data;
195*4b6cb2b6SLucas Stach 	struct imx_hdmi_encoder *hdmi_encoder;
196*4b6cb2b6SLucas Stach 	struct drm_encoder *encoder;
197*4b6cb2b6SLucas Stach 	int ret;
198*4b6cb2b6SLucas Stach 
199*4b6cb2b6SLucas Stach 	hdmi_encoder = drmm_simple_encoder_alloc(drm, struct imx_hdmi_encoder,
200*4b6cb2b6SLucas Stach 						 encoder, DRM_MODE_ENCODER_TMDS);
201*4b6cb2b6SLucas Stach 	if (IS_ERR(hdmi_encoder))
202*4b6cb2b6SLucas Stach 		return PTR_ERR(hdmi_encoder);
203*4b6cb2b6SLucas Stach 
204*4b6cb2b6SLucas Stach 	hdmi_encoder->hdmi = dev_get_drvdata(dev);
205*4b6cb2b6SLucas Stach 	encoder = &hdmi_encoder->encoder;
206*4b6cb2b6SLucas Stach 
207*4b6cb2b6SLucas Stach 	ret = imx_drm_encoder_parse_of(drm, encoder, dev->of_node);
208*4b6cb2b6SLucas Stach 	if (ret)
209*4b6cb2b6SLucas Stach 		return ret;
210*4b6cb2b6SLucas Stach 
211*4b6cb2b6SLucas Stach 	drm_encoder_helper_add(encoder, &dw_hdmi_imx_encoder_helper_funcs);
212*4b6cb2b6SLucas Stach 
213*4b6cb2b6SLucas Stach 	return drm_bridge_attach(encoder, hdmi_encoder->hdmi->bridge, NULL, 0);
214*4b6cb2b6SLucas Stach }
215*4b6cb2b6SLucas Stach 
216*4b6cb2b6SLucas Stach static const struct component_ops dw_hdmi_imx_ops = {
217*4b6cb2b6SLucas Stach 	.bind	= dw_hdmi_imx_bind,
218*4b6cb2b6SLucas Stach };
219*4b6cb2b6SLucas Stach 
dw_hdmi_imx_probe(struct platform_device * pdev)220*4b6cb2b6SLucas Stach static int dw_hdmi_imx_probe(struct platform_device *pdev)
221*4b6cb2b6SLucas Stach {
222*4b6cb2b6SLucas Stach 	struct device_node *np = pdev->dev.of_node;
223*4b6cb2b6SLucas Stach 	const struct of_device_id *match = of_match_node(dw_hdmi_imx_dt_ids, np);
224*4b6cb2b6SLucas Stach 	struct imx_hdmi *hdmi;
225*4b6cb2b6SLucas Stach 	int ret;
226*4b6cb2b6SLucas Stach 
227*4b6cb2b6SLucas Stach 	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
228*4b6cb2b6SLucas Stach 	if (!hdmi)
229*4b6cb2b6SLucas Stach 		return -ENOMEM;
230*4b6cb2b6SLucas Stach 
231*4b6cb2b6SLucas Stach 	platform_set_drvdata(pdev, hdmi);
232*4b6cb2b6SLucas Stach 	hdmi->dev = &pdev->dev;
233*4b6cb2b6SLucas Stach 
234*4b6cb2b6SLucas Stach 	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
235*4b6cb2b6SLucas Stach 	if (IS_ERR(hdmi->regmap)) {
236*4b6cb2b6SLucas Stach 		dev_err(hdmi->dev, "Unable to get gpr\n");
237*4b6cb2b6SLucas Stach 		return PTR_ERR(hdmi->regmap);
238*4b6cb2b6SLucas Stach 	}
239*4b6cb2b6SLucas Stach 
240*4b6cb2b6SLucas Stach 	hdmi->hdmi = dw_hdmi_probe(pdev, match->data);
241*4b6cb2b6SLucas Stach 	if (IS_ERR(hdmi->hdmi))
242*4b6cb2b6SLucas Stach 		return PTR_ERR(hdmi->hdmi);
243*4b6cb2b6SLucas Stach 
244*4b6cb2b6SLucas Stach 	hdmi->bridge = of_drm_find_bridge(np);
245*4b6cb2b6SLucas Stach 	if (!hdmi->bridge) {
246*4b6cb2b6SLucas Stach 		dev_err(hdmi->dev, "Unable to find bridge\n");
247*4b6cb2b6SLucas Stach 		dw_hdmi_remove(hdmi->hdmi);
248*4b6cb2b6SLucas Stach 		return -ENODEV;
249*4b6cb2b6SLucas Stach 	}
250*4b6cb2b6SLucas Stach 
251*4b6cb2b6SLucas Stach 	ret = component_add(&pdev->dev, &dw_hdmi_imx_ops);
252*4b6cb2b6SLucas Stach 	if (ret)
253*4b6cb2b6SLucas Stach 		dw_hdmi_remove(hdmi->hdmi);
254*4b6cb2b6SLucas Stach 
255*4b6cb2b6SLucas Stach 	return ret;
256*4b6cb2b6SLucas Stach }
257*4b6cb2b6SLucas Stach 
dw_hdmi_imx_remove(struct platform_device * pdev)258*4b6cb2b6SLucas Stach static int dw_hdmi_imx_remove(struct platform_device *pdev)
259*4b6cb2b6SLucas Stach {
260*4b6cb2b6SLucas Stach 	struct imx_hdmi *hdmi = platform_get_drvdata(pdev);
261*4b6cb2b6SLucas Stach 
262*4b6cb2b6SLucas Stach 	component_del(&pdev->dev, &dw_hdmi_imx_ops);
263*4b6cb2b6SLucas Stach 	dw_hdmi_remove(hdmi->hdmi);
264*4b6cb2b6SLucas Stach 
265*4b6cb2b6SLucas Stach 	return 0;
266*4b6cb2b6SLucas Stach }
267*4b6cb2b6SLucas Stach 
268*4b6cb2b6SLucas Stach static struct platform_driver dw_hdmi_imx_platform_driver = {
269*4b6cb2b6SLucas Stach 	.probe  = dw_hdmi_imx_probe,
270*4b6cb2b6SLucas Stach 	.remove = dw_hdmi_imx_remove,
271*4b6cb2b6SLucas Stach 	.driver = {
272*4b6cb2b6SLucas Stach 		.name = "dwhdmi-imx",
273*4b6cb2b6SLucas Stach 		.of_match_table = dw_hdmi_imx_dt_ids,
274*4b6cb2b6SLucas Stach 	},
275*4b6cb2b6SLucas Stach };
276*4b6cb2b6SLucas Stach 
277*4b6cb2b6SLucas Stach module_platform_driver(dw_hdmi_imx_platform_driver);
278*4b6cb2b6SLucas Stach 
279*4b6cb2b6SLucas Stach MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
280*4b6cb2b6SLucas Stach MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
281*4b6cb2b6SLucas Stach MODULE_DESCRIPTION("IMX6 Specific DW-HDMI Driver Extension");
282*4b6cb2b6SLucas Stach MODULE_LICENSE("GPL");
283*4b6cb2b6SLucas Stach MODULE_ALIAS("platform:dwhdmi-imx");
284