xref: /rk3399_rockchip-uboot/drivers/video/drm/inno_hdmi.c (revision 9c170041dcf97ed3fe5bbd76e70cda2ed3fcbd92)
1f097e410SAlgea Cao // SPDX-License-Identifier: GPL-2.0
2f097e410SAlgea Cao /*
3f097e410SAlgea Cao  * (C) Copyright 2020 Rockchip Electronics Co., Ltd
4f097e410SAlgea Cao  */
5f097e410SAlgea Cao #include <common.h>
6f097e410SAlgea Cao #include <clk.h>
7f097e410SAlgea Cao #include <syscon.h>
8f097e410SAlgea Cao #include <asm/io.h>
9f097e410SAlgea Cao #include <asm/arch-rockchip/clock.h>
100594ce39SZhang Yubing #include <dm/of_access.h>
11f097e410SAlgea Cao #include <dm/device.h>
12f097e410SAlgea Cao #include <dm/read.h>
13f097e410SAlgea Cao #include <linux/hdmi.h>
14f097e410SAlgea Cao #include <linux/media-bus-format.h>
15f097e410SAlgea Cao 
16f097e410SAlgea Cao #include "inno_hdmi.h"
17f097e410SAlgea Cao #include "rockchip_connector.h"
18f097e410SAlgea Cao #include "rockchip_crtc.h"
19f097e410SAlgea Cao #include "rockchip_display.h"
20f097e410SAlgea Cao 
21f097e410SAlgea Cao struct inno_hdmi_i2c {
22f097e410SAlgea Cao 	u8			slave_reg;
23f097e410SAlgea Cao 	u8			ddc_addr;
24f097e410SAlgea Cao 	u8			segment_addr;
25f097e410SAlgea Cao 	bool			is_regaddr;
26f097e410SAlgea Cao 	bool			is_segment;
27f097e410SAlgea Cao 
28f097e410SAlgea Cao 	unsigned int		scl_high_ns;
29f097e410SAlgea Cao 	unsigned int		scl_low_ns;
30f097e410SAlgea Cao };
31f097e410SAlgea Cao 
32f097e410SAlgea Cao enum inno_hdmi_dev_type {
33f097e410SAlgea Cao 	RK3036_HDMI,
34f097e410SAlgea Cao 	RK3128_HDMI,
35f097e410SAlgea Cao };
36f097e410SAlgea Cao 
37f097e410SAlgea Cao enum {
38f097e410SAlgea Cao 	CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
39f097e410SAlgea Cao 	CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
40f097e410SAlgea Cao 	CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
41f097e410SAlgea Cao 	CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
42f097e410SAlgea Cao 	CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
43f097e410SAlgea Cao 	CSC_RGB_0_255_TO_RGB_16_235_8BIT,
44f097e410SAlgea Cao };
45f097e410SAlgea Cao 
46f097e410SAlgea Cao static const char coeff_csc[][24] = {
47f097e410SAlgea Cao 	/*
48f097e410SAlgea Cao 	 * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
49f097e410SAlgea Cao 	 *   R = 1.164*Y + 1.596*V - 204
50f097e410SAlgea Cao 	 *   G = 1.164*Y - 0.391*U - 0.813*V + 154
51f097e410SAlgea Cao 	 *   B = 1.164*Y + 2.018*U - 258
52f097e410SAlgea Cao 	 */
53f097e410SAlgea Cao 	{
54f097e410SAlgea Cao 		0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
55f097e410SAlgea Cao 		0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
56f097e410SAlgea Cao 		0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
57f097e410SAlgea Cao 	},
58f097e410SAlgea Cao 	/*
59f097e410SAlgea Cao 	 * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
60f097e410SAlgea Cao 	 *   R = Y + 1.402*V - 248
61f097e410SAlgea Cao 	 *   G = Y - 0.344*U - 0.714*V + 135
62f097e410SAlgea Cao 	 *   B = Y + 1.772*U - 227
63f097e410SAlgea Cao 	 */
64f097e410SAlgea Cao 	{
65f097e410SAlgea Cao 		0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
66f097e410SAlgea Cao 		0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
67f097e410SAlgea Cao 		0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
68f097e410SAlgea Cao 	},
69f097e410SAlgea Cao 	/*
70f097e410SAlgea Cao 	 * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
71f097e410SAlgea Cao 	 *   R = 1.164*Y + 1.793*V - 248
72f097e410SAlgea Cao 	 *   G = 1.164*Y - 0.213*U - 0.534*V + 77
73f097e410SAlgea Cao 	 *   B = 1.164*Y + 2.115*U - 289
74f097e410SAlgea Cao 	 */
75f097e410SAlgea Cao 	{
76f097e410SAlgea Cao 		0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
77f097e410SAlgea Cao 		0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
78f097e410SAlgea Cao 		0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
79f097e410SAlgea Cao 	},
80f097e410SAlgea Cao 
81f097e410SAlgea Cao 	/*
82f097e410SAlgea Cao 	 * RGB2YUV:601 SD mode:
83f097e410SAlgea Cao 	 *   Cb = -0.291G - 0.148R + 0.439B + 128
84f097e410SAlgea Cao 	 *   Y  = 0.504G  + 0.257R + 0.098B + 16
85f097e410SAlgea Cao 	 *   Cr = -0.368G + 0.439R - 0.071B + 128
86f097e410SAlgea Cao 	 */
87f097e410SAlgea Cao 	{
88f097e410SAlgea Cao 		0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
89f097e410SAlgea Cao 		0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
90f097e410SAlgea Cao 		0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
91f097e410SAlgea Cao 	},
92f097e410SAlgea Cao 	/*
93f097e410SAlgea Cao 	 * RGB2YUV:709 HD mode:
94f097e410SAlgea Cao 	 *   Cb = - 0.338G - 0.101R + 0.439B + 128
95f097e410SAlgea Cao 	 *   Y  = 0.614G   + 0.183R + 0.062B + 16
96f097e410SAlgea Cao 	 *   Cr = - 0.399G + 0.439R - 0.040B + 128
97f097e410SAlgea Cao 	 */
98f097e410SAlgea Cao 	{
99f097e410SAlgea Cao 		0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
100f097e410SAlgea Cao 		0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
101f097e410SAlgea Cao 		0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
102f097e410SAlgea Cao 	},
103f097e410SAlgea Cao 	/*
104f097e410SAlgea Cao 	 * RGB[0:255]2RGB[16:235]:
105f097e410SAlgea Cao 	 *   R' = R x (235-16)/255 + 16;
106f097e410SAlgea Cao 	 *   G' = G x (235-16)/255 + 16;
107f097e410SAlgea Cao 	 *   B' = B x (235-16)/255 + 16;
108f097e410SAlgea Cao 	 */
109f097e410SAlgea Cao 	{
110f097e410SAlgea Cao 		0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
111f097e410SAlgea Cao 		0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
112f097e410SAlgea Cao 		0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
113f097e410SAlgea Cao 	},
114f097e410SAlgea Cao };
115f097e410SAlgea Cao 
116f097e410SAlgea Cao struct hdmi_data_info {
117f097e410SAlgea Cao 	int vic;
118f097e410SAlgea Cao 	bool sink_is_hdmi;
119f097e410SAlgea Cao 	bool sink_has_audio;
120f097e410SAlgea Cao 	unsigned int enc_in_format;
121f097e410SAlgea Cao 	unsigned int enc_out_format;
122f097e410SAlgea Cao 	unsigned int colorimetry;
123f097e410SAlgea Cao };
124f097e410SAlgea Cao 
125f097e410SAlgea Cao struct inno_hdmi_phy_config {
126f097e410SAlgea Cao 	unsigned long mpixelclock;
127f097e410SAlgea Cao 	u8 pre_emphasis;	/* pre-emphasis value */
128f097e410SAlgea Cao 	u8 vlev_ctr;		/* voltage level control */
129f097e410SAlgea Cao };
130f097e410SAlgea Cao 
131f097e410SAlgea Cao struct inno_hdmi_plat_data {
132f097e410SAlgea Cao 	enum inno_hdmi_dev_type dev_type;
133f097e410SAlgea Cao 	struct inno_hdmi_phy_config *phy_config;
134f097e410SAlgea Cao };
135f097e410SAlgea Cao 
136f097e410SAlgea Cao struct inno_hdmi {
137f097e410SAlgea Cao 	struct device *dev;
138f097e410SAlgea Cao 	struct drm_device *drm_dev;
139f097e410SAlgea Cao 	struct ddc_adapter adap;
140f097e410SAlgea Cao 	struct hdmi_edid_data edid_data;
141f097e410SAlgea Cao 	struct hdmi_data_info	hdmi_data;
142f097e410SAlgea Cao 
143f097e410SAlgea Cao 	struct clk pclk;
144f097e410SAlgea Cao 	int vic;
145f097e410SAlgea Cao 	void *regs;
146f097e410SAlgea Cao 	void *grf;
147f097e410SAlgea Cao 
148f097e410SAlgea Cao 	struct inno_hdmi_i2c *i2c;
149f097e410SAlgea Cao 
150f097e410SAlgea Cao 	unsigned int tmds_rate;
151f097e410SAlgea Cao 	const struct inno_hdmi_plat_data *plat_data;
152f097e410SAlgea Cao 
153f097e410SAlgea Cao 	unsigned int sample_rate;
154f097e410SAlgea Cao 	unsigned int audio_cts;
155f097e410SAlgea Cao 	unsigned int audio_n;
156f097e410SAlgea Cao 	bool audio_enable;
157f097e410SAlgea Cao 
158f097e410SAlgea Cao 	struct drm_display_mode previous_mode;
159f097e410SAlgea Cao };
160f097e410SAlgea Cao 
161f097e410SAlgea Cao static struct inno_hdmi_phy_config rk3036_hdmi_phy_config[] = {
162f097e410SAlgea Cao 	/* pixelclk pre-emp vlev */
163f097e410SAlgea Cao 	{ 74250000,  0x3f, 0xbb },
164f097e410SAlgea Cao 	{ 165000000, 0x6f, 0xbb },
165f097e410SAlgea Cao 	{ ~0UL,	     0x00, 0x00 }
166f097e410SAlgea Cao };
167f097e410SAlgea Cao 
168f097e410SAlgea Cao static struct inno_hdmi_phy_config rk3128_hdmi_phy_config[] = {
169f097e410SAlgea Cao 	/* pixelclk pre-emp vlev */
170f097e410SAlgea Cao 	{ 74250000,  0x3f, 0xaa },
171f097e410SAlgea Cao 	{ 165000000, 0x5f, 0xaa },
172f097e410SAlgea Cao 	{ ~0UL,	     0x00, 0x00 }
173f097e410SAlgea Cao };
174f097e410SAlgea Cao 
hdmi_writeb(struct inno_hdmi * hdmi,u16 offset,u32 val)175f097e410SAlgea Cao static void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val)
176f097e410SAlgea Cao {
177f097e410SAlgea Cao 	writel(val, hdmi->regs + (offset << 2));
178f097e410SAlgea Cao }
179f097e410SAlgea Cao 
hdmi_readb(struct inno_hdmi * hdmi,u16 offset)180f097e410SAlgea Cao static u32 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
181f097e410SAlgea Cao {
182f097e410SAlgea Cao 	return readl(hdmi->regs + (offset << 2));
183f097e410SAlgea Cao }
184f097e410SAlgea Cao 
hdmi_modb(struct inno_hdmi * hdmi,u16 offset,u32 msk,u32 val)185f097e410SAlgea Cao static void hdmi_modb(struct inno_hdmi *hdmi, u16 offset, u32 msk, u32 val)
186f097e410SAlgea Cao {
187f097e410SAlgea Cao 	u32 temp = hdmi_readb(hdmi, offset) & ~msk;
188f097e410SAlgea Cao 
189f097e410SAlgea Cao 	temp |= val & msk;
190f097e410SAlgea Cao 	hdmi_writeb(hdmi, offset, temp);
191f097e410SAlgea Cao }
192f097e410SAlgea Cao 
inno_hdmi_sys_power(struct inno_hdmi * hdmi,bool enable)193f097e410SAlgea Cao static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
194f097e410SAlgea Cao {
195f097e410SAlgea Cao 	if (enable)
196f097e410SAlgea Cao 		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
197f097e410SAlgea Cao 	else
198f097e410SAlgea Cao 		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
199f097e410SAlgea Cao }
200f097e410SAlgea Cao 
inno_hdmi_set_pwr_mode(struct inno_hdmi * hdmi,int mode)201f097e410SAlgea Cao static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
202f097e410SAlgea Cao {
203f097e410SAlgea Cao 	const struct inno_hdmi_phy_config *phy_config =
204f097e410SAlgea Cao 						hdmi->plat_data->phy_config;
205f097e410SAlgea Cao 
206f097e410SAlgea Cao 	switch (mode) {
207f097e410SAlgea Cao 	case NORMAL:
208f097e410SAlgea Cao 		inno_hdmi_sys_power(hdmi, false);
209f097e410SAlgea Cao 		for (; phy_config->mpixelclock != ~0UL; phy_config++)
210f097e410SAlgea Cao 			if (hdmi->tmds_rate <= phy_config->mpixelclock)
211f097e410SAlgea Cao 				break;
212f097e410SAlgea Cao 		if (!phy_config->mpixelclock)
213f097e410SAlgea Cao 			return;
214f097e410SAlgea Cao 		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS,
215f097e410SAlgea Cao 			    phy_config->pre_emphasis);
216f097e410SAlgea Cao 		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, phy_config->vlev_ctr);
217f097e410SAlgea Cao 
218f097e410SAlgea Cao 		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
219f097e410SAlgea Cao 		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
220f097e410SAlgea Cao 		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
221f097e410SAlgea Cao 
222f097e410SAlgea Cao 		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
223f097e410SAlgea Cao 		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
224f097e410SAlgea Cao 		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
225f097e410SAlgea Cao 		inno_hdmi_sys_power(hdmi, true);
226f097e410SAlgea Cao 
227f097e410SAlgea Cao 		break;
228f097e410SAlgea Cao 
229f097e410SAlgea Cao 	case LOWER_PWR:
230f097e410SAlgea Cao 		inno_hdmi_sys_power(hdmi, false);
231f097e410SAlgea Cao 		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
232f097e410SAlgea Cao 		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
233f097e410SAlgea Cao 		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
234f097e410SAlgea Cao 		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
235f097e410SAlgea Cao 
236f097e410SAlgea Cao 		break;
237f097e410SAlgea Cao 
238f097e410SAlgea Cao 	default:
239f097e410SAlgea Cao 		dev_err(hdmi->dev, "Unknown power mode %d\n", mode);
240f097e410SAlgea Cao 	}
241f097e410SAlgea Cao }
242f097e410SAlgea Cao 
inno_hdmi_i2c_init(struct inno_hdmi * hdmi)243f097e410SAlgea Cao static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
244f097e410SAlgea Cao {
245f097e410SAlgea Cao 	int ddc_bus_freq;
246f097e410SAlgea Cao 
247f097e410SAlgea Cao 	ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
248f097e410SAlgea Cao 	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
249f097e410SAlgea Cao 	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
250f097e410SAlgea Cao 
251f097e410SAlgea Cao 	/* Clear the EDID interrupt flag and mute the interrupt */
252f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
253f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
254f097e410SAlgea Cao }
255f097e410SAlgea Cao 
inno_hdmi_reset(struct inno_hdmi * hdmi)256f097e410SAlgea Cao static void inno_hdmi_reset(struct inno_hdmi *hdmi)
257f097e410SAlgea Cao {
258f097e410SAlgea Cao 	u32 val;
259f097e410SAlgea Cao 	u32 msk;
260f097e410SAlgea Cao 
261f097e410SAlgea Cao 	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
262f097e410SAlgea Cao 	udelay(100);
263f097e410SAlgea Cao 
264f097e410SAlgea Cao 	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
265f097e410SAlgea Cao 	udelay(100);
266f097e410SAlgea Cao 
267f097e410SAlgea Cao 	msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
268f097e410SAlgea Cao 	val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH;
269f097e410SAlgea Cao 
270f097e410SAlgea Cao 	hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
271f097e410SAlgea Cao 
272f097e410SAlgea Cao 	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
273f097e410SAlgea Cao }
274f097e410SAlgea Cao 
inno_hdmi_upload_frame(struct inno_hdmi * hdmi,int setup_rc,union hdmi_infoframe * frame,u32 frame_index,u32 mask,u32 disable,u32 enable)275f097e410SAlgea Cao static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
276f097e410SAlgea Cao 				  union hdmi_infoframe *frame, u32 frame_index,
277f097e410SAlgea Cao 				  u32 mask, u32 disable, u32 enable)
278f097e410SAlgea Cao {
279f097e410SAlgea Cao 	if (mask)
280f097e410SAlgea Cao 		hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);
281f097e410SAlgea Cao 
282f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);
283f097e410SAlgea Cao 
284f097e410SAlgea Cao 	if (setup_rc >= 0) {
285f097e410SAlgea Cao 		u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
286f097e410SAlgea Cao 		ssize_t rc, i;
287f097e410SAlgea Cao 
288f097e410SAlgea Cao 		rc = hdmi_infoframe_pack(frame, packed_frame,
289f097e410SAlgea Cao 					 sizeof(packed_frame));
290f097e410SAlgea Cao 		if (rc < 0)
291f097e410SAlgea Cao 			return rc;
292f097e410SAlgea Cao 
293f097e410SAlgea Cao 		for (i = 0; i < rc; i++)
294f097e410SAlgea Cao 			hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
295f097e410SAlgea Cao 				    packed_frame[i]);
296f097e410SAlgea Cao 
297f097e410SAlgea Cao 		if (mask)
298f097e410SAlgea Cao 			hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable);
299f097e410SAlgea Cao 	}
300f097e410SAlgea Cao 
301f097e410SAlgea Cao 	return setup_rc;
302f097e410SAlgea Cao }
303f097e410SAlgea Cao 
inno_hdmi_config_video_vsi(struct inno_hdmi * hdmi,struct drm_display_mode * mode)304f097e410SAlgea Cao static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi,
305f097e410SAlgea Cao 				      struct drm_display_mode *mode)
306f097e410SAlgea Cao {
307f097e410SAlgea Cao 	union hdmi_infoframe frame;
308f097e410SAlgea Cao 	int rc;
309f097e410SAlgea Cao 
310f097e410SAlgea Cao 	rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
311f097e410SAlgea Cao 							 mode);
312f097e410SAlgea Cao 
313f097e410SAlgea Cao 	return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_VSI,
314f097e410SAlgea Cao 		m_PACKET_VSI_EN, v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1));
315f097e410SAlgea Cao }
316f097e410SAlgea Cao 
inno_hdmi_config_video_avi(struct inno_hdmi * hdmi,struct drm_display_mode * mode)317f097e410SAlgea Cao static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
318f097e410SAlgea Cao 				      struct drm_display_mode *mode)
319f097e410SAlgea Cao {
320f097e410SAlgea Cao 	union hdmi_infoframe frame;
321f097e410SAlgea Cao 	int rc;
322f097e410SAlgea Cao 
323f097e410SAlgea Cao 	rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode, false);
324f097e410SAlgea Cao 
325f097e410SAlgea Cao 	if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
326f097e410SAlgea Cao 		frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
327f097e410SAlgea Cao 	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
328f097e410SAlgea Cao 		frame.avi.colorspace = HDMI_COLORSPACE_YUV422;
329f097e410SAlgea Cao 	else
330f097e410SAlgea Cao 		frame.avi.colorspace = HDMI_COLORSPACE_RGB;
331f097e410SAlgea Cao 
332f097e410SAlgea Cao 	if (frame.avi.colorspace != HDMI_COLORSPACE_RGB)
333f097e410SAlgea Cao 		frame.avi.colorimetry = hdmi->hdmi_data.colorimetry;
334f097e410SAlgea Cao 
335f097e410SAlgea Cao 	frame.avi.scan_mode = HDMI_SCAN_MODE_NONE;
336f097e410SAlgea Cao 
337f097e410SAlgea Cao 	return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI, 0, 0, 0);
338f097e410SAlgea Cao }
339f097e410SAlgea Cao 
inno_hdmi_config_video_csc(struct inno_hdmi * hdmi)340f097e410SAlgea Cao static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
341f097e410SAlgea Cao {
342f097e410SAlgea Cao 	struct hdmi_data_info *data = &hdmi->hdmi_data;
343f097e410SAlgea Cao 	int c0_c2_change = 0;
344f097e410SAlgea Cao 	int csc_enable = 0;
345f097e410SAlgea Cao 	int csc_mode = 0;
346f097e410SAlgea Cao 	int auto_csc = 0;
347f097e410SAlgea Cao 	int value;
348f097e410SAlgea Cao 	int i;
349f097e410SAlgea Cao 
350f097e410SAlgea Cao 	/* Input video mode is SDR RGB24bit, data enable signal from external */
351f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
352f097e410SAlgea Cao 		    v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444));
353f097e410SAlgea Cao 
354f097e410SAlgea Cao 	/* Input color hardcode to RGB, and output color hardcode to RGB888 */
355f097e410SAlgea Cao 	value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
356f097e410SAlgea Cao 		v_VIDEO_OUTPUT_COLOR(0) |
357f097e410SAlgea Cao 		v_VIDEO_INPUT_CSP(0);
358f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
359f097e410SAlgea Cao 
360f097e410SAlgea Cao 	if (data->enc_in_format == data->enc_out_format) {
361f097e410SAlgea Cao 		if (data->enc_in_format == HDMI_COLORSPACE_RGB ||
362f097e410SAlgea Cao 		    data->enc_in_format >= HDMI_COLORSPACE_YUV444) {
363f097e410SAlgea Cao 			value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
364f097e410SAlgea Cao 			hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
365f097e410SAlgea Cao 
366f097e410SAlgea Cao 			hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
367f097e410SAlgea Cao 				  m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
368f097e410SAlgea Cao 				  v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
369f097e410SAlgea Cao 				  v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
370f097e410SAlgea Cao 			return 0;
371f097e410SAlgea Cao 		}
372f097e410SAlgea Cao 	}
373f097e410SAlgea Cao 
374f097e410SAlgea Cao 	if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
375f097e410SAlgea Cao 		if (data->enc_in_format == HDMI_COLORSPACE_RGB &&
376f097e410SAlgea Cao 		    data->enc_out_format == HDMI_COLORSPACE_YUV444) {
377f097e410SAlgea Cao 			csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
378f097e410SAlgea Cao 			auto_csc = AUTO_CSC_DISABLE;
379f097e410SAlgea Cao 			c0_c2_change = C0_C2_CHANGE_DISABLE;
380f097e410SAlgea Cao 			csc_enable = v_CSC_ENABLE;
381f097e410SAlgea Cao 		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
382f097e410SAlgea Cao 			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
383f097e410SAlgea Cao 			csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
384f097e410SAlgea Cao 			auto_csc = AUTO_CSC_ENABLE;
385f097e410SAlgea Cao 			c0_c2_change = C0_C2_CHANGE_DISABLE;
386f097e410SAlgea Cao 			csc_enable = v_CSC_DISABLE;
387f097e410SAlgea Cao 		}
388f097e410SAlgea Cao 	} else {
389f097e410SAlgea Cao 		if (data->enc_in_format == HDMI_COLORSPACE_RGB &&
390f097e410SAlgea Cao 		    data->enc_out_format == HDMI_COLORSPACE_YUV444) {
391f097e410SAlgea Cao 			csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
392f097e410SAlgea Cao 			auto_csc = AUTO_CSC_DISABLE;
393f097e410SAlgea Cao 			c0_c2_change = C0_C2_CHANGE_DISABLE;
394f097e410SAlgea Cao 			csc_enable = v_CSC_ENABLE;
395f097e410SAlgea Cao 		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
396f097e410SAlgea Cao 			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
397f097e410SAlgea Cao 			csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
398f097e410SAlgea Cao 			auto_csc = AUTO_CSC_ENABLE;
399f097e410SAlgea Cao 			c0_c2_change = C0_C2_CHANGE_DISABLE;
400f097e410SAlgea Cao 			csc_enable = v_CSC_DISABLE;
401f097e410SAlgea Cao 		}
402f097e410SAlgea Cao 	}
403f097e410SAlgea Cao 
404f097e410SAlgea Cao 	for (i = 0; i < 24; i++)
405f097e410SAlgea Cao 		hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
406f097e410SAlgea Cao 			    coeff_csc[csc_mode][i]);
407f097e410SAlgea Cao 
408f097e410SAlgea Cao 	value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1);
409f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
410f097e410SAlgea Cao 	hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
411f097e410SAlgea Cao 		  m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
412f097e410SAlgea Cao 		  v_VIDEO_C0_C2_SWAP(c0_c2_change));
413f097e410SAlgea Cao 
414f097e410SAlgea Cao 	return 0;
415f097e410SAlgea Cao }
416f097e410SAlgea Cao 
inno_hdmi_config_video_timing(struct inno_hdmi * hdmi,struct drm_display_mode * mode)417f097e410SAlgea Cao static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
418f097e410SAlgea Cao 					 struct drm_display_mode *mode)
419f097e410SAlgea Cao {
420f097e410SAlgea Cao 	int value;
421f097e410SAlgea Cao 
422f097e410SAlgea Cao 	if (hdmi->plat_data->dev_type == RK3036_HDMI) {
423f097e410SAlgea Cao 		value = BIT(20) | BIT(21);
424f097e410SAlgea Cao 		value |= mode->flags & DRM_MODE_FLAG_PHSYNC ? BIT(4) : 0;
425f097e410SAlgea Cao 		value |= mode->flags & DRM_MODE_FLAG_PVSYNC ? BIT(5) : 0;
426f097e410SAlgea Cao 		writel(value, hdmi->grf + 0x148);
427f097e410SAlgea Cao 	}
428f097e410SAlgea Cao 	/* Set detail external video timing polarity and interlace mode */
429f097e410SAlgea Cao 	value = v_EXTERANL_VIDEO(1);
430f097e410SAlgea Cao 	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
431f097e410SAlgea Cao 		 v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
432f097e410SAlgea Cao 	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
433f097e410SAlgea Cao 		 v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
434f097e410SAlgea Cao 	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
435f097e410SAlgea Cao 		 v_INETLACE(1) : v_INETLACE(0);
436f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
437f097e410SAlgea Cao 
438f097e410SAlgea Cao 	/* Set detail external video timing */
439f097e410SAlgea Cao 	value = mode->htotal;
440f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
441f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
442f097e410SAlgea Cao 
443f097e410SAlgea Cao 	value = mode->htotal - mode->hdisplay;
444f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
445f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
446f097e410SAlgea Cao 
447f097e410SAlgea Cao 	value = mode->htotal - mode->hsync_start;
448f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
449f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
450f097e410SAlgea Cao 
451f097e410SAlgea Cao 	value = mode->hsync_end - mode->hsync_start;
452f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
453f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
454f097e410SAlgea Cao 
455f097e410SAlgea Cao 	value = mode->vtotal;
456f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
457f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
458f097e410SAlgea Cao 
459f097e410SAlgea Cao 	value = mode->vtotal - mode->vdisplay;
460f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
461f097e410SAlgea Cao 
462f097e410SAlgea Cao 	value = mode->vtotal - mode->vsync_start;
463f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
464f097e410SAlgea Cao 
465f097e410SAlgea Cao 	value = mode->vsync_end - mode->vsync_start;
466f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
467f097e410SAlgea Cao 
468f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e);
469f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
470f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
471f097e410SAlgea Cao 
472f097e410SAlgea Cao 	return 0;
473f097e410SAlgea Cao }
474f097e410SAlgea Cao 
inno_hdmi_setup(struct inno_hdmi * hdmi,struct drm_display_mode * mode)475f097e410SAlgea Cao static int inno_hdmi_setup(struct inno_hdmi *hdmi,
476f097e410SAlgea Cao 			   struct drm_display_mode *mode)
477f097e410SAlgea Cao {
478f097e410SAlgea Cao 	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
479f097e410SAlgea Cao 
480f097e410SAlgea Cao 	hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
481f097e410SAlgea Cao 	hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
482f097e410SAlgea Cao 
483f097e410SAlgea Cao 	if (hdmi->hdmi_data.vic == 6 || hdmi->hdmi_data.vic == 7 ||
484f097e410SAlgea Cao 	    hdmi->hdmi_data.vic == 21 || hdmi->hdmi_data.vic == 22 ||
485f097e410SAlgea Cao 	    hdmi->hdmi_data.vic == 2 || hdmi->hdmi_data.vic == 3 ||
486f097e410SAlgea Cao 	    hdmi->hdmi_data.vic == 17 || hdmi->hdmi_data.vic == 18)
487f097e410SAlgea Cao 		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
488f097e410SAlgea Cao 	else
489f097e410SAlgea Cao 		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
490f097e410SAlgea Cao 
491f097e410SAlgea Cao 	/* Mute video and audio output */
492f097e410SAlgea Cao 	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
493f097e410SAlgea Cao 		  v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
494f097e410SAlgea Cao 
495f097e410SAlgea Cao 	/* Set HDMI Mode */
496f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
497f097e410SAlgea Cao 		    v_HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi));
498f097e410SAlgea Cao 
499f097e410SAlgea Cao 	inno_hdmi_config_video_timing(hdmi, mode);
500f097e410SAlgea Cao 
501f097e410SAlgea Cao 	inno_hdmi_config_video_csc(hdmi);
502f097e410SAlgea Cao 
503f097e410SAlgea Cao 	if (hdmi->hdmi_data.sink_is_hdmi) {
504f097e410SAlgea Cao 		inno_hdmi_config_video_avi(hdmi, mode);
505f097e410SAlgea Cao 		inno_hdmi_config_video_vsi(hdmi, mode);
506f097e410SAlgea Cao 	}
507f097e410SAlgea Cao 
508f097e410SAlgea Cao 	/*
509f097e410SAlgea Cao 	 * When IP controller have configured to an accurate video
510f097e410SAlgea Cao 	 * timing, then the TMDS clock source would be switched to
511f097e410SAlgea Cao 	 * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
512f097e410SAlgea Cao 	 * clock rate, and reconfigure the DDC clock.
513f097e410SAlgea Cao 	 */
514f097e410SAlgea Cao 	hdmi->tmds_rate = mode->clock * 1000;
515f097e410SAlgea Cao 	inno_hdmi_i2c_init(hdmi);
516f097e410SAlgea Cao 	/* Unmute video and audio output */
517f097e410SAlgea Cao 	hdmi_modb(hdmi, HDMI_AV_MUTE, m_VIDEO_BLACK, v_VIDEO_MUTE(0));
518f097e410SAlgea Cao 	if (hdmi->audio_enable)
519f097e410SAlgea Cao 		hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE, v_AUDIO_MUTE(0));
520f097e410SAlgea Cao 
521f097e410SAlgea Cao 	return 0;
522f097e410SAlgea Cao }
523f097e410SAlgea Cao 
inno_hdmi_i2c_read(struct inno_hdmi * hdmi,struct i2c_msg * msgs)524f097e410SAlgea Cao static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi,
525f097e410SAlgea Cao 			      struct i2c_msg *msgs)
526f097e410SAlgea Cao {
527f097e410SAlgea Cao 	struct inno_hdmi_i2c *i2c = hdmi->i2c;
528f097e410SAlgea Cao 	unsigned int length = msgs->len;
529f097e410SAlgea Cao 	unsigned char *buf = msgs->buf;
530f097e410SAlgea Cao 	int interrupt = 0, i = 20;
531f097e410SAlgea Cao 
532f097e410SAlgea Cao 	while (i--) {
533f097e410SAlgea Cao 		mdelay(50);
534f097e410SAlgea Cao 		interrupt = 0;
535f097e410SAlgea Cao 		interrupt = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
536f097e410SAlgea Cao 
537f097e410SAlgea Cao 		if (interrupt & m_INT_EDID_READY)
538f097e410SAlgea Cao 			break;
539f097e410SAlgea Cao 	}
540f097e410SAlgea Cao 
541f097e410SAlgea Cao 	if (!interrupt) {
542f097e410SAlgea Cao 		printf("[%s] i2c read reg[0x%02x] no interrupt\n",
543f097e410SAlgea Cao 		       __func__, i2c->slave_reg);
544f097e410SAlgea Cao 		return -EAGAIN;
545f097e410SAlgea Cao 	}
546f097e410SAlgea Cao 
547f097e410SAlgea Cao 	/* Clear HDMI EDID interrupt flag */
548f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
549f097e410SAlgea Cao 
550f097e410SAlgea Cao 	while (length--)
551f097e410SAlgea Cao 		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
552f097e410SAlgea Cao 
553f097e410SAlgea Cao 	return 0;
554f097e410SAlgea Cao }
555f097e410SAlgea Cao 
inno_hdmi_i2c_write(struct inno_hdmi * hdmi,struct i2c_msg * msgs)556f097e410SAlgea Cao static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi,
557f097e410SAlgea Cao 			       struct i2c_msg *msgs)
558f097e410SAlgea Cao {
559f097e410SAlgea Cao 	unsigned int length = msgs->len;
560f097e410SAlgea Cao 
561f097e410SAlgea Cao 	hdmi->i2c->segment_addr = 0;
562f097e410SAlgea Cao 	hdmi->i2c->ddc_addr = 0;
563f097e410SAlgea Cao 
564f097e410SAlgea Cao 	/*
565f097e410SAlgea Cao 	 * The DDC module only support read EDID message, so
566f097e410SAlgea Cao 	 * we assume that each word write to this i2c adapter
567f097e410SAlgea Cao 	 * should be the offset of EDID word address.
568f097e410SAlgea Cao 	 */
569f097e410SAlgea Cao 	if (length != 1 ||
570f097e410SAlgea Cao 	    (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR)) {
571f097e410SAlgea Cao 		printf("DDC word write to i2c adapter is not EDID address\n");
572f097e410SAlgea Cao 		return -EINVAL;
573f097e410SAlgea Cao 	}
574f097e410SAlgea Cao 
575f097e410SAlgea Cao 	if (msgs->addr == DDC_SEGMENT_ADDR)
576f097e410SAlgea Cao 		hdmi->i2c->segment_addr = msgs->buf[0];
577f097e410SAlgea Cao 	if (msgs->addr == DDC_ADDR)
578f097e410SAlgea Cao 		hdmi->i2c->ddc_addr = msgs->buf[0];
579f097e410SAlgea Cao 
580f097e410SAlgea Cao 	/* Set edid fifo first addr */
581f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
582f097e410SAlgea Cao 
583f097e410SAlgea Cao 	/* Set edid word address 0x00/0x80 */
584f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
585f097e410SAlgea Cao 
586f097e410SAlgea Cao 	/* Set edid segment pointer */
587f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
588f097e410SAlgea Cao 
589f097e410SAlgea Cao 	return 0;
590f097e410SAlgea Cao }
591f097e410SAlgea Cao 
inno_hdmi_i2c_xfer(struct ddc_adapter * adap,struct i2c_msg * msgs,int num)592f097e410SAlgea Cao static int inno_hdmi_i2c_xfer(struct ddc_adapter *adap,
593f097e410SAlgea Cao 			      struct i2c_msg *msgs, int num)
594f097e410SAlgea Cao {
595f097e410SAlgea Cao 	struct inno_hdmi *hdmi = container_of(adap, struct inno_hdmi, adap);
596f097e410SAlgea Cao 	int i, ret = 0;
597f097e410SAlgea Cao 
598f097e410SAlgea Cao 	/* Clear the EDID interrupt flag and unmute the interrupt */
599f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
600f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
601f097e410SAlgea Cao 
602f097e410SAlgea Cao 	for (i = 0; i < num; i++) {
603f097e410SAlgea Cao 		dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
604f097e410SAlgea Cao 			i + 1, num, msgs[i].len, msgs[i].flags);
605f097e410SAlgea Cao 
606f097e410SAlgea Cao 		if (msgs[i].flags & I2C_M_RD)
607f097e410SAlgea Cao 			ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
608f097e410SAlgea Cao 		else
609f097e410SAlgea Cao 			ret = inno_hdmi_i2c_write(hdmi, &msgs[i]);
610f097e410SAlgea Cao 
611f097e410SAlgea Cao 		if (ret < 0)
612f097e410SAlgea Cao 			break;
613f097e410SAlgea Cao 	}
614f097e410SAlgea Cao 
615f097e410SAlgea Cao 	if (!ret)
616f097e410SAlgea Cao 		ret = num;
617f097e410SAlgea Cao 
618f097e410SAlgea Cao 	/* Mute HDMI EDID interrupt */
619f097e410SAlgea Cao 	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
620f097e410SAlgea Cao 
621f097e410SAlgea Cao 	return ret;
622f097e410SAlgea Cao }
623f097e410SAlgea Cao 
rockchip_inno_hdmi_init(struct rockchip_connector * conn,struct display_state * state)6240594ce39SZhang Yubing static int rockchip_inno_hdmi_init(struct rockchip_connector *conn, struct display_state *state)
62558c17f51SSandy Huang {
62658c17f51SSandy Huang 	struct connector_state *conn_state = &state->conn_state;
627f097e410SAlgea Cao 	struct inno_hdmi *hdmi;
628f097e410SAlgea Cao 	struct drm_display_mode *mode_buf;
6290594ce39SZhang Yubing 	ofnode hdmi_node = conn->dev->node;
630f097e410SAlgea Cao 	int ret;
631f097e410SAlgea Cao 
632f097e410SAlgea Cao 	hdmi = calloc(1, sizeof(struct inno_hdmi));
633f097e410SAlgea Cao 	if (!hdmi)
634f097e410SAlgea Cao 		return -ENOMEM;
635f097e410SAlgea Cao 
636f097e410SAlgea Cao 	mode_buf = calloc(1, MODE_LEN * sizeof(struct drm_display_mode));
637f097e410SAlgea Cao 	if (!mode_buf)
638f097e410SAlgea Cao 		return -ENOMEM;
639f097e410SAlgea Cao 
6400594ce39SZhang Yubing 	hdmi->regs = dev_read_addr_ptr(conn->dev);
641f097e410SAlgea Cao 
642f097e410SAlgea Cao 	hdmi->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
643f097e410SAlgea Cao 	if (hdmi->grf <= 0) {
644f097e410SAlgea Cao 		printf("%s: Get syscon grf failed (ret=%p)\n",
645f097e410SAlgea Cao 		       __func__, hdmi->grf);
646f097e410SAlgea Cao 		return -ENXIO;
647f097e410SAlgea Cao 	}
648f097e410SAlgea Cao 
649f097e410SAlgea Cao 	hdmi->i2c = malloc(sizeof(struct inno_hdmi_i2c));
650f097e410SAlgea Cao 	if (!hdmi->i2c)
651f097e410SAlgea Cao 		return -ENOMEM;
652f097e410SAlgea Cao 
653f097e410SAlgea Cao 	hdmi->adap.ddc_xfer = inno_hdmi_i2c_xfer;
654f097e410SAlgea Cao 
655f097e410SAlgea Cao 	/*
656f097e410SAlgea Cao 	 * Read high and low time from device tree. If not available use
657f097e410SAlgea Cao 	 * the default timing scl clock rate is about 99.6KHz.
658f097e410SAlgea Cao 	 */
659f097e410SAlgea Cao 	hdmi->i2c->scl_high_ns =
660f097e410SAlgea Cao 		ofnode_read_s32_default(hdmi_node,
661f097e410SAlgea Cao 					"ddc-i2c-scl-high-time-ns", 4708);
662f097e410SAlgea Cao 	hdmi->i2c->scl_low_ns =
663f097e410SAlgea Cao 		ofnode_read_s32_default(hdmi_node,
664f097e410SAlgea Cao 					"ddc-i2c-scl-low-time-ns", 4916);
665f097e410SAlgea Cao 
666f097e410SAlgea Cao 	conn_state->type = DRM_MODE_CONNECTOR_HDMIA;
667f097e410SAlgea Cao 	conn_state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
668f097e410SAlgea Cao 
6690594ce39SZhang Yubing 	hdmi->plat_data = (struct inno_hdmi_plat_data *)dev_get_driver_data(conn->dev);
670f097e410SAlgea Cao 	hdmi->edid_data.mode_buf = mode_buf;
671f097e410SAlgea Cao 	hdmi->sample_rate = 48000;
672f097e410SAlgea Cao 
6730594ce39SZhang Yubing 	conn->data = hdmi;
674f097e410SAlgea Cao 
675f097e410SAlgea Cao 	inno_hdmi_reset(hdmi);
6760594ce39SZhang Yubing 	ret = clk_get_by_name(conn->dev, "pclk", &hdmi->pclk);
677f097e410SAlgea Cao 	if (ret < 0) {
678f097e410SAlgea Cao 		dev_err(hdmi->dev, "failed to get pclk: %d\n", ret);
679f097e410SAlgea Cao 		return ret;
680f097e410SAlgea Cao 	}
681f097e410SAlgea Cao 	hdmi->tmds_rate = clk_get_rate(&hdmi->pclk);
682f097e410SAlgea Cao 	inno_hdmi_i2c_init(hdmi);
683f097e410SAlgea Cao 
684f097e410SAlgea Cao 	/* Unmute hotplug interrupt */
685f097e410SAlgea Cao 	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
686f097e410SAlgea Cao 
687f097e410SAlgea Cao 	return 0;
688f097e410SAlgea Cao }
689f097e410SAlgea Cao 
rockchip_inno_hdmi_enable(struct rockchip_connector * conn,struct display_state * state)6900594ce39SZhang Yubing static int rockchip_inno_hdmi_enable(struct rockchip_connector *conn, struct display_state *state)
691f097e410SAlgea Cao {
692f097e410SAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
693f097e410SAlgea Cao 	struct drm_display_mode *mode = &conn_state->mode;
6940594ce39SZhang Yubing 	struct inno_hdmi *hdmi = conn->data;
695f097e410SAlgea Cao 
696f097e410SAlgea Cao 	if (!hdmi)
697f097e410SAlgea Cao 		return -EFAULT;
698f097e410SAlgea Cao 
699f097e410SAlgea Cao 	/* Store the display mode for plugin/DKMS poweron events */
700f097e410SAlgea Cao 	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
701f097e410SAlgea Cao 
702f097e410SAlgea Cao 	inno_hdmi_setup(hdmi, mode);
703f097e410SAlgea Cao 	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
704f097e410SAlgea Cao 
705f097e410SAlgea Cao 	return 0;
706f097e410SAlgea Cao }
707f097e410SAlgea Cao 
rockchip_inno_hdmi_deinit(struct rockchip_connector * conn,struct display_state * state)7080594ce39SZhang Yubing static void rockchip_inno_hdmi_deinit(struct rockchip_connector *conn, struct display_state *state)
709f097e410SAlgea Cao {
7100594ce39SZhang Yubing 	struct inno_hdmi *hdmi = conn->data;
711f097e410SAlgea Cao 
712f097e410SAlgea Cao 	if (hdmi->i2c)
713f097e410SAlgea Cao 		free(hdmi->i2c);
714f097e410SAlgea Cao 	if (hdmi)
715f097e410SAlgea Cao 		free(hdmi);
716f097e410SAlgea Cao }
717f097e410SAlgea Cao 
rockchip_inno_hdmi_prepare(struct rockchip_connector * conn,struct display_state * state)7180594ce39SZhang Yubing static int rockchip_inno_hdmi_prepare(struct rockchip_connector *conn, struct display_state *state)
719f097e410SAlgea Cao {
720f097e410SAlgea Cao 	return 0;
721f097e410SAlgea Cao }
722f097e410SAlgea Cao 
rockchip_inno_hdmi_disable(struct rockchip_connector * conn,struct display_state * state)7230594ce39SZhang Yubing static int rockchip_inno_hdmi_disable(struct rockchip_connector *conn, struct display_state *state)
724f097e410SAlgea Cao {
7250594ce39SZhang Yubing 	struct inno_hdmi *hdmi = conn->data;
726f097e410SAlgea Cao 
727f097e410SAlgea Cao 	inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
728f097e410SAlgea Cao 	return 0;
729f097e410SAlgea Cao }
730f097e410SAlgea Cao 
rockchip_inno_hdmi_detect(struct rockchip_connector * conn,struct display_state * state)7310594ce39SZhang Yubing static int rockchip_inno_hdmi_detect(struct rockchip_connector *conn, struct display_state *state)
732f097e410SAlgea Cao {
7330594ce39SZhang Yubing 	struct inno_hdmi *hdmi = conn->data;
734f097e410SAlgea Cao 
735f097e410SAlgea Cao 	return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
736f097e410SAlgea Cao 		connector_status_connected : connector_status_disconnected;
737f097e410SAlgea Cao }
738f097e410SAlgea Cao 
rockchip_inno_hdmi_get_timing(struct rockchip_connector * conn,struct display_state * state)7390594ce39SZhang Yubing static int rockchip_inno_hdmi_get_timing(struct rockchip_connector *conn,
7400594ce39SZhang Yubing 					 struct display_state *state)
741f097e410SAlgea Cao {
742*9c170041SAlgea Cao 	int  i, ret = 0;
743f097e410SAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
744f097e410SAlgea Cao 	struct drm_display_mode *mode = &conn_state->mode;
7450594ce39SZhang Yubing 	struct inno_hdmi *hdmi = conn->data;
746f097e410SAlgea Cao 	struct edid *edid = (struct edid *)conn_state->edid;
747f097e410SAlgea Cao 	const u8 def_modes_vic[6] = {16, 4, 2, 17, 31, 19};
748f097e410SAlgea Cao 
749f097e410SAlgea Cao 	if (!hdmi)
750f097e410SAlgea Cao 		return -EFAULT;
751f097e410SAlgea Cao 
752*9c170041SAlgea Cao 	conn_state->edid = drm_do_get_edid(&hdmi->adap);
753*9c170041SAlgea Cao 	if (conn_state->edid) {
754f097e410SAlgea Cao 		hdmi->hdmi_data.sink_is_hdmi =
755f097e410SAlgea Cao 			drm_detect_hdmi_monitor(edid);
756f097e410SAlgea Cao 		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
757f097e410SAlgea Cao 		ret = drm_add_edid_modes(&hdmi->edid_data, conn_state->edid);
758f097e410SAlgea Cao 	}
759f097e410SAlgea Cao 	if (ret <= 0) {
760f097e410SAlgea Cao 		hdmi->hdmi_data.sink_is_hdmi = true;
761f097e410SAlgea Cao 		hdmi->hdmi_data.sink_has_audio = true;
762f097e410SAlgea Cao 		do_cea_modes(&hdmi->edid_data, def_modes_vic,
763f097e410SAlgea Cao 			     sizeof(def_modes_vic));
764f097e410SAlgea Cao 		hdmi->edid_data.preferred_mode = &hdmi->edid_data.mode_buf[0];
765f097e410SAlgea Cao 		printf("failed to get edid\n");
766f097e410SAlgea Cao 	}
767f097e410SAlgea Cao 	drm_rk_filter_whitelist(&hdmi->edid_data);
768f097e410SAlgea Cao 
769f097e410SAlgea Cao 	if (!drm_mode_prune_invalid(&hdmi->edid_data)) {
770f097e410SAlgea Cao 		printf("can't find valid hdmi mode\n");
771f097e410SAlgea Cao 		return -EINVAL;
772f097e410SAlgea Cao 	}
773f097e410SAlgea Cao 
774f097e410SAlgea Cao 	for (i = 0; i < hdmi->edid_data.modes; i++)
775f097e410SAlgea Cao 		hdmi->edid_data.mode_buf[i].vrefresh =
776f097e410SAlgea Cao 			drm_mode_vrefresh(&hdmi->edid_data.mode_buf[i]);
777f097e410SAlgea Cao 
778f097e410SAlgea Cao 	drm_mode_sort(&hdmi->edid_data);
779f097e410SAlgea Cao 
780f097e410SAlgea Cao 	*mode = *hdmi->edid_data.preferred_mode;
781f097e410SAlgea Cao 	hdmi->vic = drm_match_cea_mode(mode);
782f097e410SAlgea Cao 
783f097e410SAlgea Cao 	printf("mode:%dx%d\n", mode->hdisplay, mode->vdisplay);
784f097e410SAlgea Cao 
785f097e410SAlgea Cao 	conn_state->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
786f097e410SAlgea Cao 
787f097e410SAlgea Cao 	return 0;
788f097e410SAlgea Cao }
789f097e410SAlgea Cao 
790f097e410SAlgea Cao const struct rockchip_connector_funcs rockchip_inno_hdmi_funcs = {
791f097e410SAlgea Cao 	.init = rockchip_inno_hdmi_init,
792f097e410SAlgea Cao 	.deinit = rockchip_inno_hdmi_deinit,
793f097e410SAlgea Cao 	.prepare = rockchip_inno_hdmi_prepare,
794f097e410SAlgea Cao 	.enable = rockchip_inno_hdmi_enable,
795f097e410SAlgea Cao 	.disable = rockchip_inno_hdmi_disable,
796f097e410SAlgea Cao 	.get_timing = rockchip_inno_hdmi_get_timing,
797f097e410SAlgea Cao 	.detect = rockchip_inno_hdmi_detect,
798f097e410SAlgea Cao };
799f097e410SAlgea Cao 
rockchip_inno_hdmi_probe(struct udevice * dev)8000594ce39SZhang Yubing static int rockchip_inno_hdmi_probe(struct udevice *dev)
8010594ce39SZhang Yubing {
8020594ce39SZhang Yubing 	int id;
8030594ce39SZhang Yubing 	struct rockchip_connector *conn = dev_get_priv(dev);
8040594ce39SZhang Yubing 
8050594ce39SZhang Yubing 	id = of_alias_get_id(ofnode_to_np(dev->node), "hdmi");
8060594ce39SZhang Yubing 	if (id < 0)
8070594ce39SZhang Yubing 		id = 0;
8080594ce39SZhang Yubing 
8090594ce39SZhang Yubing 	rockchip_connector_bind(conn, dev, id, &rockchip_inno_hdmi_funcs, NULL,
8100594ce39SZhang Yubing 				DRM_MODE_CONNECTOR_HDMIA);
8110594ce39SZhang Yubing 
8120594ce39SZhang Yubing 	return 0;
8130594ce39SZhang Yubing }
8140594ce39SZhang Yubing 
rockchip_inno_hdmi_bind(struct udevice * dev)8150594ce39SZhang Yubing static int rockchip_inno_hdmi_bind(struct udevice *dev)
8160594ce39SZhang Yubing {
8170594ce39SZhang Yubing 	return 0;
8180594ce39SZhang Yubing }
8190594ce39SZhang Yubing 
820f097e410SAlgea Cao static const struct inno_hdmi_plat_data rk3036_hdmi_drv_data = {
821f097e410SAlgea Cao 	.dev_type   = RK3036_HDMI,
822f097e410SAlgea Cao 	.phy_config = rk3036_hdmi_phy_config,
823f097e410SAlgea Cao };
824f097e410SAlgea Cao 
825f097e410SAlgea Cao static const struct inno_hdmi_plat_data rk3128_hdmi_drv_data = {
826f097e410SAlgea Cao 	.dev_type   = RK3128_HDMI,
827f097e410SAlgea Cao 	.phy_config = rk3128_hdmi_phy_config,
828f097e410SAlgea Cao };
829f097e410SAlgea Cao 
830f097e410SAlgea Cao static const struct udevice_id rockchip_inno_hdmi_ids[] = {
831f097e410SAlgea Cao 	{
832f097e410SAlgea Cao 	 .compatible = "rockchip,rk3036-inno-hdmi",
8330594ce39SZhang Yubing 	 .data = (ulong)&rk3036_hdmi_drv_data,
834f097e410SAlgea Cao 	},
835f097e410SAlgea Cao 	{
836f097e410SAlgea Cao 	 .compatible = "rockchip,rk3128-inno-hdmi",
8370594ce39SZhang Yubing 	 .data = (ulong)&rk3128_hdmi_drv_data,
838f097e410SAlgea Cao 	}, {}
839f097e410SAlgea Cao 
840f097e410SAlgea Cao };
841f097e410SAlgea Cao 
842f097e410SAlgea Cao U_BOOT_DRIVER(rockchip_inno_hdmi) = {
843f097e410SAlgea Cao 	.name = "rockchip_inno_hdmi",
844f097e410SAlgea Cao 	.id = UCLASS_DISPLAY,
845f097e410SAlgea Cao 	.of_match = rockchip_inno_hdmi_ids,
846f097e410SAlgea Cao 	.probe	= rockchip_inno_hdmi_probe,
847f097e410SAlgea Cao 	.bind	= rockchip_inno_hdmi_bind,
8480594ce39SZhang Yubing 	.priv_auto_alloc_size = sizeof(struct rockchip_connector),
849f097e410SAlgea Cao };
850