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