xref: /rk3399_rockchip-uboot/drivers/video/drm/dw_hdmi.c (revision cb17ca6c52fc044e0a9c628bc67dae1f1d83d6f4)
1f5e7d251SAlgea Cao /*
2f5e7d251SAlgea Cao  * (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd
3f5e7d251SAlgea Cao  *
4f5e7d251SAlgea Cao  * SPDX-License-Identifier:	GPL-2.0+
5f5e7d251SAlgea Cao  */
6f5e7d251SAlgea Cao 
7f5e7d251SAlgea Cao #include <common.h>
8f5e7d251SAlgea Cao #include <malloc.h>
9f5e7d251SAlgea Cao #include <syscon.h>
10f5e7d251SAlgea Cao #include <asm/arch-rockchip/clock.h>
118e2bab3fSAlgea Cao #include <asm/arch/vendor.h>
12f5e7d251SAlgea Cao #include <edid.h>
13e2bce6e4SKever Yang #include <dm/device.h>
141f71919fSShunqing Chen #include <dm/of_access.h>
158e2bab3fSAlgea Cao #include <dm/ofnode.h>
16e2bce6e4SKever Yang #include <dm/read.h>
17f5e7d251SAlgea Cao #include <linux/hdmi.h>
18f5e7d251SAlgea Cao #include <linux/media-bus-format.h>
19f5e7d251SAlgea Cao #include <linux/dw_hdmi.h>
20f5e7d251SAlgea Cao #include <asm/io.h>
21f5e7d251SAlgea Cao #include "rockchip_display.h"
22f5e7d251SAlgea Cao #include "rockchip_crtc.h"
23f5e7d251SAlgea Cao #include "rockchip_connector.h"
24f5e7d251SAlgea Cao #include "dw_hdmi.h"
258e2bab3fSAlgea Cao #include "rockchip_phy.h"
26f5e7d251SAlgea Cao 
278e2bab3fSAlgea Cao #define HDCP_PRIVATE_KEY_SIZE   280
288e2bab3fSAlgea Cao #define HDCP_KEY_SHA_SIZE       20
298e2bab3fSAlgea Cao #define HDMI_HDCP1X_ID		5
30f5e7d251SAlgea Cao /*
31f5e7d251SAlgea Cao  * Unless otherwise noted, entries in this table are 100% optimization.
32f5e7d251SAlgea Cao  * Values can be obtained from hdmi_compute_n() but that function is
33f5e7d251SAlgea Cao  * slow so we pre-compute values we expect to see.
34f5e7d251SAlgea Cao  *
35f5e7d251SAlgea Cao  * All 32k and 48k values are expected to be the same (due to the way
36f5e7d251SAlgea Cao  * the math works) for any rate that's an exact kHz.
37f5e7d251SAlgea Cao  */
38f5e7d251SAlgea Cao static const struct dw_hdmi_audio_tmds_n common_tmds_n_table[] = {
39f5e7d251SAlgea Cao 	{ .tmds = 25175000, .n_32k = 4096, .n_44k1 = 12854, .n_48k = 6144, },
40f5e7d251SAlgea Cao 	{ .tmds = 25200000, .n_32k = 4096, .n_44k1 = 5656, .n_48k = 6144, },
41f5e7d251SAlgea Cao 	{ .tmds = 27000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
42f5e7d251SAlgea Cao 	{ .tmds = 28320000, .n_32k = 4096, .n_44k1 = 5586, .n_48k = 6144, },
43f5e7d251SAlgea Cao 	{ .tmds = 30240000, .n_32k = 4096, .n_44k1 = 5642, .n_48k = 6144, },
44f5e7d251SAlgea Cao 	{ .tmds = 31500000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
45f5e7d251SAlgea Cao 	{ .tmds = 32000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
46f5e7d251SAlgea Cao 	{ .tmds = 33750000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
47f5e7d251SAlgea Cao 	{ .tmds = 36000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
48f5e7d251SAlgea Cao 	{ .tmds = 40000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
49f5e7d251SAlgea Cao 	{ .tmds = 49500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
50f5e7d251SAlgea Cao 	{ .tmds = 50000000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
51f5e7d251SAlgea Cao 	{ .tmds = 54000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
52f5e7d251SAlgea Cao 	{ .tmds = 65000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
53f5e7d251SAlgea Cao 	{ .tmds = 68250000, .n_32k = 4096, .n_44k1 = 5376, .n_48k = 6144, },
54f5e7d251SAlgea Cao 	{ .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
55f5e7d251SAlgea Cao 	{ .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, },
56f5e7d251SAlgea Cao 	{ .tmds = 73250000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
57f5e7d251SAlgea Cao 	{ .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
58f5e7d251SAlgea Cao 	{ .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, },
59f5e7d251SAlgea Cao 	{ .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
60f5e7d251SAlgea Cao 	{ .tmds = 78800000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
61f5e7d251SAlgea Cao 	{ .tmds = 79500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
62f5e7d251SAlgea Cao 	{ .tmds = 83500000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
63f5e7d251SAlgea Cao 	{ .tmds = 85500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
64f5e7d251SAlgea Cao 	{ .tmds = 88750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
65f5e7d251SAlgea Cao 	{ .tmds = 97750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
66f5e7d251SAlgea Cao 	{ .tmds = 101000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
67f5e7d251SAlgea Cao 	{ .tmds = 106500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
68f5e7d251SAlgea Cao 	{ .tmds = 108000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
69f5e7d251SAlgea Cao 	{ .tmds = 115500000, .n_32k = 4096, .n_44k1 = 5712, .n_48k = 6144, },
70f5e7d251SAlgea Cao 	{ .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
71f5e7d251SAlgea Cao 	{ .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
72f5e7d251SAlgea Cao 	{ .tmds = 146250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
73f5e7d251SAlgea Cao 	{ .tmds = 148500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
74f5e7d251SAlgea Cao 	{ .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
75f5e7d251SAlgea Cao 	{ .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
76f5e7d251SAlgea Cao 
77f5e7d251SAlgea Cao 	/* For 297 MHz+ HDMI spec have some other rule for setting N */
78f5e7d251SAlgea Cao 	{ .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, },
79f5e7d251SAlgea Cao 	{ .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240, },
80f5e7d251SAlgea Cao 
81f5e7d251SAlgea Cao 	/* End of table */
82f5e7d251SAlgea Cao 	{ .tmds = 0,         .n_32k = 0,    .n_44k1 = 0,    .n_48k = 0, },
83f5e7d251SAlgea Cao };
84f5e7d251SAlgea Cao 
85f5e7d251SAlgea Cao static const u16 csc_coeff_default[3][4] = {
86f5e7d251SAlgea Cao 	{ 0x2000, 0x0000, 0x0000, 0x0000 },
87f5e7d251SAlgea Cao 	{ 0x0000, 0x2000, 0x0000, 0x0000 },
88f5e7d251SAlgea Cao 	{ 0x0000, 0x0000, 0x2000, 0x0000 }
89f5e7d251SAlgea Cao };
90f5e7d251SAlgea Cao 
91f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_out_eitu601[3][4] = {
92f5e7d251SAlgea Cao 	{ 0x2000, 0x6926, 0x74fd, 0x010e },
93f5e7d251SAlgea Cao 	{ 0x2000, 0x2cdd, 0x0000, 0x7e9a },
94f5e7d251SAlgea Cao 	{ 0x2000, 0x0000, 0x38b4, 0x7e3b }
95f5e7d251SAlgea Cao };
96f5e7d251SAlgea Cao 
97f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_out_eitu709[3][4] = {
98f5e7d251SAlgea Cao 	{ 0x2000, 0x7106, 0x7a02, 0x00a7 },
99f5e7d251SAlgea Cao 	{ 0x2000, 0x3264, 0x0000, 0x7e6d },
100f5e7d251SAlgea Cao 	{ 0x2000, 0x0000, 0x3b61, 0x7e25 }
101f5e7d251SAlgea Cao };
102f5e7d251SAlgea Cao 
103f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_in_eitu601[3][4] = {
104f5e7d251SAlgea Cao 	{ 0x2591, 0x1322, 0x074b, 0x0000 },
105f5e7d251SAlgea Cao 	{ 0x6535, 0x2000, 0x7acc, 0x0200 },
106f5e7d251SAlgea Cao 	{ 0x6acd, 0x7534, 0x2000, 0x0200 }
107f5e7d251SAlgea Cao };
108f5e7d251SAlgea Cao 
109f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
110f5e7d251SAlgea Cao 	{ 0x2dc5, 0x0d9b, 0x049e, 0x0000 },
111f5e7d251SAlgea Cao 	{ 0x62f0, 0x2000, 0x7d11, 0x0200 },
112f5e7d251SAlgea Cao 	{ 0x6756, 0x78ab, 0x2000, 0x0200 }
113f5e7d251SAlgea Cao };
114f5e7d251SAlgea Cao 
115b5016cf2SAlgea Cao static const u16 csc_coeff_full_to_limited[3][4] = {
116b5016cf2SAlgea Cao 	{ 0x36f7, 0x0000, 0x0000, 0x0040 },
117b5016cf2SAlgea Cao 	{ 0x0000, 0x36f7, 0x0000, 0x0040 },
118b5016cf2SAlgea Cao 	{ 0x0000, 0x0000, 0x36f7, 0x0040 }
119b5016cf2SAlgea Cao };
120b5016cf2SAlgea Cao 
121f5e7d251SAlgea Cao struct hdmi_vmode {
122f5e7d251SAlgea Cao 	bool mdataenablepolarity;
123f5e7d251SAlgea Cao 
124f5e7d251SAlgea Cao 	unsigned int mpixelclock;
125f5e7d251SAlgea Cao 	unsigned int mpixelrepetitioninput;
126f5e7d251SAlgea Cao 	unsigned int mpixelrepetitionoutput;
1278e2bab3fSAlgea Cao 	unsigned int mtmdsclock;
128f5e7d251SAlgea Cao };
129f5e7d251SAlgea Cao 
130f5e7d251SAlgea Cao struct hdmi_data_info {
131f5e7d251SAlgea Cao 	unsigned int enc_in_bus_format;
132f5e7d251SAlgea Cao 	unsigned int enc_out_bus_format;
133f5e7d251SAlgea Cao 	unsigned int enc_in_encoding;
134f5e7d251SAlgea Cao 	unsigned int enc_out_encoding;
135b5016cf2SAlgea Cao 	unsigned int quant_range;
136f5e7d251SAlgea Cao 	unsigned int pix_repet_factor;
137f5e7d251SAlgea Cao 	struct hdmi_vmode video_mode;
138f5e7d251SAlgea Cao };
139f5e7d251SAlgea Cao 
140f5e7d251SAlgea Cao struct dw_hdmi_phy_data {
141f5e7d251SAlgea Cao 	enum dw_hdmi_phy_type type;
142f5e7d251SAlgea Cao 	const char *name;
143f5e7d251SAlgea Cao 	unsigned int gen;
144f5e7d251SAlgea Cao 	bool has_svsret;
145f5e7d251SAlgea Cao 	int (*configure)(struct dw_hdmi *hdmi,
146f5e7d251SAlgea Cao 			 const struct dw_hdmi_plat_data *pdata,
147f5e7d251SAlgea Cao 			 unsigned long mpixelclock);
148f5e7d251SAlgea Cao };
149f5e7d251SAlgea Cao 
1508e2bab3fSAlgea Cao struct hdcp_keys {
1518e2bab3fSAlgea Cao 	u8 KSV[8];
1528e2bab3fSAlgea Cao 	u8 devicekey[HDCP_PRIVATE_KEY_SIZE];
1538e2bab3fSAlgea Cao 	u8 sha1[HDCP_KEY_SHA_SIZE];
1548e2bab3fSAlgea Cao 	u8 seeds[2];
1558e2bab3fSAlgea Cao };
1568e2bab3fSAlgea Cao 
1578e2bab3fSAlgea Cao struct dw_hdmi_i2c {
1588e2bab3fSAlgea Cao 	u8			slave_reg;
1598e2bab3fSAlgea Cao 	bool			is_regaddr;
1608e2bab3fSAlgea Cao 	bool			is_segment;
1618e2bab3fSAlgea Cao 
1628e2bab3fSAlgea Cao 	unsigned int		scl_high_ns;
1638e2bab3fSAlgea Cao 	unsigned int		scl_low_ns;
1648e2bab3fSAlgea Cao };
1658e2bab3fSAlgea Cao 
166f5e7d251SAlgea Cao struct dw_hdmi {
167*cb17ca6cSSandy Huang 	int id;
168f5e7d251SAlgea Cao 	enum dw_hdmi_devtype dev_type;
169f5e7d251SAlgea Cao 	unsigned int version;
170f5e7d251SAlgea Cao 	struct hdmi_data_info hdmi_data;
171f5e7d251SAlgea Cao 	struct hdmi_edid_data edid_data;
172f5e7d251SAlgea Cao 	const struct dw_hdmi_plat_data *plat_data;
1738e2bab3fSAlgea Cao 	struct ddc_adapter adap;
174f5e7d251SAlgea Cao 
175f5e7d251SAlgea Cao 	int vic;
176f5e7d251SAlgea Cao 	int io_width;
177f5e7d251SAlgea Cao 
178f5e7d251SAlgea Cao 	unsigned long bus_format;
179f5e7d251SAlgea Cao 	bool cable_plugin;
180f5e7d251SAlgea Cao 	bool sink_is_hdmi;
181f5e7d251SAlgea Cao 	bool sink_has_audio;
182f5e7d251SAlgea Cao 	void *regs;
183f5e7d251SAlgea Cao 	void *grf;
1848e2bab3fSAlgea Cao 	struct dw_hdmi_i2c *i2c;
185f5e7d251SAlgea Cao 
186f5e7d251SAlgea Cao 	struct {
187f5e7d251SAlgea Cao 		const struct dw_hdmi_phy_ops *ops;
188f5e7d251SAlgea Cao 		const char *name;
189f5e7d251SAlgea Cao 		void *data;
190f5e7d251SAlgea Cao 		bool enabled;
191f5e7d251SAlgea Cao 	} phy;
192f5e7d251SAlgea Cao 
193f5e7d251SAlgea Cao 	struct drm_display_mode previous_mode;
194f5e7d251SAlgea Cao 
195f5e7d251SAlgea Cao 	unsigned int sample_rate;
196f5e7d251SAlgea Cao 	unsigned int audio_cts;
197f5e7d251SAlgea Cao 	unsigned int audio_n;
198f5e7d251SAlgea Cao 	bool audio_enable;
1998e2bab3fSAlgea Cao 	bool scramble_low_rates;
200f5e7d251SAlgea Cao 
201f5e7d251SAlgea Cao 	void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
202f5e7d251SAlgea Cao 	u8 (*read)(struct dw_hdmi *hdmi, int offset);
2038e2bab3fSAlgea Cao 
2048e2bab3fSAlgea Cao 	bool hdcp1x_enable;
205f5e7d251SAlgea Cao };
206f5e7d251SAlgea Cao 
207f5e7d251SAlgea Cao static void dw_hdmi_writel(struct dw_hdmi *hdmi, u8 val, int offset)
208f5e7d251SAlgea Cao {
209f5e7d251SAlgea Cao 	writel(val, hdmi->regs + (offset << 2));
210f5e7d251SAlgea Cao }
211f5e7d251SAlgea Cao 
212f5e7d251SAlgea Cao static u8 dw_hdmi_readl(struct dw_hdmi *hdmi, int offset)
213f5e7d251SAlgea Cao {
214f5e7d251SAlgea Cao 	return readl(hdmi->regs + (offset << 2));
215f5e7d251SAlgea Cao }
216f5e7d251SAlgea Cao 
217f5e7d251SAlgea Cao static void dw_hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset)
218f5e7d251SAlgea Cao {
219f5e7d251SAlgea Cao 	writeb(val, hdmi->regs + offset);
220f5e7d251SAlgea Cao }
221f5e7d251SAlgea Cao 
222f5e7d251SAlgea Cao static u8 dw_hdmi_readb(struct dw_hdmi *hdmi, int offset)
223f5e7d251SAlgea Cao {
224f5e7d251SAlgea Cao 	return readb(hdmi->regs + offset);
225f5e7d251SAlgea Cao }
226f5e7d251SAlgea Cao 
227f5e7d251SAlgea Cao static inline void hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset)
228f5e7d251SAlgea Cao {
229f5e7d251SAlgea Cao 	hdmi->write(hdmi, val, offset);
230f5e7d251SAlgea Cao }
231f5e7d251SAlgea Cao 
232f5e7d251SAlgea Cao static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset)
233f5e7d251SAlgea Cao {
234f5e7d251SAlgea Cao 	return hdmi->read(hdmi, offset);
235f5e7d251SAlgea Cao }
236f5e7d251SAlgea Cao 
237f5e7d251SAlgea Cao static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
238f5e7d251SAlgea Cao {
239f5e7d251SAlgea Cao 	u8 val = hdmi_readb(hdmi, reg) & ~mask;
240f5e7d251SAlgea Cao 
241f5e7d251SAlgea Cao 	val |= data & mask;
242f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, val, reg);
243f5e7d251SAlgea Cao }
244f5e7d251SAlgea Cao 
245f5e7d251SAlgea Cao static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg,
246f5e7d251SAlgea Cao 			     u8 shift, u8 mask)
247f5e7d251SAlgea Cao {
248f5e7d251SAlgea Cao 	hdmi_modb(hdmi, data << shift, mask, reg);
249f5e7d251SAlgea Cao }
250f5e7d251SAlgea Cao 
251f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format)
252f5e7d251SAlgea Cao {
253f5e7d251SAlgea Cao 	switch (bus_format) {
254f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_RGB888_1X24:
255f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_RGB101010_1X30:
256f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_RGB121212_1X36:
257f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_RGB161616_1X48:
258f5e7d251SAlgea Cao 		return true;
259f5e7d251SAlgea Cao 
260f5e7d251SAlgea Cao 	default:
261f5e7d251SAlgea Cao 		return false;
262f5e7d251SAlgea Cao 	}
263f5e7d251SAlgea Cao }
264f5e7d251SAlgea Cao 
265f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format)
266f5e7d251SAlgea Cao {
267f5e7d251SAlgea Cao 	switch (bus_format) {
268f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_YUV8_1X24:
269f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_YUV10_1X30:
270f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_YUV12_1X36:
271f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_YUV16_1X48:
272f5e7d251SAlgea Cao 		return true;
273f5e7d251SAlgea Cao 
274f5e7d251SAlgea Cao 	default:
275f5e7d251SAlgea Cao 		return false;
276f5e7d251SAlgea Cao 	}
277f5e7d251SAlgea Cao }
278f5e7d251SAlgea Cao 
279f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format)
280f5e7d251SAlgea Cao {
281f5e7d251SAlgea Cao 	switch (bus_format) {
282f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYVY8_1X16:
283f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYVY10_1X20:
284f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYVY12_1X24:
285f5e7d251SAlgea Cao 		return true;
286f5e7d251SAlgea Cao 
287f5e7d251SAlgea Cao 	default:
288f5e7d251SAlgea Cao 		return false;
289f5e7d251SAlgea Cao 	}
290f5e7d251SAlgea Cao }
291f5e7d251SAlgea Cao 
292f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format)
293f5e7d251SAlgea Cao {
294f5e7d251SAlgea Cao 	switch (bus_format) {
295f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
296f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
297f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
298f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
299f5e7d251SAlgea Cao 		return true;
300f5e7d251SAlgea Cao 
301f5e7d251SAlgea Cao 	default:
302f5e7d251SAlgea Cao 		return false;
303f5e7d251SAlgea Cao 	}
304f5e7d251SAlgea Cao }
305f5e7d251SAlgea Cao 
306f5e7d251SAlgea Cao static int hdmi_bus_fmt_color_depth(unsigned int bus_format)
307f5e7d251SAlgea Cao {
308f5e7d251SAlgea Cao 	switch (bus_format) {
309f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_RGB888_1X24:
310f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_YUV8_1X24:
311f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYVY8_1X16:
312f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
313f5e7d251SAlgea Cao 		return 8;
314f5e7d251SAlgea Cao 
315f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_RGB101010_1X30:
316f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_YUV10_1X30:
317f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYVY10_1X20:
318f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
319f5e7d251SAlgea Cao 		return 10;
320f5e7d251SAlgea Cao 
321f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_RGB121212_1X36:
322f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_YUV12_1X36:
323f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYVY12_1X24:
324f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
325f5e7d251SAlgea Cao 		return 12;
326f5e7d251SAlgea Cao 
327f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_RGB161616_1X48:
328f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_YUV16_1X48:
329f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
330f5e7d251SAlgea Cao 		return 16;
331f5e7d251SAlgea Cao 
332f5e7d251SAlgea Cao 	default:
333f5e7d251SAlgea Cao 		return 0;
334f5e7d251SAlgea Cao 	}
335f5e7d251SAlgea Cao }
336f5e7d251SAlgea Cao 
337f5e7d251SAlgea Cao static int is_color_space_conversion(struct dw_hdmi *hdmi)
338f5e7d251SAlgea Cao {
339b5016cf2SAlgea Cao 	struct drm_display_mode *mode =
340b5016cf2SAlgea Cao 		hdmi->edid_data.preferred_mode;
341b5016cf2SAlgea Cao 	bool is_cea_default;
342b5016cf2SAlgea Cao 
343b5016cf2SAlgea Cao 	is_cea_default = (drm_match_cea_mode(mode) > 1) &&
344b5016cf2SAlgea Cao 			 (hdmi->hdmi_data.quant_range ==
345b5016cf2SAlgea Cao 			  HDMI_QUANTIZATION_RANGE_DEFAULT);
346b5016cf2SAlgea Cao 
347b5016cf2SAlgea Cao 	/*
348b5016cf2SAlgea Cao 	 * When output is rgb limited range or default range with
349b5016cf2SAlgea Cao 	 * cea mode, csc should be enabled.
350b5016cf2SAlgea Cao 	 */
351b5016cf2SAlgea Cao 	if (hdmi->hdmi_data.enc_in_bus_format !=
352b5016cf2SAlgea Cao 	    hdmi->hdmi_data.enc_out_bus_format ||
353b5016cf2SAlgea Cao 	    ((hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_LIMITED ||
354b5016cf2SAlgea Cao 	      is_cea_default) &&
355b5016cf2SAlgea Cao 	     hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format)))
356b5016cf2SAlgea Cao 		return 1;
357b5016cf2SAlgea Cao 
358b5016cf2SAlgea Cao 	return 0;
359f5e7d251SAlgea Cao }
360f5e7d251SAlgea Cao 
361f5e7d251SAlgea Cao static int is_color_space_decimation(struct dw_hdmi *hdmi)
362f5e7d251SAlgea Cao {
363f5e7d251SAlgea Cao 	if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
364f5e7d251SAlgea Cao 		return 0;
365f5e7d251SAlgea Cao 
366f5e7d251SAlgea Cao 	if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format) ||
367f5e7d251SAlgea Cao 	    hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_in_bus_format))
368f5e7d251SAlgea Cao 		return 1;
369f5e7d251SAlgea Cao 
370f5e7d251SAlgea Cao 	return 0;
371f5e7d251SAlgea Cao }
372f5e7d251SAlgea Cao 
373f5e7d251SAlgea Cao static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi,
374f5e7d251SAlgea Cao 				       unsigned char bit)
375f5e7d251SAlgea Cao {
376f5e7d251SAlgea Cao 	hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET,
377f5e7d251SAlgea Cao 		  HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0);
378f5e7d251SAlgea Cao }
379f5e7d251SAlgea Cao 
380f5e7d251SAlgea Cao static inline void hdmi_phy_test_enable(struct dw_hdmi *hdmi,
381f5e7d251SAlgea Cao 					unsigned char bit)
382f5e7d251SAlgea Cao {
383f5e7d251SAlgea Cao 	hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET,
384f5e7d251SAlgea Cao 		  HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0);
385f5e7d251SAlgea Cao }
386f5e7d251SAlgea Cao 
387f5e7d251SAlgea Cao static inline void hdmi_phy_test_clock(struct dw_hdmi *hdmi,
388f5e7d251SAlgea Cao 				       unsigned char bit)
389f5e7d251SAlgea Cao {
390f5e7d251SAlgea Cao 	hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET,
391f5e7d251SAlgea Cao 		  HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0);
392f5e7d251SAlgea Cao }
393f5e7d251SAlgea Cao 
394f5e7d251SAlgea Cao static inline void hdmi_phy_test_din(struct dw_hdmi *hdmi,
395f5e7d251SAlgea Cao 				     unsigned char bit)
396f5e7d251SAlgea Cao {
397f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, bit, HDMI_PHY_TST1);
398f5e7d251SAlgea Cao }
399f5e7d251SAlgea Cao 
400f5e7d251SAlgea Cao static inline void hdmi_phy_test_dout(struct dw_hdmi *hdmi,
401f5e7d251SAlgea Cao 				      unsigned char bit)
402f5e7d251SAlgea Cao {
403f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, bit, HDMI_PHY_TST2);
404f5e7d251SAlgea Cao }
405f5e7d251SAlgea Cao 
4068e2bab3fSAlgea Cao static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi,
4078e2bab3fSAlgea Cao 			    unsigned char *buf, unsigned int length)
4088e2bab3fSAlgea Cao {
4098e2bab3fSAlgea Cao 	struct dw_hdmi_i2c *i2c = hdmi->i2c;
4108e2bab3fSAlgea Cao 	int interrupt = 0, i = 20;
4118e2bab3fSAlgea Cao 
4128e2bab3fSAlgea Cao 	if (!i2c->is_regaddr) {
4138e2bab3fSAlgea Cao 		printf("set read register address to 0\n");
4148e2bab3fSAlgea Cao 		i2c->slave_reg = 0x00;
4158e2bab3fSAlgea Cao 		i2c->is_regaddr = true;
4168e2bab3fSAlgea Cao 	}
4178e2bab3fSAlgea Cao 
4188e2bab3fSAlgea Cao 	while (length--) {
4198e2bab3fSAlgea Cao 		hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS);
4208e2bab3fSAlgea Cao 		if (i2c->is_segment)
4218e2bab3fSAlgea Cao 			hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ_EXT,
4228e2bab3fSAlgea Cao 				    HDMI_I2CM_OPERATION);
4238e2bab3fSAlgea Cao 		else
4248e2bab3fSAlgea Cao 			hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ,
4258e2bab3fSAlgea Cao 				    HDMI_I2CM_OPERATION);
4268e2bab3fSAlgea Cao 
4278e2bab3fSAlgea Cao 		while (i--) {
4288e2bab3fSAlgea Cao 			udelay(1000);
4298e2bab3fSAlgea Cao 			interrupt = hdmi_readb(hdmi, HDMI_IH_I2CM_STAT0);
4308e2bab3fSAlgea Cao 			if (interrupt)
4318e2bab3fSAlgea Cao 				hdmi_writeb(hdmi, interrupt,
4328e2bab3fSAlgea Cao 					    HDMI_IH_I2CM_STAT0);
4338e2bab3fSAlgea Cao 			if (interrupt & (m_SCDC_READREQ | m_I2CM_DONE |
4348e2bab3fSAlgea Cao 					 m_I2CM_ERROR))
4358e2bab3fSAlgea Cao 				break;
4368e2bab3fSAlgea Cao 		}
4378e2bab3fSAlgea Cao 
4388e2bab3fSAlgea Cao 		if (!interrupt) {
4398e2bab3fSAlgea Cao 			printf("[%s] i2c read reg[0x%02x] no interrupt\n",
4408e2bab3fSAlgea Cao 			       __func__, i2c->slave_reg);
4418e2bab3fSAlgea Cao 			return -EAGAIN;
4428e2bab3fSAlgea Cao 		}
4438e2bab3fSAlgea Cao 
4448e2bab3fSAlgea Cao 		/* Check for error condition on the bus */
4458e2bab3fSAlgea Cao 		if (interrupt & HDMI_IH_I2CM_STAT0_ERROR) {
4468e2bab3fSAlgea Cao 			printf("[%s] read reg[0x%02x] data error:0x%02x\n",
4478e2bab3fSAlgea Cao 			       __func__, i2c->slave_reg, interrupt);
4488e2bab3fSAlgea Cao 			return -EIO;
4498e2bab3fSAlgea Cao 		}
4508e2bab3fSAlgea Cao 
4518e2bab3fSAlgea Cao 		i = 20;
4528e2bab3fSAlgea Cao 		*buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI);
4538e2bab3fSAlgea Cao 	}
4548e2bab3fSAlgea Cao 	i2c->is_segment = false;
4558e2bab3fSAlgea Cao 
4568e2bab3fSAlgea Cao 	return 0;
4578e2bab3fSAlgea Cao }
4588e2bab3fSAlgea Cao 
4598e2bab3fSAlgea Cao static int dw_hdmi_i2c_write(struct dw_hdmi *hdmi,
4608e2bab3fSAlgea Cao 			     unsigned char *buf, unsigned int length)
4618e2bab3fSAlgea Cao {
4628e2bab3fSAlgea Cao 	struct dw_hdmi_i2c *i2c = hdmi->i2c;
4638e2bab3fSAlgea Cao 	int i = 20;
4648e2bab3fSAlgea Cao 	u8 interrupt = 0;
4658e2bab3fSAlgea Cao 
4668e2bab3fSAlgea Cao 	if (!i2c->is_regaddr) {
4678e2bab3fSAlgea Cao 		/* Use the first write byte as register address */
4688e2bab3fSAlgea Cao 		i2c->slave_reg = buf[0];
4698e2bab3fSAlgea Cao 		length--;
4708e2bab3fSAlgea Cao 		buf++;
4718e2bab3fSAlgea Cao 		i2c->is_regaddr = true;
4728e2bab3fSAlgea Cao 	}
4738e2bab3fSAlgea Cao 
4748e2bab3fSAlgea Cao 	while (length--) {
4758e2bab3fSAlgea Cao 		hdmi_writeb(hdmi, *buf++, HDMI_I2CM_DATAO);
4768e2bab3fSAlgea Cao 		hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS);
4778e2bab3fSAlgea Cao 		hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_WRITE,
4788e2bab3fSAlgea Cao 			    HDMI_I2CM_OPERATION);
4798e2bab3fSAlgea Cao 
4808e2bab3fSAlgea Cao 		while (i--) {
4818e2bab3fSAlgea Cao 			udelay(1000);
4828e2bab3fSAlgea Cao 			interrupt = hdmi_readb(hdmi, HDMI_IH_I2CM_STAT0);
4838e2bab3fSAlgea Cao 			if (interrupt)
4848e2bab3fSAlgea Cao 				hdmi_writeb(hdmi,
4858e2bab3fSAlgea Cao 					    interrupt, HDMI_IH_I2CM_STAT0);
4868e2bab3fSAlgea Cao 
4878e2bab3fSAlgea Cao 			if (interrupt & (m_SCDC_READREQ |
4888e2bab3fSAlgea Cao 					 m_I2CM_DONE | m_I2CM_ERROR))
4898e2bab3fSAlgea Cao 				break;
4908e2bab3fSAlgea Cao 		}
4918e2bab3fSAlgea Cao 
4928e2bab3fSAlgea Cao 		if ((interrupt & m_I2CM_ERROR) || (i == -1)) {
4938e2bab3fSAlgea Cao 			printf("[%s] write data error\n", __func__);
4948e2bab3fSAlgea Cao 			return -EIO;
4958e2bab3fSAlgea Cao 		} else if (interrupt & m_I2CM_DONE) {
4968e2bab3fSAlgea Cao 			printf("[%s] write offset %02x success\n",
4978e2bab3fSAlgea Cao 			       __func__, i2c->slave_reg);
4988e2bab3fSAlgea Cao 			return -EAGAIN;
4998e2bab3fSAlgea Cao 		}
5008e2bab3fSAlgea Cao 
5018e2bab3fSAlgea Cao 		i = 20;
5028e2bab3fSAlgea Cao 	}
5038e2bab3fSAlgea Cao 
5048e2bab3fSAlgea Cao 	return 0;
5058e2bab3fSAlgea Cao }
5068e2bab3fSAlgea Cao 
5078e2bab3fSAlgea Cao static int dw_hdmi_i2c_xfer(struct ddc_adapter *adap,
5088e2bab3fSAlgea Cao 			    struct i2c_msg *msgs, int num)
5098e2bab3fSAlgea Cao {
5108e2bab3fSAlgea Cao 	struct dw_hdmi *hdmi = container_of(adap, struct dw_hdmi, adap);
5118e2bab3fSAlgea Cao 	struct dw_hdmi_i2c *i2c = hdmi->i2c;
5128e2bab3fSAlgea Cao 	u8 addr = msgs[0].addr;
5138e2bab3fSAlgea Cao 	int i, ret = 0;
5148e2bab3fSAlgea Cao 
5158e2bab3fSAlgea Cao 	printf("xfer: num: %d, addr: %#x\n", num, addr);
5168e2bab3fSAlgea Cao 	for (i = 0; i < num; i++) {
5178e2bab3fSAlgea Cao 		if (msgs[i].len == 0) {
5188e2bab3fSAlgea Cao 			printf("unsupported transfer %d/%d, no data\n",
5198e2bab3fSAlgea Cao 			       i + 1, num);
5208e2bab3fSAlgea Cao 			return -EOPNOTSUPP;
5218e2bab3fSAlgea Cao 		}
5228e2bab3fSAlgea Cao 	}
5238e2bab3fSAlgea Cao 
5248e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, 0x00, HDMI_IH_MUTE_I2CM_STAT0);
5258e2bab3fSAlgea Cao 
5268e2bab3fSAlgea Cao 	/* Set slave device address taken from the first I2C message */
5278e2bab3fSAlgea Cao 	if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1)
5288e2bab3fSAlgea Cao 		addr = DDC_ADDR;
5298e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, addr, HDMI_I2CM_SLAVE);
5308e2bab3fSAlgea Cao 
5318e2bab3fSAlgea Cao 	/* Set slave device register address on transfer */
5328e2bab3fSAlgea Cao 	i2c->is_regaddr = false;
5338e2bab3fSAlgea Cao 
5348e2bab3fSAlgea Cao 	/* Set segment pointer for I2C extended read mode operation */
5358e2bab3fSAlgea Cao 	i2c->is_segment = false;
5368e2bab3fSAlgea Cao 
5378e2bab3fSAlgea Cao 	for (i = 0; i < num; i++) {
5388e2bab3fSAlgea Cao 		debug("xfer: num: %d/%d, len: %d, flags: %#x\n",
5398e2bab3fSAlgea Cao 		      i + 1, num, msgs[i].len, msgs[i].flags);
5408e2bab3fSAlgea Cao 		if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) {
5418e2bab3fSAlgea Cao 			i2c->is_segment = true;
5428e2bab3fSAlgea Cao 			hdmi_writeb(hdmi, DDC_SEGMENT_ADDR, HDMI_I2CM_SEGADDR);
5438e2bab3fSAlgea Cao 			hdmi_writeb(hdmi, *msgs[i].buf, HDMI_I2CM_SEGPTR);
5448e2bab3fSAlgea Cao 		} else {
5458e2bab3fSAlgea Cao 			if (msgs[i].flags & I2C_M_RD)
5468e2bab3fSAlgea Cao 				ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf,
5478e2bab3fSAlgea Cao 						       msgs[i].len);
5488e2bab3fSAlgea Cao 			else
5498e2bab3fSAlgea Cao 				ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf,
5508e2bab3fSAlgea Cao 							msgs[i].len);
5518e2bab3fSAlgea Cao 		}
5528e2bab3fSAlgea Cao 		if (ret < 0)
5538e2bab3fSAlgea Cao 			break;
5548e2bab3fSAlgea Cao 	}
5558e2bab3fSAlgea Cao 
5568e2bab3fSAlgea Cao 	if (!ret)
5578e2bab3fSAlgea Cao 		ret = num;
5588e2bab3fSAlgea Cao 
5598e2bab3fSAlgea Cao 	/* Mute DONE and ERROR interrupts */
5608e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
5618e2bab3fSAlgea Cao 		    HDMI_IH_MUTE_I2CM_STAT0);
5628e2bab3fSAlgea Cao 
5638e2bab3fSAlgea Cao 	return ret;
5648e2bab3fSAlgea Cao }
5658e2bab3fSAlgea Cao 
566f5e7d251SAlgea Cao static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec)
567f5e7d251SAlgea Cao {
568f5e7d251SAlgea Cao 	u32 val;
569f5e7d251SAlgea Cao 
570f5e7d251SAlgea Cao 	while ((val = hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) {
571f5e7d251SAlgea Cao 		if (msec-- == 0)
572f5e7d251SAlgea Cao 			return false;
573f5e7d251SAlgea Cao 		udelay(1000);
574f5e7d251SAlgea Cao 	}
575f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, val, HDMI_IH_I2CMPHY_STAT0);
576f5e7d251SAlgea Cao 
577f5e7d251SAlgea Cao 	return true;
578f5e7d251SAlgea Cao }
579f5e7d251SAlgea Cao 
580f5e7d251SAlgea Cao static void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
581f5e7d251SAlgea Cao 				  unsigned char addr)
582f5e7d251SAlgea Cao {
583f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
584f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
585f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, (unsigned char)(data >> 8),
586f5e7d251SAlgea Cao 		    HDMI_PHY_I2CM_DATAO_1_ADDR);
587f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, (unsigned char)(data >> 0),
588f5e7d251SAlgea Cao 		    HDMI_PHY_I2CM_DATAO_0_ADDR);
589f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
590f5e7d251SAlgea Cao 		    HDMI_PHY_I2CM_OPERATION_ADDR);
591f5e7d251SAlgea Cao 	hdmi_phy_wait_i2c_done(hdmi, 1000);
592f5e7d251SAlgea Cao }
593f5e7d251SAlgea Cao 
594f5e7d251SAlgea Cao static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
595f5e7d251SAlgea Cao {
596f5e7d251SAlgea Cao 	hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0,
597f5e7d251SAlgea Cao 			 HDMI_PHY_CONF0_PDZ_OFFSET,
598f5e7d251SAlgea Cao 			 HDMI_PHY_CONF0_PDZ_MASK);
599f5e7d251SAlgea Cao }
600f5e7d251SAlgea Cao 
601f5e7d251SAlgea Cao static void dw_hdmi_phy_enable_tmds(struct dw_hdmi *hdmi, u8 enable)
602f5e7d251SAlgea Cao {
603f5e7d251SAlgea Cao 	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
604f5e7d251SAlgea Cao 			 HDMI_PHY_CONF0_ENTMDS_OFFSET,
605f5e7d251SAlgea Cao 			 HDMI_PHY_CONF0_ENTMDS_MASK);
606f5e7d251SAlgea Cao }
607f5e7d251SAlgea Cao 
608f5e7d251SAlgea Cao static void dw_hdmi_phy_enable_svsret(struct dw_hdmi *hdmi, u8 enable)
609f5e7d251SAlgea Cao {
610f5e7d251SAlgea Cao 	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
611f5e7d251SAlgea Cao 			 HDMI_PHY_CONF0_SVSRET_OFFSET,
612f5e7d251SAlgea Cao 			 HDMI_PHY_CONF0_SVSRET_MASK);
613f5e7d251SAlgea Cao }
614f5e7d251SAlgea Cao 
615f5e7d251SAlgea Cao static void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable)
616f5e7d251SAlgea Cao {
617f5e7d251SAlgea Cao 	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
618f5e7d251SAlgea Cao 			 HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
619f5e7d251SAlgea Cao 			 HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
620f5e7d251SAlgea Cao }
621f5e7d251SAlgea Cao 
622f5e7d251SAlgea Cao static void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable)
623f5e7d251SAlgea Cao {
624f5e7d251SAlgea Cao 	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
625f5e7d251SAlgea Cao 			 HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
626f5e7d251SAlgea Cao 			 HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
627f5e7d251SAlgea Cao }
628f5e7d251SAlgea Cao 
629f5e7d251SAlgea Cao static void dw_hdmi_phy_sel_data_en_pol(struct dw_hdmi *hdmi, u8 enable)
630f5e7d251SAlgea Cao {
631f5e7d251SAlgea Cao 	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
632f5e7d251SAlgea Cao 			 HDMI_PHY_CONF0_SELDATAENPOL_OFFSET,
633f5e7d251SAlgea Cao 			 HDMI_PHY_CONF0_SELDATAENPOL_MASK);
634f5e7d251SAlgea Cao }
635f5e7d251SAlgea Cao 
636f5e7d251SAlgea Cao static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable)
637f5e7d251SAlgea Cao {
638f5e7d251SAlgea Cao 	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
639f5e7d251SAlgea Cao 			 HDMI_PHY_CONF0_SELDIPIF_OFFSET,
640f5e7d251SAlgea Cao 			 HDMI_PHY_CONF0_SELDIPIF_MASK);
641f5e7d251SAlgea Cao }
642f5e7d251SAlgea Cao 
643f5e7d251SAlgea Cao static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi)
644f5e7d251SAlgea Cao {
645f5e7d251SAlgea Cao 	const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
646f5e7d251SAlgea Cao 	unsigned int i;
647f5e7d251SAlgea Cao 	u16 val;
648f5e7d251SAlgea Cao 
649f5e7d251SAlgea Cao 	if (phy->gen == 1) {
650f5e7d251SAlgea Cao 		dw_hdmi_phy_enable_tmds(hdmi, 0);
651f5e7d251SAlgea Cao 		dw_hdmi_phy_enable_powerdown(hdmi, true);
652f5e7d251SAlgea Cao 		return;
653f5e7d251SAlgea Cao 	}
654f5e7d251SAlgea Cao 
655f5e7d251SAlgea Cao 	dw_hdmi_phy_gen2_txpwron(hdmi, 0);
656f5e7d251SAlgea Cao 
657f5e7d251SAlgea Cao 	/*
658f5e7d251SAlgea Cao 	 * Wait for TX_PHY_LOCK to be deasserted to indicate that the PHY went
659f5e7d251SAlgea Cao 	 * to low power mode.
660f5e7d251SAlgea Cao 	 */
661f5e7d251SAlgea Cao 	for (i = 0; i < 5; ++i) {
662f5e7d251SAlgea Cao 		val = hdmi_readb(hdmi, HDMI_PHY_STAT0);
663f5e7d251SAlgea Cao 		if (!(val & HDMI_PHY_TX_PHY_LOCK))
664f5e7d251SAlgea Cao 			break;
665f5e7d251SAlgea Cao 
666f5e7d251SAlgea Cao 		udelay(2000);
667f5e7d251SAlgea Cao 	}
668f5e7d251SAlgea Cao 
669f5e7d251SAlgea Cao 	if (val & HDMI_PHY_TX_PHY_LOCK)
670f5e7d251SAlgea Cao 		printf("PHY failed to power down\n");
671f5e7d251SAlgea Cao 	else
672f5e7d251SAlgea Cao 		printf("PHY powered down in %u iterations\n", i);
673f5e7d251SAlgea Cao 
674f5e7d251SAlgea Cao 	dw_hdmi_phy_gen2_pddq(hdmi, 1);
675f5e7d251SAlgea Cao }
676f5e7d251SAlgea Cao 
677f5e7d251SAlgea Cao static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi)
678f5e7d251SAlgea Cao {
679f5e7d251SAlgea Cao 	const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
680f5e7d251SAlgea Cao 	unsigned int i;
681f5e7d251SAlgea Cao 	u8 val;
682f5e7d251SAlgea Cao 
683f5e7d251SAlgea Cao 	if (phy->gen == 1) {
684f5e7d251SAlgea Cao 		dw_hdmi_phy_enable_powerdown(hdmi, false);
685f5e7d251SAlgea Cao 
686f5e7d251SAlgea Cao 		/* Toggle TMDS enable. */
687f5e7d251SAlgea Cao 		dw_hdmi_phy_enable_tmds(hdmi, 0);
688f5e7d251SAlgea Cao 		dw_hdmi_phy_enable_tmds(hdmi, 1);
689f5e7d251SAlgea Cao 		return 0;
690f5e7d251SAlgea Cao 	}
691f5e7d251SAlgea Cao 
692f5e7d251SAlgea Cao 	dw_hdmi_phy_gen2_txpwron(hdmi, 1);
693f5e7d251SAlgea Cao 	dw_hdmi_phy_gen2_pddq(hdmi, 0);
694f5e7d251SAlgea Cao 
695f5e7d251SAlgea Cao 	/* Wait for PHY PLL lock */
696f5e7d251SAlgea Cao 	for (i = 0; i < 5; ++i) {
697f5e7d251SAlgea Cao 		val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
698f5e7d251SAlgea Cao 		if (val)
699f5e7d251SAlgea Cao 			break;
700f5e7d251SAlgea Cao 
701f5e7d251SAlgea Cao 		udelay(2000);
702f5e7d251SAlgea Cao 	}
703f5e7d251SAlgea Cao 
704f5e7d251SAlgea Cao 	if (!val) {
705f5e7d251SAlgea Cao 		printf("PHY PLL failed to lock\n");
706f5e7d251SAlgea Cao 		return -ETIMEDOUT;
707f5e7d251SAlgea Cao 	}
708f5e7d251SAlgea Cao 	printf("PHY PLL locked %u iterations\n", i);
7098e2bab3fSAlgea Cao 
710f5e7d251SAlgea Cao 	return 0;
711f5e7d251SAlgea Cao }
712f5e7d251SAlgea Cao 
713f5e7d251SAlgea Cao /*
714f5e7d251SAlgea Cao  * PHY configuration function for the DWC HDMI 3D TX PHY. Based on the available
715f5e7d251SAlgea Cao  * information the DWC MHL PHY has the same register layout and is thus also
716f5e7d251SAlgea Cao  * supported by this function.
717f5e7d251SAlgea Cao  */
718f5e7d251SAlgea Cao static
719f5e7d251SAlgea Cao int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
720f5e7d251SAlgea Cao 				      const struct dw_hdmi_plat_data *pdata,
721f5e7d251SAlgea Cao 				      unsigned long mpixelclock)
722f5e7d251SAlgea Cao {
723f5e7d251SAlgea Cao 	const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
724f5e7d251SAlgea Cao 	const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
725f5e7d251SAlgea Cao 	const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
7268e2bab3fSAlgea Cao 	unsigned int tmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
7278e2bab3fSAlgea Cao 	unsigned int depth =
7288e2bab3fSAlgea Cao 		hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
7298e2bab3fSAlgea Cao 
7308e2bab3fSAlgea Cao 	if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) &&
7318e2bab3fSAlgea Cao 	    pdata->mpll_cfg_420)
7328e2bab3fSAlgea Cao 		mpll_config = pdata->mpll_cfg_420;
733f5e7d251SAlgea Cao 
734f5e7d251SAlgea Cao 	/* PLL/MPLL Cfg - always match on final entry */
735f5e7d251SAlgea Cao 	for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
736f5e7d251SAlgea Cao 		if (mpixelclock <= mpll_config->mpixelclock)
737f5e7d251SAlgea Cao 			break;
738f5e7d251SAlgea Cao 
739f5e7d251SAlgea Cao 	for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
7408e2bab3fSAlgea Cao 		if (tmdsclock <= curr_ctrl->mpixelclock)
741f5e7d251SAlgea Cao 			break;
742f5e7d251SAlgea Cao 
743f5e7d251SAlgea Cao 	for (; phy_config->mpixelclock != ~0UL; phy_config++)
7448e2bab3fSAlgea Cao 		if (tmdsclock <= phy_config->mpixelclock)
745f5e7d251SAlgea Cao 			break;
746f5e7d251SAlgea Cao 
747f5e7d251SAlgea Cao 	if (mpll_config->mpixelclock == ~0UL ||
748f5e7d251SAlgea Cao 	    curr_ctrl->mpixelclock == ~0UL ||
749f5e7d251SAlgea Cao 	    phy_config->mpixelclock == ~0UL)
750f5e7d251SAlgea Cao 		return -EINVAL;
751f5e7d251SAlgea Cao 
7528e2bab3fSAlgea Cao 	if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
7538e2bab3fSAlgea Cao 		depth = fls(depth - 8);
754f5e7d251SAlgea Cao 	else
7558e2bab3fSAlgea Cao 		depth = 0;
7568e2bab3fSAlgea Cao 	if (depth)
7578e2bab3fSAlgea Cao 		depth--;
7588e2bab3fSAlgea Cao 
7598e2bab3fSAlgea Cao 	dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].cpce,
760f5e7d251SAlgea Cao 			      HDMI_3D_TX_PHY_CPCE_CTRL);
7618e2bab3fSAlgea Cao 
7628e2bab3fSAlgea Cao 	dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].gmp,
763f5e7d251SAlgea Cao 			      HDMI_3D_TX_PHY_GMPCTRL);
7648e2bab3fSAlgea Cao 	dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[depth],
765f5e7d251SAlgea Cao 			      HDMI_3D_TX_PHY_CURRCTRL);
766f5e7d251SAlgea Cao 
767f5e7d251SAlgea Cao 	dw_hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL);
768f5e7d251SAlgea Cao 	dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK,
769f5e7d251SAlgea Cao 			      HDMI_3D_TX_PHY_MSM_CTRL);
770f5e7d251SAlgea Cao 
7718e2bab3fSAlgea Cao 	dw_hdmi_phy_i2c_write(hdmi, phy_config->term, HDMI_3D_TX_PHY_TXTERM);
7728e2bab3fSAlgea Cao 	dw_hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr,
773f5e7d251SAlgea Cao 			      HDMI_3D_TX_PHY_CKSYMTXCTRL);
7748e2bab3fSAlgea Cao 	dw_hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr,
775f5e7d251SAlgea Cao 			      HDMI_3D_TX_PHY_VLEVCTRL);
776f5e7d251SAlgea Cao 
777f5e7d251SAlgea Cao 	return 0;
778f5e7d251SAlgea Cao }
779f5e7d251SAlgea Cao 
780f5e7d251SAlgea Cao static const struct dw_hdmi_phy_data dw_hdmi_phys[] = {
781f5e7d251SAlgea Cao 	{
782f5e7d251SAlgea Cao 		.type = DW_HDMI_PHY_DWC_HDMI_TX_PHY,
783f5e7d251SAlgea Cao 		.name = "DWC HDMI TX PHY",
784f5e7d251SAlgea Cao 		.gen = 1,
785f5e7d251SAlgea Cao 	}, {
786f5e7d251SAlgea Cao 		.type = DW_HDMI_PHY_DWC_MHL_PHY_HEAC,
787f5e7d251SAlgea Cao 		.name = "DWC MHL PHY + HEAC PHY",
788f5e7d251SAlgea Cao 		.gen = 2,
789f5e7d251SAlgea Cao 		.has_svsret = true,
790f5e7d251SAlgea Cao 		.configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
791f5e7d251SAlgea Cao 	}, {
792f5e7d251SAlgea Cao 		.type = DW_HDMI_PHY_DWC_MHL_PHY,
793f5e7d251SAlgea Cao 		.name = "DWC MHL PHY",
794f5e7d251SAlgea Cao 		.gen = 2,
795f5e7d251SAlgea Cao 		.has_svsret = true,
796f5e7d251SAlgea Cao 		.configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
797f5e7d251SAlgea Cao 	}, {
798f5e7d251SAlgea Cao 		.type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY_HEAC,
799f5e7d251SAlgea Cao 		.name = "DWC HDMI 3D TX PHY + HEAC PHY",
800f5e7d251SAlgea Cao 		.gen = 2,
801f5e7d251SAlgea Cao 		.configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
802f5e7d251SAlgea Cao 	}, {
803f5e7d251SAlgea Cao 		.type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY,
804f5e7d251SAlgea Cao 		.name = "DWC HDMI 3D TX PHY",
805f5e7d251SAlgea Cao 		.gen = 2,
806f5e7d251SAlgea Cao 		.configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
807f5e7d251SAlgea Cao 	}, {
808f5e7d251SAlgea Cao 		.type = DW_HDMI_PHY_DWC_HDMI20_TX_PHY,
809f5e7d251SAlgea Cao 		.name = "DWC HDMI 2.0 TX PHY",
810f5e7d251SAlgea Cao 		.gen = 2,
811f5e7d251SAlgea Cao 		.has_svsret = true,
812f5e7d251SAlgea Cao 		.configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
813f5e7d251SAlgea Cao 	}, {
814f5e7d251SAlgea Cao 		.type = DW_HDMI_PHY_VENDOR_PHY,
815f5e7d251SAlgea Cao 		.name = "Vendor PHY",
816f5e7d251SAlgea Cao 	}
817f5e7d251SAlgea Cao };
818f5e7d251SAlgea Cao 
819f5e7d251SAlgea Cao static int rockchip_dw_hdmi_scrambling_enable(struct dw_hdmi *hdmi,
820f5e7d251SAlgea Cao 					      int enable)
821f5e7d251SAlgea Cao {
8228e2bab3fSAlgea Cao 	u8 stat;
823f5e7d251SAlgea Cao 
8248e2bab3fSAlgea Cao 	drm_scdc_readb(&hdmi->adap, SCDC_TMDS_CONFIG, &stat);
8258e2bab3fSAlgea Cao 
826f5e7d251SAlgea Cao 	if (stat < 0) {
827f5e7d251SAlgea Cao 		debug("Failed to read tmds config\n");
828f5e7d251SAlgea Cao 		return false;
829f5e7d251SAlgea Cao 	}
830f5e7d251SAlgea Cao 
831f5e7d251SAlgea Cao 	if (enable == 1) {
832f5e7d251SAlgea Cao 		/* Write on Rx the bit Scrambling_Enable, register 0x20 */
833f5e7d251SAlgea Cao 		stat |= SCDC_SCRAMBLING_ENABLE;
8348e2bab3fSAlgea Cao 		drm_scdc_writeb(&hdmi->adap, SCDC_TMDS_CONFIG, stat);
835f5e7d251SAlgea Cao 		/* TMDS software reset request */
836f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ,
837f5e7d251SAlgea Cao 			    HDMI_MC_SWRSTZ);
838f5e7d251SAlgea Cao 		/* Enable/Disable Scrambling */
839f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, 1, HDMI_FC_SCRAMBLER_CTRL);
840f5e7d251SAlgea Cao 	} else {
841f5e7d251SAlgea Cao 		/* Enable/Disable Scrambling */
842f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, 0, HDMI_FC_SCRAMBLER_CTRL);
843f5e7d251SAlgea Cao 		/* TMDS software reset request */
844f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ,
845f5e7d251SAlgea Cao 			    HDMI_MC_SWRSTZ);
846f5e7d251SAlgea Cao 		/* Write on Rx the bit Scrambling_Enable, register 0x20 */
847f5e7d251SAlgea Cao 		stat &= ~SCDC_SCRAMBLING_ENABLE;
8488e2bab3fSAlgea Cao 		drm_scdc_writeb(&hdmi->adap, SCDC_TMDS_CONFIG, stat);
849f5e7d251SAlgea Cao 	}
850f5e7d251SAlgea Cao 
851f5e7d251SAlgea Cao 	return 0;
852f5e7d251SAlgea Cao }
853f5e7d251SAlgea Cao 
854f5e7d251SAlgea Cao static void rockchip_dw_hdmi_scdc_set_tmds_rate(struct dw_hdmi *hdmi)
855f5e7d251SAlgea Cao {
8568e2bab3fSAlgea Cao 	u8 stat;
857f5e7d251SAlgea Cao 
8588e2bab3fSAlgea Cao 	drm_scdc_readb(&hdmi->adap, SCDC_TMDS_CONFIG, &stat);
8598e2bab3fSAlgea Cao 	if (hdmi->hdmi_data.video_mode.mtmdsclock > 340000000)
860f5e7d251SAlgea Cao 		stat |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
861f5e7d251SAlgea Cao 	else
862f5e7d251SAlgea Cao 		stat &= ~SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
8638e2bab3fSAlgea Cao 	drm_scdc_writeb(&hdmi->adap, SCDC_TMDS_CONFIG, stat);
864f5e7d251SAlgea Cao }
865f5e7d251SAlgea Cao 
866f5e7d251SAlgea Cao static int hdmi_phy_configure(struct dw_hdmi *hdmi)
867f5e7d251SAlgea Cao {
868f5e7d251SAlgea Cao 	const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
869f5e7d251SAlgea Cao 	const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
870f5e7d251SAlgea Cao 	unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
8718e2bab3fSAlgea Cao 	unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
8728e2bab3fSAlgea Cao 	int ret;
873f5e7d251SAlgea Cao 
874f5e7d251SAlgea Cao 	dw_hdmi_phy_power_off(hdmi);
875f5e7d251SAlgea Cao 
876f5e7d251SAlgea Cao 	/* Control for TMDS Bit Period/TMDS Clock-Period Ratio */
877f5e7d251SAlgea Cao 	if (hdmi->edid_data.display_info.hdmi.scdc.supported)
878f5e7d251SAlgea Cao 		rockchip_dw_hdmi_scdc_set_tmds_rate(hdmi);
879f5e7d251SAlgea Cao 
880f5e7d251SAlgea Cao 	/* Leave low power consumption mode by asserting SVSRET. */
881f5e7d251SAlgea Cao 	if (phy->has_svsret)
882f5e7d251SAlgea Cao 		dw_hdmi_phy_enable_svsret(hdmi, 1);
883f5e7d251SAlgea Cao 
884f5e7d251SAlgea Cao 	/* PHY reset. The reset signal is active high on Gen2 PHYs. */
885f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ);
886f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ);
887f5e7d251SAlgea Cao 
888f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
889f5e7d251SAlgea Cao 
890f5e7d251SAlgea Cao 	hdmi_phy_test_clear(hdmi, 1);
891f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
892f5e7d251SAlgea Cao 		    HDMI_PHY_I2CM_SLAVE_ADDR);
893f5e7d251SAlgea Cao 	hdmi_phy_test_clear(hdmi, 0);
894f5e7d251SAlgea Cao 
895f5e7d251SAlgea Cao 	/* Write to the PHY as configured by the platform */
896f5e7d251SAlgea Cao 	if (pdata->configure_phy)
897f5e7d251SAlgea Cao 		ret = pdata->configure_phy(hdmi, pdata, mpixelclock);
898f5e7d251SAlgea Cao 	else
899f5e7d251SAlgea Cao 		ret = phy->configure(hdmi, pdata, mpixelclock);
900f5e7d251SAlgea Cao 	if (ret) {
901f5e7d251SAlgea Cao 		printf("PHY configuration failed (clock %lu)\n",
902f5e7d251SAlgea Cao 		       mpixelclock);
903f5e7d251SAlgea Cao 		return ret;
904f5e7d251SAlgea Cao 	}
905f5e7d251SAlgea Cao 
906f5e7d251SAlgea Cao 	/* Wait for resuming transmission of TMDS clock and data */
9078e2bab3fSAlgea Cao 	if (mtmdsclock > 340000000)
908f5e7d251SAlgea Cao 		mdelay(100);
909f5e7d251SAlgea Cao 
910f5e7d251SAlgea Cao 	return dw_hdmi_phy_power_on(hdmi);
911f5e7d251SAlgea Cao }
912f5e7d251SAlgea Cao 
9138e2bab3fSAlgea Cao static int dw_hdmi_phy_init(struct dw_hdmi *hdmi,
9148e2bab3fSAlgea Cao 			    void *data)
915f5e7d251SAlgea Cao {
916f5e7d251SAlgea Cao 	int i, ret;
917f5e7d251SAlgea Cao 
918f5e7d251SAlgea Cao 	/* HDMI Phy spec says to do the phy initialization sequence twice */
919f5e7d251SAlgea Cao 	for (i = 0; i < 2; i++) {
920f5e7d251SAlgea Cao 		dw_hdmi_phy_sel_data_en_pol(hdmi, 1);
921f5e7d251SAlgea Cao 		dw_hdmi_phy_sel_interface_control(hdmi, 0);
922f5e7d251SAlgea Cao 		ret = hdmi_phy_configure(hdmi);
923f5e7d251SAlgea Cao 		if (ret)
924f5e7d251SAlgea Cao 			return ret;
925f5e7d251SAlgea Cao 	}
926f5e7d251SAlgea Cao 
927f5e7d251SAlgea Cao 	return 0;
928f5e7d251SAlgea Cao }
929f5e7d251SAlgea Cao 
9308e2bab3fSAlgea Cao static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi,
9318e2bab3fSAlgea Cao 				void *data)
932f5e7d251SAlgea Cao {
933f5e7d251SAlgea Cao 	dw_hdmi_phy_power_off(hdmi);
934f5e7d251SAlgea Cao }
935f5e7d251SAlgea Cao 
9368e2bab3fSAlgea Cao static enum drm_connector_status
9378e2bab3fSAlgea Cao dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, void *data)
938f5e7d251SAlgea Cao {
939f5e7d251SAlgea Cao 	return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
940f5e7d251SAlgea Cao 		connector_status_connected : connector_status_disconnected;
941f5e7d251SAlgea Cao }
942f5e7d251SAlgea Cao 
943f5e7d251SAlgea Cao static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = {
944f5e7d251SAlgea Cao 	.init = dw_hdmi_phy_init,
945f5e7d251SAlgea Cao 	.disable = dw_hdmi_phy_disable,
946f5e7d251SAlgea Cao 	.read_hpd = dw_hdmi_phy_read_hpd,
947f5e7d251SAlgea Cao };
948f5e7d251SAlgea Cao 
949f5e7d251SAlgea Cao static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi)
950f5e7d251SAlgea Cao {
951f5e7d251SAlgea Cao 	unsigned int i;
952f5e7d251SAlgea Cao 	u8 phy_type;
953f5e7d251SAlgea Cao 
954f5e7d251SAlgea Cao 	phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID);
955f5e7d251SAlgea Cao 
956f5e7d251SAlgea Cao 	/*
957f5e7d251SAlgea Cao 	 * RK3228 and RK3328 phy_type is DW_HDMI_PHY_DWC_HDMI20_TX_PHY,
958f5e7d251SAlgea Cao 	 * but it has a vedor phy.
959f5e7d251SAlgea Cao 	 */
960f5e7d251SAlgea Cao 	if (phy_type == DW_HDMI_PHY_VENDOR_PHY ||
961f5e7d251SAlgea Cao 	    hdmi->dev_type == RK3328_HDMI ||
962f5e7d251SAlgea Cao 	    hdmi->dev_type == RK3228_HDMI) {
963f5e7d251SAlgea Cao 		/* Vendor PHYs require support from the glue layer. */
964f5e7d251SAlgea Cao 		if (!hdmi->plat_data->phy_ops || !hdmi->plat_data->phy_name) {
965f5e7d251SAlgea Cao 			printf(
966f5e7d251SAlgea Cao 				"Vendor HDMI PHY not supported by glue layer\n");
967f5e7d251SAlgea Cao 			return -ENODEV;
968f5e7d251SAlgea Cao 		}
969f5e7d251SAlgea Cao 
970f5e7d251SAlgea Cao 		hdmi->phy.ops = hdmi->plat_data->phy_ops;
971f5e7d251SAlgea Cao 		hdmi->phy.data = hdmi->plat_data->phy_data;
972f5e7d251SAlgea Cao 		hdmi->phy.name = hdmi->plat_data->phy_name;
973f5e7d251SAlgea Cao 		return 0;
974f5e7d251SAlgea Cao 	}
975f5e7d251SAlgea Cao 
976f5e7d251SAlgea Cao 	/* Synopsys PHYs are handled internally. */
977f5e7d251SAlgea Cao 	for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); ++i) {
978f5e7d251SAlgea Cao 		if (dw_hdmi_phys[i].type == phy_type) {
979f5e7d251SAlgea Cao 			hdmi->phy.ops = &dw_hdmi_synopsys_phy_ops;
980f5e7d251SAlgea Cao 			hdmi->phy.name = dw_hdmi_phys[i].name;
981f5e7d251SAlgea Cao 			hdmi->phy.data = (void *)&dw_hdmi_phys[i];
982f5e7d251SAlgea Cao 
983f5e7d251SAlgea Cao 			if (!dw_hdmi_phys[i].configure &&
984f5e7d251SAlgea Cao 			    !hdmi->plat_data->configure_phy) {
985f5e7d251SAlgea Cao 				printf("%s requires platform support\n",
986f5e7d251SAlgea Cao 				       hdmi->phy.name);
987f5e7d251SAlgea Cao 				return -ENODEV;
988f5e7d251SAlgea Cao 			}
989f5e7d251SAlgea Cao 
990f5e7d251SAlgea Cao 			return 0;
991f5e7d251SAlgea Cao 		}
992f5e7d251SAlgea Cao 	}
993f5e7d251SAlgea Cao 
994f5e7d251SAlgea Cao 	printf("Unsupported HDMI PHY type (%02x)\n", phy_type);
995f5e7d251SAlgea Cao 	return -ENODEV;
996f5e7d251SAlgea Cao }
997f5e7d251SAlgea Cao 
9988e2bab3fSAlgea Cao static unsigned int
9998e2bab3fSAlgea Cao hdmi_get_tmdsclock(struct dw_hdmi *hdmi, unsigned long mpixelclock)
10008e2bab3fSAlgea Cao {
10018e2bab3fSAlgea Cao 	unsigned int tmdsclock = mpixelclock;
10028e2bab3fSAlgea Cao 	unsigned int depth =
10038e2bab3fSAlgea Cao 		hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
10048e2bab3fSAlgea Cao 
10058e2bab3fSAlgea Cao 	if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) {
10068e2bab3fSAlgea Cao 		switch (depth) {
10078e2bab3fSAlgea Cao 		case 16:
10088e2bab3fSAlgea Cao 			tmdsclock = mpixelclock * 2;
10098e2bab3fSAlgea Cao 			break;
10108e2bab3fSAlgea Cao 		case 12:
10118e2bab3fSAlgea Cao 			tmdsclock = mpixelclock * 3 / 2;
10128e2bab3fSAlgea Cao 			break;
10138e2bab3fSAlgea Cao 		case 10:
10148e2bab3fSAlgea Cao 			tmdsclock = mpixelclock * 5 / 4;
10158e2bab3fSAlgea Cao 			break;
10168e2bab3fSAlgea Cao 		default:
10178e2bab3fSAlgea Cao 			break;
10188e2bab3fSAlgea Cao 		}
10198e2bab3fSAlgea Cao 	}
10208e2bab3fSAlgea Cao 
10218e2bab3fSAlgea Cao 	return tmdsclock;
10228e2bab3fSAlgea Cao }
10238e2bab3fSAlgea Cao 
1024f5e7d251SAlgea Cao static void hdmi_av_composer(struct dw_hdmi *hdmi,
1025f5e7d251SAlgea Cao 			     const struct drm_display_mode *mode)
1026f5e7d251SAlgea Cao {
10278e2bab3fSAlgea Cao 	u8 bytes = 0, inv_val = 0;
1028f5e7d251SAlgea Cao 	struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
1029f5e7d251SAlgea Cao 	struct drm_hdmi_info *hdmi_info = &hdmi->edid_data.display_info.hdmi;
10308e2bab3fSAlgea Cao 	int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
1031f5e7d251SAlgea Cao 	unsigned int hdisplay, vdisplay;
1032f5e7d251SAlgea Cao 
10338e2bab3fSAlgea Cao 	vmode->mpixelclock = mode->crtc_clock * 1000;
1034f5e7d251SAlgea Cao 	if ((mode->flags & DRM_MODE_FLAG_3D_MASK) ==
1035f5e7d251SAlgea Cao 		DRM_MODE_FLAG_3D_FRAME_PACKING)
1036f5e7d251SAlgea Cao 		vmode->mpixelclock *= 2;
10378e2bab3fSAlgea Cao 	vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock);
10388e2bab3fSAlgea Cao 	if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
10398e2bab3fSAlgea Cao 		vmode->mtmdsclock /= 2;
10408e2bab3fSAlgea Cao 	printf("final pixclk = %d tmdsclk = %d\n",
10418e2bab3fSAlgea Cao 	       vmode->mpixelclock, vmode->mtmdsclock);
1042f5e7d251SAlgea Cao 
1043f5e7d251SAlgea Cao 	/* Set up HDMI_FC_INVIDCONF
1044f5e7d251SAlgea Cao 	 * fc_invidconf.HDCP_keepout must be set (1'b1)
1045f5e7d251SAlgea Cao 	 * when activate the scrambler feature.
1046f5e7d251SAlgea Cao 	 */
10478e2bab3fSAlgea Cao 	inv_val = (vmode->mtmdsclock > 340000000 ||
10488e2bab3fSAlgea Cao 		   (hdmi_info->scdc.scrambling.low_rates &&
10498e2bab3fSAlgea Cao 		   hdmi->scramble_low_rates) ?
1050f5e7d251SAlgea Cao 		   HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
1051f5e7d251SAlgea Cao 		   HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
1052f5e7d251SAlgea Cao 
1053f5e7d251SAlgea Cao 	inv_val |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
1054f5e7d251SAlgea Cao 		HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
1055f5e7d251SAlgea Cao 		HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW;
1056f5e7d251SAlgea Cao 
1057f5e7d251SAlgea Cao 	inv_val |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
1058f5e7d251SAlgea Cao 		HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH :
1059f5e7d251SAlgea Cao 		HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW;
1060f5e7d251SAlgea Cao 
1061f5e7d251SAlgea Cao 	inv_val |= (vmode->mdataenablepolarity ?
1062f5e7d251SAlgea Cao 		HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH :
1063f5e7d251SAlgea Cao 		HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW);
1064f5e7d251SAlgea Cao 
1065f5e7d251SAlgea Cao 	if (hdmi->vic == 39)
1066f5e7d251SAlgea Cao 		inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH;
1067f5e7d251SAlgea Cao 	else
1068f5e7d251SAlgea Cao 		inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
1069f5e7d251SAlgea Cao 			HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH :
1070f5e7d251SAlgea Cao 			HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW;
1071f5e7d251SAlgea Cao 
1072f5e7d251SAlgea Cao 	inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
1073f5e7d251SAlgea Cao 		HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
1074f5e7d251SAlgea Cao 		HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE;
1075f5e7d251SAlgea Cao 
1076f5e7d251SAlgea Cao 	inv_val |= hdmi->sink_is_hdmi ?
1077f5e7d251SAlgea Cao 		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
1078f5e7d251SAlgea Cao 		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE;
1079f5e7d251SAlgea Cao 
1080f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
1081f5e7d251SAlgea Cao 
1082f5e7d251SAlgea Cao 	hdisplay = mode->hdisplay;
1083f5e7d251SAlgea Cao 	hblank = mode->htotal - mode->hdisplay;
1084f5e7d251SAlgea Cao 	h_de_hs = mode->hsync_start - mode->hdisplay;
1085f5e7d251SAlgea Cao 	hsync_len = mode->hsync_end - mode->hsync_start;
1086f5e7d251SAlgea Cao 
1087f5e7d251SAlgea Cao 	/*
1088f5e7d251SAlgea Cao 	 * When we're setting a YCbCr420 mode, we need
1089f5e7d251SAlgea Cao 	 * to adjust the horizontal timing to suit.
1090f5e7d251SAlgea Cao 	 */
1091f5e7d251SAlgea Cao 	if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) {
1092f5e7d251SAlgea Cao 		hdisplay /= 2;
1093f5e7d251SAlgea Cao 		hblank /= 2;
1094f5e7d251SAlgea Cao 		h_de_hs /= 2;
1095f5e7d251SAlgea Cao 		hsync_len /= 2;
1096f5e7d251SAlgea Cao 	}
1097f5e7d251SAlgea Cao 
1098f5e7d251SAlgea Cao 	vdisplay = mode->vdisplay;
1099f5e7d251SAlgea Cao 	vblank = mode->vtotal - mode->vdisplay;
1100f5e7d251SAlgea Cao 	v_de_vs = mode->vsync_start - mode->vdisplay;
1101f5e7d251SAlgea Cao 	vsync_len = mode->vsync_end - mode->vsync_start;
1102f5e7d251SAlgea Cao 
1103f5e7d251SAlgea Cao 	/*
1104f5e7d251SAlgea Cao 	 * When we're setting an interlaced mode, we need
1105f5e7d251SAlgea Cao 	 * to adjust the vertical timing to suit.
1106f5e7d251SAlgea Cao 	 */
1107f5e7d251SAlgea Cao 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
1108f5e7d251SAlgea Cao 		vdisplay /= 2;
1109f5e7d251SAlgea Cao 		vblank /= 2;
1110f5e7d251SAlgea Cao 		v_de_vs /= 2;
1111f5e7d251SAlgea Cao 		vsync_len /= 2;
1112f5e7d251SAlgea Cao 	} else if ((mode->flags & DRM_MODE_FLAG_3D_MASK) ==
1113f5e7d251SAlgea Cao 		DRM_MODE_FLAG_3D_FRAME_PACKING) {
1114f5e7d251SAlgea Cao 		vdisplay += mode->vtotal;
1115f5e7d251SAlgea Cao 	}
1116f5e7d251SAlgea Cao 
1117f5e7d251SAlgea Cao 	/* Scrambling Control */
1118f5e7d251SAlgea Cao 	if (hdmi_info->scdc.supported) {
11198e2bab3fSAlgea Cao 		if (vmode->mtmdsclock > 340000000 ||
11208e2bab3fSAlgea Cao 		    (hdmi_info->scdc.scrambling.low_rates &&
11218e2bab3fSAlgea Cao 		     hdmi->scramble_low_rates)) {
11228e2bab3fSAlgea Cao 			drm_scdc_readb(&hdmi->adap, SCDC_SINK_VERSION, &bytes);
11238e2bab3fSAlgea Cao 			drm_scdc_writeb(&hdmi->adap, SCDC_SOURCE_VERSION,
11248e2bab3fSAlgea Cao 					bytes);
1125f5e7d251SAlgea Cao 			rockchip_dw_hdmi_scrambling_enable(hdmi, 1);
1126f5e7d251SAlgea Cao 		} else {
1127f5e7d251SAlgea Cao 			rockchip_dw_hdmi_scrambling_enable(hdmi, 0);
1128f5e7d251SAlgea Cao 		}
1129f5e7d251SAlgea Cao 	}
1130f5e7d251SAlgea Cao 
1131f5e7d251SAlgea Cao 	/* Set up horizontal active pixel width */
1132f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, hdisplay >> 8, HDMI_FC_INHACTV1);
1133f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, hdisplay, HDMI_FC_INHACTV0);
1134f5e7d251SAlgea Cao 
1135f5e7d251SAlgea Cao 	/* Set up vertical active lines */
1136f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1);
1137f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0);
1138f5e7d251SAlgea Cao 
1139f5e7d251SAlgea Cao 	/* Set up horizontal blanking pixel region width */
1140f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
1141f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
1142f5e7d251SAlgea Cao 
1143f5e7d251SAlgea Cao 	/* Set up vertical blanking pixel region width */
1144f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
1145f5e7d251SAlgea Cao 
1146f5e7d251SAlgea Cao 	/* Set up HSYNC active edge delay width (in pixel clks) */
1147f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
1148f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
1149f5e7d251SAlgea Cao 
1150f5e7d251SAlgea Cao 	/* Set up VSYNC active edge delay (in lines) */
1151f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
1152f5e7d251SAlgea Cao 
1153f5e7d251SAlgea Cao 	/* Set up HSYNC active pulse width (in pixel clks) */
1154f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
1155f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
1156f5e7d251SAlgea Cao 
1157f5e7d251SAlgea Cao 	/* Set up VSYNC active edge delay (in lines) */
1158f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
1159f5e7d251SAlgea Cao }
1160f5e7d251SAlgea Cao 
1161f5e7d251SAlgea Cao static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi)
1162f5e7d251SAlgea Cao {
1163f5e7d251SAlgea Cao 	const u16 (*csc_coeff)[3][4] = &csc_coeff_default;
1164f5e7d251SAlgea Cao 	unsigned i;
1165f5e7d251SAlgea Cao 	u32 csc_scale = 1;
1166b5016cf2SAlgea Cao 	int enc_out_rgb, enc_in_rgb;
1167b5016cf2SAlgea Cao 
1168b5016cf2SAlgea Cao 	enc_out_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format);
1169b5016cf2SAlgea Cao 	enc_in_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format);
1170f5e7d251SAlgea Cao 
1171f5e7d251SAlgea Cao 	if (is_color_space_conversion(hdmi)) {
11723f6d16abSAlgea Cao 		if (enc_out_rgb && enc_in_rgb) {
1173b5016cf2SAlgea Cao 			csc_coeff = &csc_coeff_full_to_limited;
1174b5016cf2SAlgea Cao 			csc_scale = 0;
1175b5016cf2SAlgea Cao 		} else if (enc_out_rgb) {
1176f5e7d251SAlgea Cao 			if (hdmi->hdmi_data.enc_out_encoding ==
1177f5e7d251SAlgea Cao 						V4L2_YCBCR_ENC_601)
1178f5e7d251SAlgea Cao 				csc_coeff = &csc_coeff_rgb_out_eitu601;
1179f5e7d251SAlgea Cao 			else
1180f5e7d251SAlgea Cao 				csc_coeff = &csc_coeff_rgb_out_eitu709;
1181b5016cf2SAlgea Cao 		} else if (enc_in_rgb) {
1182f5e7d251SAlgea Cao 			if (hdmi->hdmi_data.enc_out_encoding ==
1183f5e7d251SAlgea Cao 						V4L2_YCBCR_ENC_601)
1184f5e7d251SAlgea Cao 				csc_coeff = &csc_coeff_rgb_in_eitu601;
1185f5e7d251SAlgea Cao 			else
1186f5e7d251SAlgea Cao 				csc_coeff = &csc_coeff_rgb_in_eitu709;
1187f5e7d251SAlgea Cao 			csc_scale = 0;
1188f5e7d251SAlgea Cao 		}
1189f5e7d251SAlgea Cao 	}
1190f5e7d251SAlgea Cao 
1191f5e7d251SAlgea Cao 	/* The CSC registers are sequential, alternating MSB then LSB */
1192f5e7d251SAlgea Cao 	for (i = 0; i < ARRAY_SIZE(csc_coeff_default[0]); i++) {
1193f5e7d251SAlgea Cao 		u16 coeff_a = (*csc_coeff)[0][i];
1194f5e7d251SAlgea Cao 		u16 coeff_b = (*csc_coeff)[1][i];
1195f5e7d251SAlgea Cao 		u16 coeff_c = (*csc_coeff)[2][i];
1196f5e7d251SAlgea Cao 
1197f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, coeff_a & 0xff, HDMI_CSC_COEF_A1_LSB + i * 2);
1198f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
1199f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
1200f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
1201f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, coeff_c & 0xff, HDMI_CSC_COEF_C1_LSB + i * 2);
1202f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
1203f5e7d251SAlgea Cao 	}
1204f5e7d251SAlgea Cao 
1205f5e7d251SAlgea Cao 	hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
1206f5e7d251SAlgea Cao 		  HDMI_CSC_SCALE);
1207f5e7d251SAlgea Cao }
1208f5e7d251SAlgea Cao 
1209f5e7d251SAlgea Cao static int is_color_space_interpolation(struct dw_hdmi *hdmi)
1210f5e7d251SAlgea Cao {
1211f5e7d251SAlgea Cao 	if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_in_bus_format))
1212f5e7d251SAlgea Cao 		return 0;
1213f5e7d251SAlgea Cao 
1214f5e7d251SAlgea Cao 	if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
1215f5e7d251SAlgea Cao 	    hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
1216f5e7d251SAlgea Cao 		return 1;
1217f5e7d251SAlgea Cao 
1218f5e7d251SAlgea Cao 	return 0;
1219f5e7d251SAlgea Cao }
1220f5e7d251SAlgea Cao 
1221f5e7d251SAlgea Cao static void hdmi_video_csc(struct dw_hdmi *hdmi)
1222f5e7d251SAlgea Cao {
1223f5e7d251SAlgea Cao 	int color_depth = 0;
1224f5e7d251SAlgea Cao 	int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE;
1225f5e7d251SAlgea Cao 	int decimation = 0;
1226f5e7d251SAlgea Cao 
1227f5e7d251SAlgea Cao 	/* YCC422 interpolation to 444 mode */
1228f5e7d251SAlgea Cao 	if (is_color_space_interpolation(hdmi))
1229f5e7d251SAlgea Cao 		interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1;
1230f5e7d251SAlgea Cao 	else if (is_color_space_decimation(hdmi))
1231f5e7d251SAlgea Cao 		decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3;
1232f5e7d251SAlgea Cao 
1233f5e7d251SAlgea Cao 	switch (hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format)) {
1234f5e7d251SAlgea Cao 	case 8:
1235f5e7d251SAlgea Cao 		color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP;
1236f5e7d251SAlgea Cao 		break;
1237f5e7d251SAlgea Cao 	case 10:
1238f5e7d251SAlgea Cao 		color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP;
1239f5e7d251SAlgea Cao 		break;
1240f5e7d251SAlgea Cao 	case 12:
1241f5e7d251SAlgea Cao 		color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP;
1242f5e7d251SAlgea Cao 		break;
1243f5e7d251SAlgea Cao 	case 16:
1244f5e7d251SAlgea Cao 		color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP;
1245f5e7d251SAlgea Cao 		break;
1246f5e7d251SAlgea Cao 
1247f5e7d251SAlgea Cao 	default:
1248f5e7d251SAlgea Cao 		return;
1249f5e7d251SAlgea Cao 	}
1250f5e7d251SAlgea Cao 
1251f5e7d251SAlgea Cao 	/* Configure the CSC registers */
1252f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG);
1253f5e7d251SAlgea Cao 	hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
1254f5e7d251SAlgea Cao 		  HDMI_CSC_SCALE);
1255f5e7d251SAlgea Cao 
1256f5e7d251SAlgea Cao 	dw_hdmi_update_csc_coeffs(hdmi);
1257f5e7d251SAlgea Cao }
1258f5e7d251SAlgea Cao 
1259f5e7d251SAlgea Cao static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
1260f5e7d251SAlgea Cao {
1261f5e7d251SAlgea Cao 	u8 clkdis;
1262f5e7d251SAlgea Cao 
1263f5e7d251SAlgea Cao 	/* control period minimum duration */
1264f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
1265f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
1266f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 1, HDMI_FC_EXCTRLSPAC);
1267f5e7d251SAlgea Cao 
1268f5e7d251SAlgea Cao 	/* Set to fill TMDS data channels */
1269f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0x0B, HDMI_FC_CH0PREAM);
1270f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0x16, HDMI_FC_CH1PREAM);
1271f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
1272f5e7d251SAlgea Cao 
1273f5e7d251SAlgea Cao 	/* Enable pixel clock and tmds data path */
1274f5e7d251SAlgea Cao 	clkdis = 0x7F;
1275f5e7d251SAlgea Cao 	clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
1276f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
1277f5e7d251SAlgea Cao 
1278f5e7d251SAlgea Cao 	clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
1279f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
1280f5e7d251SAlgea Cao 
1281f5e7d251SAlgea Cao 	/* Enable csc path */
1282f5e7d251SAlgea Cao 	if (is_color_space_conversion(hdmi)) {
1283f5e7d251SAlgea Cao 		clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
1284f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
1285f5e7d251SAlgea Cao 	}
1286f5e7d251SAlgea Cao 
1287f5e7d251SAlgea Cao 	/* Enable pixel repetition path */
1288f5e7d251SAlgea Cao 	if (hdmi->hdmi_data.video_mode.mpixelrepetitioninput) {
1289f5e7d251SAlgea Cao 		clkdis &= ~HDMI_MC_CLKDIS_PREPCLK_DISABLE;
1290f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
1291f5e7d251SAlgea Cao 	}
1292f5e7d251SAlgea Cao 
1293f5e7d251SAlgea Cao 	/* Enable color space conversion if needed */
1294f5e7d251SAlgea Cao 	if (is_color_space_conversion(hdmi))
1295f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH,
1296f5e7d251SAlgea Cao 			    HDMI_MC_FLOWCTRL);
1297f5e7d251SAlgea Cao 	else
1298f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS,
1299f5e7d251SAlgea Cao 			    HDMI_MC_FLOWCTRL);
1300f5e7d251SAlgea Cao }
1301f5e7d251SAlgea Cao 
1302f5e7d251SAlgea Cao static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
1303f5e7d251SAlgea Cao {
1304f5e7d251SAlgea Cao 	unsigned int count;
1305f5e7d251SAlgea Cao 	unsigned int i;
1306f5e7d251SAlgea Cao 	u8 val;
1307f5e7d251SAlgea Cao 
1308f5e7d251SAlgea Cao 	/*
1309f5e7d251SAlgea Cao 	 * Under some circumstances the Frame Composer arithmetic unit can miss
1310f5e7d251SAlgea Cao 	 * an FC register write due to being busy processing the previous one.
1311f5e7d251SAlgea Cao 	 * The issue can be worked around by issuing a TMDS software reset and
1312f5e7d251SAlgea Cao 	 * then write one of the FC registers several times.
1313f5e7d251SAlgea Cao 	 *
1314f5e7d251SAlgea Cao 	 * The number of iterations matters and depends on the HDMI TX revision
1315f5e7d251SAlgea Cao 	 * (and possibly on the platform). So far only i.MX6Q (v1.30a) and
1316f5e7d251SAlgea Cao 	 * i.MX6DL (v1.31a) have been identified as needing the workaround, with
1317f5e7d251SAlgea Cao 	 * 4 and 1 iterations respectively.
1318f5e7d251SAlgea Cao 	 */
1319f5e7d251SAlgea Cao 
1320f5e7d251SAlgea Cao 	switch (hdmi->version) {
1321f5e7d251SAlgea Cao 	case 0x130a:
1322f5e7d251SAlgea Cao 		count = 4;
1323f5e7d251SAlgea Cao 		break;
1324f5e7d251SAlgea Cao 	case 0x131a:
13258e2bab3fSAlgea Cao 	case 0x200a:
13268e2bab3fSAlgea Cao 	case 0x201a:
13278e2bab3fSAlgea Cao 	case 0x211a:
1328f5e7d251SAlgea Cao 		count = 1;
1329f5e7d251SAlgea Cao 		break;
1330f5e7d251SAlgea Cao 	default:
1331f5e7d251SAlgea Cao 		return;
1332f5e7d251SAlgea Cao 	}
1333f5e7d251SAlgea Cao 
1334f5e7d251SAlgea Cao 	/* TMDS software reset */
1335f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
1336f5e7d251SAlgea Cao 
1337f5e7d251SAlgea Cao 	val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF);
1338f5e7d251SAlgea Cao 	for (i = 0; i < count; i++)
1339f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
1340f5e7d251SAlgea Cao }
1341f5e7d251SAlgea Cao 
1342f5e7d251SAlgea Cao static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi)
1343f5e7d251SAlgea Cao {
1344f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
1345f5e7d251SAlgea Cao 		    HDMI_IH_MUTE_FC_STAT2);
1346f5e7d251SAlgea Cao }
1347f5e7d251SAlgea Cao 
1348f5e7d251SAlgea Cao static void hdmi_video_packetize(struct dw_hdmi *hdmi)
1349f5e7d251SAlgea Cao {
1350f5e7d251SAlgea Cao 	unsigned int color_depth = 0;
1351f5e7d251SAlgea Cao 	unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit;
1352f5e7d251SAlgea Cao 	unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP;
1353f5e7d251SAlgea Cao 	struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
1354f5e7d251SAlgea Cao 	u8 val, vp_conf;
1355f5e7d251SAlgea Cao 
1356f5e7d251SAlgea Cao 	if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
1357f5e7d251SAlgea Cao 	    hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) ||
1358f5e7d251SAlgea Cao 	    hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) {
1359f5e7d251SAlgea Cao 		switch (hdmi_bus_fmt_color_depth(
1360f5e7d251SAlgea Cao 					hdmi->hdmi_data.enc_out_bus_format)) {
1361f5e7d251SAlgea Cao 		case 8:
1362f5e7d251SAlgea Cao 			color_depth = 0;
1363f5e7d251SAlgea Cao 			output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
1364f5e7d251SAlgea Cao 			break;
1365f5e7d251SAlgea Cao 		case 10:
1366f5e7d251SAlgea Cao 			color_depth = 5;
1367f5e7d251SAlgea Cao 			break;
1368f5e7d251SAlgea Cao 		case 12:
1369f5e7d251SAlgea Cao 			color_depth = 6;
1370f5e7d251SAlgea Cao 			break;
1371f5e7d251SAlgea Cao 		case 16:
1372f5e7d251SAlgea Cao 			color_depth = 7;
1373f5e7d251SAlgea Cao 			break;
1374f5e7d251SAlgea Cao 		default:
1375f5e7d251SAlgea Cao 			output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
1376f5e7d251SAlgea Cao 		}
1377f5e7d251SAlgea Cao 	} else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) {
1378f5e7d251SAlgea Cao 		switch (hdmi_bus_fmt_color_depth(
1379f5e7d251SAlgea Cao 					hdmi->hdmi_data.enc_out_bus_format)) {
1380f5e7d251SAlgea Cao 		case 0:
1381f5e7d251SAlgea Cao 		case 8:
1382f5e7d251SAlgea Cao 			remap_size = HDMI_VP_REMAP_YCC422_16bit;
1383f5e7d251SAlgea Cao 			break;
1384f5e7d251SAlgea Cao 		case 10:
1385f5e7d251SAlgea Cao 			remap_size = HDMI_VP_REMAP_YCC422_20bit;
1386f5e7d251SAlgea Cao 			break;
1387f5e7d251SAlgea Cao 		case 12:
1388f5e7d251SAlgea Cao 			remap_size = HDMI_VP_REMAP_YCC422_24bit;
1389f5e7d251SAlgea Cao 			break;
1390f5e7d251SAlgea Cao 
1391f5e7d251SAlgea Cao 		default:
1392f5e7d251SAlgea Cao 			return;
1393f5e7d251SAlgea Cao 		}
1394f5e7d251SAlgea Cao 		output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422;
1395f5e7d251SAlgea Cao 	} else {
1396f5e7d251SAlgea Cao 		return;
1397f5e7d251SAlgea Cao 	}
1398f5e7d251SAlgea Cao 
1399f5e7d251SAlgea Cao 	/* set the packetizer registers */
14008e2bab3fSAlgea Cao 	val = (color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
14018e2bab3fSAlgea Cao 	      HDMI_VP_PR_CD_COLOR_DEPTH_MASK;
1402f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, val, HDMI_VP_PR_CD);
1403f5e7d251SAlgea Cao 
1404f5e7d251SAlgea Cao 	hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
1405f5e7d251SAlgea Cao 		  HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF);
1406f5e7d251SAlgea Cao 
1407f5e7d251SAlgea Cao 	/* Data from pixel repeater block */
1408f5e7d251SAlgea Cao 	if (hdmi_data->pix_repet_factor > 0) {
1409f5e7d251SAlgea Cao 		vp_conf = HDMI_VP_CONF_PR_EN_ENABLE |
1410f5e7d251SAlgea Cao 			  HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER;
1411f5e7d251SAlgea Cao 	} else { /* data from packetizer block */
1412f5e7d251SAlgea Cao 		vp_conf = HDMI_VP_CONF_PR_EN_DISABLE |
1413f5e7d251SAlgea Cao 			  HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
1414f5e7d251SAlgea Cao 	}
1415f5e7d251SAlgea Cao 
1416f5e7d251SAlgea Cao 	hdmi_modb(hdmi, vp_conf,
1417f5e7d251SAlgea Cao 		  HDMI_VP_CONF_PR_EN_MASK |
1418f5e7d251SAlgea Cao 		  HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF);
1419f5e7d251SAlgea Cao 
14208e2bab3fSAlgea Cao 	if ((color_depth == 5 && hdmi->previous_mode.htotal % 4) ||
14218e2bab3fSAlgea Cao 	    (color_depth == 6 && hdmi->previous_mode.htotal % 2))
14228e2bab3fSAlgea Cao 		hdmi_modb(hdmi, 0, HDMI_VP_STUFF_IDEFAULT_PHASE_MASK,
14238e2bab3fSAlgea Cao 			  HDMI_VP_STUFF);
14248e2bab3fSAlgea Cao 	else
1425f5e7d251SAlgea Cao 		hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
1426f5e7d251SAlgea Cao 			  HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF);
1427f5e7d251SAlgea Cao 
1428f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP);
1429f5e7d251SAlgea Cao 
1430f5e7d251SAlgea Cao 	if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) {
1431f5e7d251SAlgea Cao 		vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
1432f5e7d251SAlgea Cao 			  HDMI_VP_CONF_PP_EN_ENABLE |
1433f5e7d251SAlgea Cao 			  HDMI_VP_CONF_YCC422_EN_DISABLE;
1434f5e7d251SAlgea Cao 	} else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) {
1435f5e7d251SAlgea Cao 		vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
1436f5e7d251SAlgea Cao 			  HDMI_VP_CONF_PP_EN_DISABLE |
1437f5e7d251SAlgea Cao 			  HDMI_VP_CONF_YCC422_EN_ENABLE;
1438f5e7d251SAlgea Cao 	} else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) {
1439f5e7d251SAlgea Cao 		vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE |
1440f5e7d251SAlgea Cao 			  HDMI_VP_CONF_PP_EN_DISABLE |
1441f5e7d251SAlgea Cao 			  HDMI_VP_CONF_YCC422_EN_DISABLE;
1442f5e7d251SAlgea Cao 	} else {
1443f5e7d251SAlgea Cao 		return;
1444f5e7d251SAlgea Cao 	}
1445f5e7d251SAlgea Cao 
1446f5e7d251SAlgea Cao 	hdmi_modb(hdmi, vp_conf,
1447f5e7d251SAlgea Cao 		  HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK |
1448f5e7d251SAlgea Cao 		  HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF);
1449f5e7d251SAlgea Cao 
1450f5e7d251SAlgea Cao 	hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
1451f5e7d251SAlgea Cao 			HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE,
1452f5e7d251SAlgea Cao 		  HDMI_VP_STUFF_PP_STUFFING_MASK |
1453f5e7d251SAlgea Cao 		  HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF);
1454f5e7d251SAlgea Cao 
1455f5e7d251SAlgea Cao 	hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
1456f5e7d251SAlgea Cao 		  HDMI_VP_CONF);
1457f5e7d251SAlgea Cao }
1458f5e7d251SAlgea Cao 
1459f5e7d251SAlgea Cao static void hdmi_video_sample(struct dw_hdmi *hdmi)
1460f5e7d251SAlgea Cao {
1461f5e7d251SAlgea Cao 	int color_format = 0;
1462f5e7d251SAlgea Cao 	u8 val;
1463f5e7d251SAlgea Cao 
1464f5e7d251SAlgea Cao 	switch (hdmi->hdmi_data.enc_in_bus_format) {
1465f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_RGB888_1X24:
1466f5e7d251SAlgea Cao 		color_format = 0x01;
1467f5e7d251SAlgea Cao 		break;
1468f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_RGB101010_1X30:
1469f5e7d251SAlgea Cao 		color_format = 0x03;
1470f5e7d251SAlgea Cao 		break;
1471f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_RGB121212_1X36:
1472f5e7d251SAlgea Cao 		color_format = 0x05;
1473f5e7d251SAlgea Cao 		break;
1474f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_RGB161616_1X48:
1475f5e7d251SAlgea Cao 		color_format = 0x07;
1476f5e7d251SAlgea Cao 		break;
1477f5e7d251SAlgea Cao 
1478f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_YUV8_1X24:
1479f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
1480f5e7d251SAlgea Cao 		color_format = 0x09;
1481f5e7d251SAlgea Cao 		break;
1482f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_YUV10_1X30:
1483f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
1484f5e7d251SAlgea Cao 		color_format = 0x0B;
1485f5e7d251SAlgea Cao 		break;
1486f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_YUV12_1X36:
1487f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
1488f5e7d251SAlgea Cao 		color_format = 0x0D;
1489f5e7d251SAlgea Cao 		break;
1490f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_YUV16_1X48:
1491f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
1492f5e7d251SAlgea Cao 		color_format = 0x0F;
1493f5e7d251SAlgea Cao 		break;
1494f5e7d251SAlgea Cao 
1495f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYVY8_1X16:
1496f5e7d251SAlgea Cao 		color_format = 0x16;
1497f5e7d251SAlgea Cao 		break;
1498f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYVY10_1X20:
1499f5e7d251SAlgea Cao 		color_format = 0x14;
1500f5e7d251SAlgea Cao 		break;
1501f5e7d251SAlgea Cao 	case MEDIA_BUS_FMT_UYVY12_1X24:
1502f5e7d251SAlgea Cao 		color_format = 0x12;
1503f5e7d251SAlgea Cao 		break;
1504f5e7d251SAlgea Cao 
1505f5e7d251SAlgea Cao 	default:
1506f5e7d251SAlgea Cao 		return;
1507f5e7d251SAlgea Cao 	}
1508f5e7d251SAlgea Cao 
1509f5e7d251SAlgea Cao 	val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
1510f5e7d251SAlgea Cao 		((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
1511f5e7d251SAlgea Cao 		HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
1512f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, val, HDMI_TX_INVID0);
1513f5e7d251SAlgea Cao 
1514f5e7d251SAlgea Cao 	/* Enable TX stuffing: When DE is inactive, fix the output data to 0 */
1515f5e7d251SAlgea Cao 	val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
1516f5e7d251SAlgea Cao 		HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
1517f5e7d251SAlgea Cao 		HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
1518f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, val, HDMI_TX_INSTUFFING);
1519f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA0);
1520f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA1);
1521f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA0);
1522f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA1);
1523f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA0);
1524f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1);
1525f5e7d251SAlgea Cao }
1526f5e7d251SAlgea Cao 
15278e2bab3fSAlgea Cao static void dw_hdmi_disable(struct dw_hdmi *hdmi, struct display_state *state)
1528f5e7d251SAlgea Cao {
1529f5e7d251SAlgea Cao 	if (hdmi->phy.enabled) {
15308e2bab3fSAlgea Cao 		hdmi->phy.ops->disable(hdmi, state);
1531f5e7d251SAlgea Cao 		hdmi->phy.enabled = false;
1532f5e7d251SAlgea Cao 	}
1533f5e7d251SAlgea Cao }
1534f5e7d251SAlgea Cao 
1535f5e7d251SAlgea Cao static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
1536f5e7d251SAlgea Cao {
1537f5e7d251SAlgea Cao 	struct hdmi_avi_infoframe frame;
1538f5e7d251SAlgea Cao 	u8 val;
1539f5e7d251SAlgea Cao 	bool is_hdmi2 = false;
1540b5016cf2SAlgea Cao 	enum hdmi_quantization_range rgb_quant_range =
1541b5016cf2SAlgea Cao 		hdmi->hdmi_data.quant_range;
1542f5e7d251SAlgea Cao 
1543f5e7d251SAlgea Cao 	if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) ||
1544f5e7d251SAlgea Cao 	    hdmi->edid_data.display_info.hdmi.scdc.supported)
1545f5e7d251SAlgea Cao 		is_hdmi2 = true;
1546f5e7d251SAlgea Cao 	/* Initialise info frame from DRM mode */
1547f5e7d251SAlgea Cao 	drm_hdmi_avi_infoframe_from_display_mode(&frame, mode, is_hdmi2);
1548f5e7d251SAlgea Cao 
1549b5016cf2SAlgea Cao 	/*
1550b5016cf2SAlgea Cao 	 * Ignore monitor selectable quantization, use quantization set
1551b5016cf2SAlgea Cao 	 * by the user
1552b5016cf2SAlgea Cao 	 */
1553b5016cf2SAlgea Cao 	drm_hdmi_avi_infoframe_quant_range(&frame, mode, rgb_quant_range,
1554b5016cf2SAlgea Cao 					   true);
1555f5e7d251SAlgea Cao 	if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
1556f5e7d251SAlgea Cao 		frame.colorspace = HDMI_COLORSPACE_YUV444;
1557f5e7d251SAlgea Cao 	else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
1558f5e7d251SAlgea Cao 		frame.colorspace = HDMI_COLORSPACE_YUV422;
1559f5e7d251SAlgea Cao 	else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
1560f5e7d251SAlgea Cao 		frame.colorspace = HDMI_COLORSPACE_YUV420;
1561f5e7d251SAlgea Cao 	else
1562f5e7d251SAlgea Cao 		frame.colorspace = HDMI_COLORSPACE_RGB;
1563f5e7d251SAlgea Cao 
1564f5e7d251SAlgea Cao 	/* Set up colorimetry */
1565f5e7d251SAlgea Cao 	switch (hdmi->hdmi_data.enc_out_encoding) {
1566f5e7d251SAlgea Cao 	case V4L2_YCBCR_ENC_601:
1567f5e7d251SAlgea Cao 		if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV601)
1568f5e7d251SAlgea Cao 			frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
1569f5e7d251SAlgea Cao 		else
1570f5e7d251SAlgea Cao 			frame.colorimetry = HDMI_COLORIMETRY_ITU_601;
1571f5e7d251SAlgea Cao 		frame.extended_colorimetry =
1572f5e7d251SAlgea Cao 				HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
1573f5e7d251SAlgea Cao 		break;
1574f5e7d251SAlgea Cao 	case V4L2_YCBCR_ENC_709:
1575f5e7d251SAlgea Cao 		if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV709)
1576f5e7d251SAlgea Cao 			frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
1577f5e7d251SAlgea Cao 		else
1578f5e7d251SAlgea Cao 			frame.colorimetry = HDMI_COLORIMETRY_ITU_709;
1579f5e7d251SAlgea Cao 		frame.extended_colorimetry =
1580f5e7d251SAlgea Cao 				HDMI_EXTENDED_COLORIMETRY_XV_YCC_709;
1581f5e7d251SAlgea Cao 		break;
1582f5e7d251SAlgea Cao 	default: /* Carries no data */
1583f5e7d251SAlgea Cao 		frame.colorimetry = HDMI_COLORIMETRY_ITU_601;
1584f5e7d251SAlgea Cao 		frame.extended_colorimetry =
1585f5e7d251SAlgea Cao 				HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
1586f5e7d251SAlgea Cao 		break;
1587f5e7d251SAlgea Cao 	}
1588f5e7d251SAlgea Cao 
1589f5e7d251SAlgea Cao 	frame.scan_mode = HDMI_SCAN_MODE_NONE;
1590f5e7d251SAlgea Cao 
1591f5e7d251SAlgea Cao 	/*
1592f5e7d251SAlgea Cao 	 * The Designware IP uses a different byte format from standard
1593f5e7d251SAlgea Cao 	 * AVI info frames, though generally the bits are in the correct
1594f5e7d251SAlgea Cao 	 * bytes.
1595f5e7d251SAlgea Cao 	 */
1596f5e7d251SAlgea Cao 
1597f5e7d251SAlgea Cao 	/*
1598f5e7d251SAlgea Cao 	 * AVI data byte 1 differences: Colorspace in bits 0,1,7 rather than
1599f5e7d251SAlgea Cao 	 * 5,6,7, active aspect present in bit 6 rather than 4.
1600f5e7d251SAlgea Cao 	 */
1601f5e7d251SAlgea Cao 	val = (frame.scan_mode & 3) << 4 | (frame.colorspace & 0x3);
1602f5e7d251SAlgea Cao 	if (frame.active_aspect & 15)
1603f5e7d251SAlgea Cao 		val |= HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT;
1604f5e7d251SAlgea Cao 	if (frame.top_bar || frame.bottom_bar)
1605f5e7d251SAlgea Cao 		val |= HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR;
1606f5e7d251SAlgea Cao 	if (frame.left_bar || frame.right_bar)
1607f5e7d251SAlgea Cao 		val |= HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR;
1608f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0);
1609f5e7d251SAlgea Cao 
1610f5e7d251SAlgea Cao 	/* AVI data byte 2 differences: none */
1611f5e7d251SAlgea Cao 	val = ((frame.colorimetry & 0x3) << 6) |
1612f5e7d251SAlgea Cao 	      ((frame.picture_aspect & 0x3) << 4) |
1613f5e7d251SAlgea Cao 	      (frame.active_aspect & 0xf);
1614f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF1);
1615f5e7d251SAlgea Cao 
1616f5e7d251SAlgea Cao 	/* AVI data byte 3 differences: none */
1617f5e7d251SAlgea Cao 	val = ((frame.extended_colorimetry & 0x7) << 4) |
1618f5e7d251SAlgea Cao 	      ((frame.quantization_range & 0x3) << 2) |
1619f5e7d251SAlgea Cao 	      (frame.nups & 0x3);
1620f5e7d251SAlgea Cao 	if (frame.itc)
1621f5e7d251SAlgea Cao 		val |= HDMI_FC_AVICONF2_IT_CONTENT_VALID;
1622f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2);
1623f5e7d251SAlgea Cao 
1624f5e7d251SAlgea Cao 	/* AVI data byte 4 differences: none */
1625f5e7d251SAlgea Cao 	val = frame.video_code & 0x7f;
1626f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, val, HDMI_FC_AVIVID);
1627f5e7d251SAlgea Cao 
1628f5e7d251SAlgea Cao 	/* AVI Data Byte 5- set up input and output pixel repetition */
1629f5e7d251SAlgea Cao 	val = (((hdmi->hdmi_data.video_mode.mpixelrepetitioninput + 1) <<
1630f5e7d251SAlgea Cao 		HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET) &
1631f5e7d251SAlgea Cao 		HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK) |
1632f5e7d251SAlgea Cao 		((hdmi->hdmi_data.video_mode.mpixelrepetitionoutput <<
1633f5e7d251SAlgea Cao 		HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) &
1634f5e7d251SAlgea Cao 		HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK);
1635f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, val, HDMI_FC_PRCONF);
1636f5e7d251SAlgea Cao 
1637f5e7d251SAlgea Cao 	/*
1638f5e7d251SAlgea Cao 	 * AVI data byte 5 differences: content type in 0,1 rather than 4,5,
1639f5e7d251SAlgea Cao 	 * ycc range in bits 2,3 rather than 6,7
1640f5e7d251SAlgea Cao 	 */
1641f5e7d251SAlgea Cao 	val = ((frame.ycc_quantization_range & 0x3) << 2) |
1642f5e7d251SAlgea Cao 	      (frame.content_type & 0x3);
1643f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF3);
1644f5e7d251SAlgea Cao 
1645f5e7d251SAlgea Cao 	/* AVI Data Bytes 6-13 */
1646f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, frame.top_bar & 0xff, HDMI_FC_AVIETB0);
1647f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, (frame.top_bar >> 8) & 0xff, HDMI_FC_AVIETB1);
1648f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, frame.bottom_bar & 0xff, HDMI_FC_AVISBB0);
1649f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, (frame.bottom_bar >> 8) & 0xff, HDMI_FC_AVISBB1);
1650f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, frame.left_bar & 0xff, HDMI_FC_AVIELB0);
1651f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, (frame.left_bar >> 8) & 0xff, HDMI_FC_AVIELB1);
1652f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, frame.right_bar & 0xff, HDMI_FC_AVISRB0);
1653f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, (frame.right_bar >> 8) & 0xff, HDMI_FC_AVISRB1);
1654f5e7d251SAlgea Cao }
1655f5e7d251SAlgea Cao 
1656f5e7d251SAlgea Cao static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi,
1657f5e7d251SAlgea Cao 						  struct drm_display_mode *mode)
1658f5e7d251SAlgea Cao {
1659f5e7d251SAlgea Cao 	struct hdmi_vendor_infoframe frame;
1660f5e7d251SAlgea Cao 	u8 buffer[10];
1661f5e7d251SAlgea Cao 	ssize_t err;
1662f5e7d251SAlgea Cao 
1663f5e7d251SAlgea Cao 	/* Disable HDMI vendor specific infoframe send */
1664f5e7d251SAlgea Cao 	hdmi_mask_writeb(hdmi, 0, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET,
1665f5e7d251SAlgea Cao 			 HDMI_FC_DATAUTO0_VSD_MASK);
1666f5e7d251SAlgea Cao 
1667f5e7d251SAlgea Cao 	err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, mode);
1668f5e7d251SAlgea Cao 	if (err < 0)
1669f5e7d251SAlgea Cao 		/*
1670f5e7d251SAlgea Cao 		 * Going into that statement does not means vendor infoframe
1671f5e7d251SAlgea Cao 		 * fails. It just informed us that vendor infoframe is not
1672f5e7d251SAlgea Cao 		 * needed for the selected mode. Only 4k or stereoscopic 3D
1673f5e7d251SAlgea Cao 		 * mode requires vendor infoframe. So just simply return.
1674f5e7d251SAlgea Cao 		 */
1675f5e7d251SAlgea Cao 		return;
1676f5e7d251SAlgea Cao 
1677f5e7d251SAlgea Cao 	err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer));
1678f5e7d251SAlgea Cao 	if (err < 0) {
1679f5e7d251SAlgea Cao 		printf("Failed to pack vendor infoframe: %zd\n", err);
1680f5e7d251SAlgea Cao 		return;
1681f5e7d251SAlgea Cao 	}
1682f5e7d251SAlgea Cao 
1683f5e7d251SAlgea Cao 	/* Set the length of HDMI vendor specific InfoFrame payload */
1684f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, buffer[2], HDMI_FC_VSDSIZE);
1685f5e7d251SAlgea Cao 
1686f5e7d251SAlgea Cao 	/* Set 24bit IEEE Registration Identifier */
1687f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, buffer[4], HDMI_FC_VSDIEEEID0);
1688f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, buffer[5], HDMI_FC_VSDIEEEID1);
1689f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, buffer[6], HDMI_FC_VSDIEEEID2);
1690f5e7d251SAlgea Cao 
1691f5e7d251SAlgea Cao 	/* Set HDMI_Video_Format and HDMI_VIC/3D_Structure */
1692f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, buffer[7], HDMI_FC_VSDPAYLOAD0);
1693f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, buffer[8], HDMI_FC_VSDPAYLOAD1);
1694f5e7d251SAlgea Cao 
1695f5e7d251SAlgea Cao 	if (frame.s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
1696f5e7d251SAlgea Cao 		hdmi_writeb(hdmi, buffer[9], HDMI_FC_VSDPAYLOAD2);
1697f5e7d251SAlgea Cao 
1698f5e7d251SAlgea Cao 	/* Packet frame interpolation */
1699f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 1, HDMI_FC_DATAUTO1);
1700f5e7d251SAlgea Cao 
1701f5e7d251SAlgea Cao 	/* Auto packets per frame and line spacing */
1702f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0x11, HDMI_FC_DATAUTO2);
1703f5e7d251SAlgea Cao 
1704f5e7d251SAlgea Cao 	/* Configures the Frame Composer On RDRB mode */
1705f5e7d251SAlgea Cao 	hdmi_mask_writeb(hdmi, 1, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET,
1706f5e7d251SAlgea Cao 			 HDMI_FC_DATAUTO0_VSD_MASK);
1707f5e7d251SAlgea Cao }
1708f5e7d251SAlgea Cao 
1709f5e7d251SAlgea Cao static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
1710f5e7d251SAlgea Cao 			   unsigned int n)
1711f5e7d251SAlgea Cao {
1712f5e7d251SAlgea Cao 	/* Must be set/cleared first */
1713f5e7d251SAlgea Cao 	hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
1714f5e7d251SAlgea Cao 
1715f5e7d251SAlgea Cao 	/* nshift factor = 0 */
1716f5e7d251SAlgea Cao 	hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
1717f5e7d251SAlgea Cao 
1718f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
1719f5e7d251SAlgea Cao 		    HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
1720f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
1721f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
1722f5e7d251SAlgea Cao 
1723f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, (n >> 16) & 0x0f, HDMI_AUD_N3);
1724f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2);
1725f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1);
1726f5e7d251SAlgea Cao }
1727f5e7d251SAlgea Cao 
1728f5e7d251SAlgea Cao static int hdmi_match_tmds_n_table(struct dw_hdmi *hdmi,
1729f5e7d251SAlgea Cao 				   unsigned long pixel_clk,
1730f5e7d251SAlgea Cao 				   unsigned long freq)
1731f5e7d251SAlgea Cao {
1732f5e7d251SAlgea Cao 	const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data;
1733f5e7d251SAlgea Cao 	const struct dw_hdmi_audio_tmds_n *tmds_n = NULL;
1734f5e7d251SAlgea Cao 	int i;
1735f5e7d251SAlgea Cao 
1736f5e7d251SAlgea Cao 	if (plat_data->tmds_n_table) {
1737f5e7d251SAlgea Cao 		for (i = 0; plat_data->tmds_n_table[i].tmds != 0; i++) {
1738f5e7d251SAlgea Cao 			if (pixel_clk == plat_data->tmds_n_table[i].tmds) {
1739f5e7d251SAlgea Cao 				tmds_n = &plat_data->tmds_n_table[i];
1740f5e7d251SAlgea Cao 				break;
1741f5e7d251SAlgea Cao 			}
1742f5e7d251SAlgea Cao 		}
1743f5e7d251SAlgea Cao 	}
1744f5e7d251SAlgea Cao 
1745f5e7d251SAlgea Cao 	if (!tmds_n) {
1746f5e7d251SAlgea Cao 		for (i = 0; common_tmds_n_table[i].tmds != 0; i++) {
1747f5e7d251SAlgea Cao 			if (pixel_clk == common_tmds_n_table[i].tmds) {
1748f5e7d251SAlgea Cao 				tmds_n = &common_tmds_n_table[i];
1749f5e7d251SAlgea Cao 				break;
1750f5e7d251SAlgea Cao 			}
1751f5e7d251SAlgea Cao 		}
1752f5e7d251SAlgea Cao 	}
1753f5e7d251SAlgea Cao 
1754f5e7d251SAlgea Cao 	if (!tmds_n)
1755f5e7d251SAlgea Cao 		return -ENOENT;
1756f5e7d251SAlgea Cao 
1757f5e7d251SAlgea Cao 	switch (freq) {
1758f5e7d251SAlgea Cao 	case 32000:
1759f5e7d251SAlgea Cao 		return tmds_n->n_32k;
1760f5e7d251SAlgea Cao 	case 44100:
1761f5e7d251SAlgea Cao 	case 88200:
1762f5e7d251SAlgea Cao 	case 176400:
1763f5e7d251SAlgea Cao 		return (freq / 44100) * tmds_n->n_44k1;
1764f5e7d251SAlgea Cao 	case 48000:
1765f5e7d251SAlgea Cao 	case 96000:
1766f5e7d251SAlgea Cao 	case 192000:
1767f5e7d251SAlgea Cao 		return (freq / 48000) * tmds_n->n_48k;
1768f5e7d251SAlgea Cao 	default:
1769f5e7d251SAlgea Cao 		return -ENOENT;
1770f5e7d251SAlgea Cao 	}
1771f5e7d251SAlgea Cao }
1772f5e7d251SAlgea Cao 
1773f5e7d251SAlgea Cao static u64 hdmi_audio_math_diff(unsigned int freq, unsigned int n,
1774f5e7d251SAlgea Cao 				unsigned int pixel_clk)
1775f5e7d251SAlgea Cao {
1776f5e7d251SAlgea Cao 	u64 final, diff;
1777f5e7d251SAlgea Cao 	u64 cts;
1778f5e7d251SAlgea Cao 
1779f5e7d251SAlgea Cao 	final = (u64)pixel_clk * n;
1780f5e7d251SAlgea Cao 
1781f5e7d251SAlgea Cao 	cts = final;
1782f5e7d251SAlgea Cao 	do_div(cts, 128 * freq);
1783f5e7d251SAlgea Cao 
1784f5e7d251SAlgea Cao 	diff = final - (u64)cts * (128 * freq);
1785f5e7d251SAlgea Cao 
1786f5e7d251SAlgea Cao 	return diff;
1787f5e7d251SAlgea Cao }
1788f5e7d251SAlgea Cao 
1789f5e7d251SAlgea Cao static unsigned int hdmi_compute_n(struct dw_hdmi *hdmi,
1790f5e7d251SAlgea Cao 				   unsigned long pixel_clk,
1791f5e7d251SAlgea Cao 				   unsigned long freq)
1792f5e7d251SAlgea Cao {
1793f5e7d251SAlgea Cao 	unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500);
1794f5e7d251SAlgea Cao 	unsigned int max_n = (128 * freq) / 300;
1795f5e7d251SAlgea Cao 	unsigned int ideal_n = (128 * freq) / 1000;
1796f5e7d251SAlgea Cao 	unsigned int best_n_distance = ideal_n;
1797f5e7d251SAlgea Cao 	unsigned int best_n = 0;
1798f5e7d251SAlgea Cao 	u64 best_diff = U64_MAX;
1799f5e7d251SAlgea Cao 	int n;
1800f5e7d251SAlgea Cao 
1801f5e7d251SAlgea Cao 	/* If the ideal N could satisfy the audio math, then just take it */
1802f5e7d251SAlgea Cao 	if (hdmi_audio_math_diff(freq, ideal_n, pixel_clk) == 0)
1803f5e7d251SAlgea Cao 		return ideal_n;
1804f5e7d251SAlgea Cao 
1805f5e7d251SAlgea Cao 	for (n = min_n; n <= max_n; n++) {
1806f5e7d251SAlgea Cao 		u64 diff = hdmi_audio_math_diff(freq, n, pixel_clk);
1807f5e7d251SAlgea Cao 
1808f5e7d251SAlgea Cao 		if (diff < best_diff || (diff == best_diff &&
1809f5e7d251SAlgea Cao 					 abs(n - ideal_n) < best_n_distance)) {
1810f5e7d251SAlgea Cao 			best_n = n;
1811f5e7d251SAlgea Cao 			best_diff = diff;
1812f5e7d251SAlgea Cao 			best_n_distance = abs(best_n - ideal_n);
1813f5e7d251SAlgea Cao 		}
1814f5e7d251SAlgea Cao 
1815f5e7d251SAlgea Cao 		/*
1816f5e7d251SAlgea Cao 		 * The best N already satisfy the audio math, and also be
1817f5e7d251SAlgea Cao 		 * the closest value to ideal N, so just cut the loop.
1818f5e7d251SAlgea Cao 		 */
1819f5e7d251SAlgea Cao 		if ((best_diff == 0) && (abs(n - ideal_n) > best_n_distance))
1820f5e7d251SAlgea Cao 			break;
1821f5e7d251SAlgea Cao 	}
1822f5e7d251SAlgea Cao 
1823f5e7d251SAlgea Cao 	return best_n;
1824f5e7d251SAlgea Cao }
1825f5e7d251SAlgea Cao 
1826f5e7d251SAlgea Cao static unsigned int hdmi_find_n(struct dw_hdmi *hdmi, unsigned long pixel_clk,
1827f5e7d251SAlgea Cao 				unsigned long sample_rate)
1828f5e7d251SAlgea Cao {
1829f5e7d251SAlgea Cao 	int n;
1830f5e7d251SAlgea Cao 
1831f5e7d251SAlgea Cao 	n = hdmi_match_tmds_n_table(hdmi, pixel_clk, sample_rate);
1832f5e7d251SAlgea Cao 	if (n > 0)
1833f5e7d251SAlgea Cao 		return n;
1834f5e7d251SAlgea Cao 
1835f5e7d251SAlgea Cao 	printf("Rate %lu missing; compute N dynamically\n",
1836f5e7d251SAlgea Cao 	       pixel_clk);
1837f5e7d251SAlgea Cao 
1838f5e7d251SAlgea Cao 	return hdmi_compute_n(hdmi, pixel_clk, sample_rate);
1839f5e7d251SAlgea Cao }
1840f5e7d251SAlgea Cao 
1841f5e7d251SAlgea Cao static
1842f5e7d251SAlgea Cao void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, unsigned long pixel_clk,
1843f5e7d251SAlgea Cao 			      unsigned int sample_rate)
1844f5e7d251SAlgea Cao {
1845f5e7d251SAlgea Cao 	unsigned long ftdms = pixel_clk;
1846f5e7d251SAlgea Cao 	unsigned int n, cts;
1847f5e7d251SAlgea Cao 	u64 tmp;
1848f5e7d251SAlgea Cao 
1849f5e7d251SAlgea Cao 	n = hdmi_find_n(hdmi, pixel_clk, sample_rate);
1850f5e7d251SAlgea Cao 
1851f5e7d251SAlgea Cao 	/*
1852f5e7d251SAlgea Cao 	 * Compute the CTS value from the N value.  Note that CTS and N
1853f5e7d251SAlgea Cao 	 * can be up to 20 bits in total, so we need 64-bit math.  Also
1854f5e7d251SAlgea Cao 	 * note that our TDMS clock is not fully accurate; it is accurate
1855f5e7d251SAlgea Cao 	 * to kHz.  This can introduce an unnecessary remainder in the
1856f5e7d251SAlgea Cao 	 * calculation below, so we don't try to warn about that.
1857f5e7d251SAlgea Cao 	 */
1858f5e7d251SAlgea Cao 	tmp = (u64)ftdms * n;
1859f5e7d251SAlgea Cao 	do_div(tmp, 128 * sample_rate);
1860f5e7d251SAlgea Cao 	cts = tmp;
1861f5e7d251SAlgea Cao 
1862f5e7d251SAlgea Cao 	printf("%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n", __func__,
1863f5e7d251SAlgea Cao 	       sample_rate, ftdms / 1000000, (ftdms / 1000) % 1000, n, cts);
1864f5e7d251SAlgea Cao 
1865f5e7d251SAlgea Cao 	hdmi->audio_n = n;
1866f5e7d251SAlgea Cao 	hdmi->audio_cts = cts;
1867f5e7d251SAlgea Cao 	hdmi_set_cts_n(hdmi, cts, hdmi->audio_enable ? n : 0);
1868f5e7d251SAlgea Cao }
1869f5e7d251SAlgea Cao 
1870f5e7d251SAlgea Cao static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
1871f5e7d251SAlgea Cao {
18728e2bab3fSAlgea Cao 	hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
1873f5e7d251SAlgea Cao 				 hdmi->sample_rate);
1874f5e7d251SAlgea Cao }
1875f5e7d251SAlgea Cao 
1876f5e7d251SAlgea Cao static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi)
1877f5e7d251SAlgea Cao {
1878f5e7d251SAlgea Cao 	hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
1879f5e7d251SAlgea Cao }
1880f5e7d251SAlgea Cao 
1881f5e7d251SAlgea Cao void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
1882f5e7d251SAlgea Cao {
1883f5e7d251SAlgea Cao 	hdmi->sample_rate = rate;
18848e2bab3fSAlgea Cao 	hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
1885f5e7d251SAlgea Cao 				 hdmi->sample_rate);
1886f5e7d251SAlgea Cao }
1887f5e7d251SAlgea Cao 
18888e2bab3fSAlgea Cao static int dw_hdmi_hdcp_load_key(struct dw_hdmi *hdmi)
18898e2bab3fSAlgea Cao {
18908e2bab3fSAlgea Cao 	int i, j, ret, val;
18918e2bab3fSAlgea Cao 	struct hdcp_keys *hdcp_keys;
18928e2bab3fSAlgea Cao 
18938e2bab3fSAlgea Cao 	val = sizeof(*hdcp_keys);
18948e2bab3fSAlgea Cao 	hdcp_keys = malloc(val);
18958e2bab3fSAlgea Cao 	if (!hdcp_keys)
18968e2bab3fSAlgea Cao 		return -ENOMEM;
18978e2bab3fSAlgea Cao 
18988e2bab3fSAlgea Cao 	memset(hdcp_keys, 0, val);
18998e2bab3fSAlgea Cao 
19008e2bab3fSAlgea Cao 	ret = vendor_storage_read(HDMI_HDCP1X_ID, hdcp_keys, val);
19018e2bab3fSAlgea Cao 	if (ret < val) {
19028e2bab3fSAlgea Cao 		printf("HDCP: read size %d\n", ret);
19038e2bab3fSAlgea Cao 		free(hdcp_keys);
19048e2bab3fSAlgea Cao 		return -EINVAL;
19058e2bab3fSAlgea Cao 	}
19068e2bab3fSAlgea Cao 
19078e2bab3fSAlgea Cao 	if (hdcp_keys->KSV[0] == 0x00 &&
19088e2bab3fSAlgea Cao 	    hdcp_keys->KSV[1] == 0x00 &&
19098e2bab3fSAlgea Cao 	    hdcp_keys->KSV[2] == 0x00 &&
19108e2bab3fSAlgea Cao 	    hdcp_keys->KSV[3] == 0x00 &&
19118e2bab3fSAlgea Cao 	    hdcp_keys->KSV[4] == 0x00) {
19128e2bab3fSAlgea Cao 		printf("HDCP: Invalid hdcp key\n");
19138e2bab3fSAlgea Cao 		free(hdcp_keys);
19148e2bab3fSAlgea Cao 		return -EINVAL;
19158e2bab3fSAlgea Cao 	}
19168e2bab3fSAlgea Cao 
19178e2bab3fSAlgea Cao 	/* Disable decryption logic */
19188e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, 0, HDMI_HDCPREG_RMCTL);
19198e2bab3fSAlgea Cao 	/* Poll untile DPK write is allowed */
19208e2bab3fSAlgea Cao 	do {
19218e2bab3fSAlgea Cao 		val = hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS);
19228e2bab3fSAlgea Cao 	} while ((val & DPK_WR_OK_STS) == 0);
19238e2bab3fSAlgea Cao 
19248e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, 0, HDMI_HDCPREG_DPK6);
19258e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, 0, HDMI_HDCPREG_DPK5);
19268e2bab3fSAlgea Cao 
19278e2bab3fSAlgea Cao 	/* The useful data in ksv should be 5 byte */
19288e2bab3fSAlgea Cao 	for (i = 4; i >= 0; i--)
19298e2bab3fSAlgea Cao 		hdmi_writeb(hdmi, hdcp_keys->KSV[i], HDMI_HDCPREG_DPK0 + i);
19308e2bab3fSAlgea Cao 	/* Poll untile DPK write is allowed */
19318e2bab3fSAlgea Cao 	do {
19328e2bab3fSAlgea Cao 		val = hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS);
19338e2bab3fSAlgea Cao 	} while ((val & DPK_WR_OK_STS) == 0);
19348e2bab3fSAlgea Cao 
19358e2bab3fSAlgea Cao 	/* Enable decryption logic */
19368e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, 1, HDMI_HDCPREG_RMCTL);
19378e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, hdcp_keys->seeds[0], HDMI_HDCPREG_SEED1);
19388e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, hdcp_keys->seeds[1], HDMI_HDCPREG_SEED0);
19398e2bab3fSAlgea Cao 
19408e2bab3fSAlgea Cao 	/* Write encrypt device private key */
19418e2bab3fSAlgea Cao 	for (i = 0; i < DW_HDMI_HDCP_DPK_LEN - 6; i += 7) {
19428e2bab3fSAlgea Cao 		for (j = 6; j >= 0; j--)
19438e2bab3fSAlgea Cao 			hdmi_writeb(hdmi, hdcp_keys->devicekey[i + j],
19448e2bab3fSAlgea Cao 				    HDMI_HDCPREG_DPK0 + j);
19458e2bab3fSAlgea Cao 		do {
19468e2bab3fSAlgea Cao 			val = hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS);
19478e2bab3fSAlgea Cao 		} while ((val & DPK_WR_OK_STS) == 0);
19488e2bab3fSAlgea Cao 	}
19498e2bab3fSAlgea Cao 
19508e2bab3fSAlgea Cao 	free(hdcp_keys);
19518e2bab3fSAlgea Cao 	return 0;
19528e2bab3fSAlgea Cao }
19538e2bab3fSAlgea Cao 
19548e2bab3fSAlgea Cao static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi,
19558e2bab3fSAlgea Cao 				const struct drm_display_mode *mode)
19568e2bab3fSAlgea Cao {
19578e2bab3fSAlgea Cao 	u8 vsync_pol, hsync_pol, data_pol, hdmi_dvi;
19588e2bab3fSAlgea Cao 
19598e2bab3fSAlgea Cao 	if (!hdmi->hdcp1x_enable)
19608e2bab3fSAlgea Cao 		return;
19618e2bab3fSAlgea Cao 
19628e2bab3fSAlgea Cao 	/* Configure the video polarity */
19638e2bab3fSAlgea Cao 	vsync_pol = mode->flags & DRM_MODE_FLAG_PVSYNC ?
19648e2bab3fSAlgea Cao 		    HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH :
19658e2bab3fSAlgea Cao 		    HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW;
19668e2bab3fSAlgea Cao 	hsync_pol = mode->flags & DRM_MODE_FLAG_PHSYNC ?
19678e2bab3fSAlgea Cao 		    HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH :
19688e2bab3fSAlgea Cao 		    HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW;
19698e2bab3fSAlgea Cao 	data_pol = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH;
19708e2bab3fSAlgea Cao 	hdmi_modb(hdmi, vsync_pol | hsync_pol | data_pol,
19718e2bab3fSAlgea Cao 		  HDMI_A_VIDPOLCFG_VSYNCPOL_MASK |
19728e2bab3fSAlgea Cao 		  HDMI_A_VIDPOLCFG_HSYNCPOL_MASK |
19738e2bab3fSAlgea Cao 		  HDMI_A_VIDPOLCFG_DATAENPOL_MASK,
19748e2bab3fSAlgea Cao 		  HDMI_A_VIDPOLCFG);
19758e2bab3fSAlgea Cao 
19768e2bab3fSAlgea Cao 	/* Config the display mode */
19778e2bab3fSAlgea Cao 	hdmi_dvi = hdmi->sink_is_hdmi ? HDMI_A_HDCPCFG0_HDMIDVI_HDMI :
19788e2bab3fSAlgea Cao 		   HDMI_A_HDCPCFG0_HDMIDVI_DVI;
19798e2bab3fSAlgea Cao 	hdmi_modb(hdmi, hdmi_dvi, HDMI_A_HDCPCFG0_HDMIDVI_MASK,
19808e2bab3fSAlgea Cao 		  HDMI_A_HDCPCFG0);
19818e2bab3fSAlgea Cao 
19828e2bab3fSAlgea Cao 	if (!(hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS) & 0x3f))
19838e2bab3fSAlgea Cao 		dw_hdmi_hdcp_load_key(hdmi);
19848e2bab3fSAlgea Cao 
19858e2bab3fSAlgea Cao 	hdmi_modb(hdmi, HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE,
19868e2bab3fSAlgea Cao 		  HDMI_FC_INVIDCONF_HDCP_KEEPOUT_MASK,
19878e2bab3fSAlgea Cao 		  HDMI_FC_INVIDCONF);
19888e2bab3fSAlgea Cao 
19898e2bab3fSAlgea Cao 	if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_A_HDCP22_MASK) {
19908e2bab3fSAlgea Cao 		hdmi_modb(hdmi, HDMI_HDCP2_OVR_ENABLE |
19918e2bab3fSAlgea Cao 			  HDMI_HDCP2_FORCE_DISABLE,
19928e2bab3fSAlgea Cao 			  HDMI_HDCP2_OVR_EN_MASK |
19938e2bab3fSAlgea Cao 			  HDMI_HDCP2_FORCE_MASK,
19948e2bab3fSAlgea Cao 			  HDMI_HDCP2REG_CTRL);
19958e2bab3fSAlgea Cao 		hdmi_writeb(hdmi, 0xff, HDMI_HDCP2REG_MASK);
19968e2bab3fSAlgea Cao 		hdmi_writeb(hdmi, 0xff, HDMI_HDCP2REG_MUTE);
19978e2bab3fSAlgea Cao 	}
19988e2bab3fSAlgea Cao 
19998e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, 0x40, HDMI_A_OESSWCFG);
20008e2bab3fSAlgea Cao 		    hdmi_modb(hdmi, HDMI_A_HDCPCFG0_BYPENCRYPTION_DISABLE |
20018e2bab3fSAlgea Cao 		    HDMI_A_HDCPCFG0_EN11FEATURE_DISABLE |
20028e2bab3fSAlgea Cao 		    HDMI_A_HDCPCFG0_SYNCRICHECK_ENABLE,
20038e2bab3fSAlgea Cao 		    HDMI_A_HDCPCFG0_BYPENCRYPTION_MASK |
20048e2bab3fSAlgea Cao 		    HDMI_A_HDCPCFG0_EN11FEATURE_MASK |
20058e2bab3fSAlgea Cao 		    HDMI_A_HDCPCFG0_SYNCRICHECK_MASK, HDMI_A_HDCPCFG0);
20068e2bab3fSAlgea Cao 
20078e2bab3fSAlgea Cao 	hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_ENABLE |
20088e2bab3fSAlgea Cao 		  HDMI_A_HDCPCFG1_PH2UPSHFTENC_ENABLE,
20098e2bab3fSAlgea Cao 		  HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK |
20108e2bab3fSAlgea Cao 		  HDMI_A_HDCPCFG1_PH2UPSHFTENC_MASK, HDMI_A_HDCPCFG1);
20118e2bab3fSAlgea Cao 
20128e2bab3fSAlgea Cao 	/* Reset HDCP Engine */
20138e2bab3fSAlgea Cao 	if (hdmi_readb(hdmi, HDMI_MC_CLKDIS) & HDMI_MC_CLKDIS_HDCPCLK_MASK) {
20148e2bab3fSAlgea Cao 		hdmi_modb(hdmi, HDMI_A_HDCPCFG1_SWRESET_ASSERT,
20158e2bab3fSAlgea Cao 			  HDMI_A_HDCPCFG1_SWRESET_MASK, HDMI_A_HDCPCFG1);
20168e2bab3fSAlgea Cao 	}
20178e2bab3fSAlgea Cao 
20188e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, 0x00, HDMI_A_APIINTMSK);
20198e2bab3fSAlgea Cao 	hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_ENABLE,
20208e2bab3fSAlgea Cao 		  HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0);
20218e2bab3fSAlgea Cao 
20228e2bab3fSAlgea Cao 	hdmi_modb(hdmi, HDMI_MC_CLKDIS_HDCPCLK_ENABLE,
20238e2bab3fSAlgea Cao 		  HDMI_MC_CLKDIS_HDCPCLK_MASK, HDMI_MC_CLKDIS);
20248e2bab3fSAlgea Cao 
20258e2bab3fSAlgea Cao 	printf("%s success\n", __func__);
20268e2bab3fSAlgea Cao }
20278e2bab3fSAlgea Cao 
2028f5e7d251SAlgea Cao static int dw_hdmi_setup(struct dw_hdmi *hdmi,
20298e2bab3fSAlgea Cao 			 struct drm_display_mode *mode,
20308e2bab3fSAlgea Cao 			 struct display_state *state)
2031f5e7d251SAlgea Cao {
2032f5e7d251SAlgea Cao 	int ret;
2033f5e7d251SAlgea Cao 	void *data = hdmi->plat_data->phy_data;
2034f5e7d251SAlgea Cao 
2035f5e7d251SAlgea Cao 	hdmi_disable_overflow_interrupts(hdmi);
2036f5e7d251SAlgea Cao 	if (!hdmi->vic)
2037f5e7d251SAlgea Cao 		printf("Non-CEA mode used in HDMI\n");
2038f5e7d251SAlgea Cao 	else
2039f5e7d251SAlgea Cao 		printf("CEA mode used vic=%d\n", hdmi->vic);
2040f5e7d251SAlgea Cao 
2041f5e7d251SAlgea Cao 	if (hdmi->plat_data->get_enc_out_encoding)
2042f5e7d251SAlgea Cao 		hdmi->hdmi_data.enc_out_encoding =
2043f5e7d251SAlgea Cao 			hdmi->plat_data->get_enc_out_encoding(data);
20445ccad8f6SAlgea Cao 	else if (hdmi->vic == 6 || hdmi->vic == 7 ||
20455ccad8f6SAlgea Cao 		 hdmi->vic == 21 || hdmi->vic == 22 ||
20465ccad8f6SAlgea Cao 		 hdmi->vic == 2 || hdmi->vic == 3 ||
20475ccad8f6SAlgea Cao 		 hdmi->vic == 17 || hdmi->vic == 18)
2048f5e7d251SAlgea Cao 		hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601;
2049f5e7d251SAlgea Cao 	else
2050f5e7d251SAlgea Cao 		hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709;
2051f5e7d251SAlgea Cao 
2052f5e7d251SAlgea Cao 	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
2053f5e7d251SAlgea Cao 		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
2054f5e7d251SAlgea Cao 		hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 1;
2055f5e7d251SAlgea Cao 	} else {
2056f5e7d251SAlgea Cao 		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
2057f5e7d251SAlgea Cao 		hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
2058f5e7d251SAlgea Cao 	}
2059f5e7d251SAlgea Cao 
2060f5e7d251SAlgea Cao 	/* TOFIX: Get input encoding from plat data or fallback to none */
2061f5e7d251SAlgea Cao 	if (hdmi->plat_data->get_enc_in_encoding)
2062f5e7d251SAlgea Cao 		hdmi->hdmi_data.enc_in_encoding =
2063f5e7d251SAlgea Cao 			hdmi->plat_data->get_enc_in_encoding(data);
2064f5e7d251SAlgea Cao 	else if (hdmi->plat_data->input_bus_encoding)
2065f5e7d251SAlgea Cao 		hdmi->hdmi_data.enc_in_encoding =
2066f5e7d251SAlgea Cao 			hdmi->plat_data->input_bus_encoding;
2067f5e7d251SAlgea Cao 	else
2068f5e7d251SAlgea Cao 		hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT;
2069b5016cf2SAlgea Cao 
2070b5016cf2SAlgea Cao 	if (hdmi->plat_data->get_quant_range)
2071b5016cf2SAlgea Cao 		hdmi->hdmi_data.quant_range =
2072b5016cf2SAlgea Cao 			hdmi->plat_data->get_quant_range(data);
2073b5016cf2SAlgea Cao 	else
2074b5016cf2SAlgea Cao 		hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
2075b5016cf2SAlgea Cao 
2076f5e7d251SAlgea Cao 	/*
2077f5e7d251SAlgea Cao 	 * According to the dw-hdmi specification 6.4.2
2078f5e7d251SAlgea Cao 	 * vp_pr_cd[3:0]:
2079f5e7d251SAlgea Cao 	 * 0000b: No pixel repetition (pixel sent only once)
2080f5e7d251SAlgea Cao 	 * 0001b: Pixel sent two times (pixel repeated once)
2081f5e7d251SAlgea Cao 	 */
2082f5e7d251SAlgea Cao 	hdmi->hdmi_data.pix_repet_factor =
2083f5e7d251SAlgea Cao 		(mode->flags & DRM_MODE_FLAG_DBLCLK) ? 1 : 0;
2084f5e7d251SAlgea Cao 	hdmi->hdmi_data.video_mode.mdataenablepolarity = true;
2085f5e7d251SAlgea Cao 
2086f5e7d251SAlgea Cao 	/* HDMI Initialization Step B.1 */
2087f5e7d251SAlgea Cao 	hdmi_av_composer(hdmi, mode);
2088f5e7d251SAlgea Cao 
2089f5e7d251SAlgea Cao 	/* HDMI Initialization Step B.2 */
20908e2bab3fSAlgea Cao 	ret = hdmi->phy.ops->init(hdmi, state);
2091f5e7d251SAlgea Cao 	if (ret)
2092f5e7d251SAlgea Cao 		return ret;
2093f5e7d251SAlgea Cao 	hdmi->phy.enabled = true;
2094f5e7d251SAlgea Cao 
2095f5e7d251SAlgea Cao 	/* HDMI Initializateion Step B.3 */
2096f5e7d251SAlgea Cao 	dw_hdmi_enable_video_path(hdmi);
2097f5e7d251SAlgea Cao 
2098f5e7d251SAlgea Cao 	/* HDMI Initialization Step E - Configure audio */
2099f5e7d251SAlgea Cao 	if (hdmi->sink_has_audio) {
2100f5e7d251SAlgea Cao 		printf("sink has audio support\n");
2101f5e7d251SAlgea Cao 		hdmi_clk_regenerator_update_pixel_clock(hdmi);
2102f5e7d251SAlgea Cao 		hdmi_enable_audio_clk(hdmi);
2103f5e7d251SAlgea Cao 	}
2104f5e7d251SAlgea Cao 
2105f5e7d251SAlgea Cao 	/* not for DVI mode */
2106f5e7d251SAlgea Cao 	if (hdmi->sink_is_hdmi) {
2107f5e7d251SAlgea Cao 		/* HDMI Initialization Step F - Configure AVI InfoFrame */
2108f5e7d251SAlgea Cao 		hdmi_config_AVI(hdmi, mode);
2109f5e7d251SAlgea Cao 		hdmi_config_vendor_specific_infoframe(hdmi, mode);
2110b327b539SShunqing Chen 		hdmi_modb(hdmi, HDMI_A_HDCPCFG0_HDMIDVI_HDMI,
2111b327b539SShunqing Chen 			  HDMI_A_HDCPCFG0_HDMIDVI_MASK,
2112b327b539SShunqing Chen 			  HDMI_A_HDCPCFG0);
2113f5e7d251SAlgea Cao 	} else {
2114b327b539SShunqing Chen 		hdmi_modb(hdmi, HDMI_A_HDCPCFG0_HDMIDVI_DVI,
2115b327b539SShunqing Chen 			  HDMI_A_HDCPCFG0_HDMIDVI_MASK,
2116b327b539SShunqing Chen 			  HDMI_A_HDCPCFG0);
2117f5e7d251SAlgea Cao 		printf("%s DVI mode\n", __func__);
2118f5e7d251SAlgea Cao 	}
2119f5e7d251SAlgea Cao 
2120f5e7d251SAlgea Cao 	hdmi_video_packetize(hdmi);
2121f5e7d251SAlgea Cao 	hdmi_video_csc(hdmi);
2122f5e7d251SAlgea Cao 	hdmi_video_sample(hdmi);
21238e2bab3fSAlgea Cao 	hdmi_tx_hdcp_config(hdmi, mode);
2124f5e7d251SAlgea Cao 	dw_hdmi_clear_overflow(hdmi);
2125f5e7d251SAlgea Cao 
2126f5e7d251SAlgea Cao 	return 0;
2127f5e7d251SAlgea Cao }
2128f5e7d251SAlgea Cao 
21298e2bab3fSAlgea Cao int dw_hdmi_detect_hotplug(struct dw_hdmi *hdmi,
21308e2bab3fSAlgea Cao 			   struct display_state *state)
2131f5e7d251SAlgea Cao {
21328e2bab3fSAlgea Cao 	return hdmi->phy.ops->read_hpd(hdmi, state);
2133f5e7d251SAlgea Cao }
2134f5e7d251SAlgea Cao 
2135f5e7d251SAlgea Cao static int dw_hdmi_set_reg_wr(struct dw_hdmi *hdmi)
2136f5e7d251SAlgea Cao {
2137f5e7d251SAlgea Cao 	switch (hdmi->io_width) {
2138f5e7d251SAlgea Cao 	case 4:
2139f5e7d251SAlgea Cao 		hdmi->write = dw_hdmi_writel;
2140f5e7d251SAlgea Cao 		hdmi->read = dw_hdmi_readl;
2141f5e7d251SAlgea Cao 		break;
2142f5e7d251SAlgea Cao 	case 1:
2143f5e7d251SAlgea Cao 		hdmi->write = dw_hdmi_writeb;
2144f5e7d251SAlgea Cao 		hdmi->read = dw_hdmi_readb;
2145f5e7d251SAlgea Cao 		break;
2146f5e7d251SAlgea Cao 	default:
2147f5e7d251SAlgea Cao 		printf("reg-io-width must be 1 or 4\n");
2148f5e7d251SAlgea Cao 		return -EINVAL;
2149f5e7d251SAlgea Cao 	}
2150f5e7d251SAlgea Cao 
2151f5e7d251SAlgea Cao 	return 0;
2152f5e7d251SAlgea Cao }
2153f5e7d251SAlgea Cao 
2154f5e7d251SAlgea Cao static void initialize_hdmi_mutes(struct dw_hdmi *hdmi)
2155f5e7d251SAlgea Cao {
2156f5e7d251SAlgea Cao 	/*mute unnecessary interrupt, only enable hpd */
21575ccad8f6SAlgea Cao 	hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK0);
21585ccad8f6SAlgea Cao 	hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK1);
21595ccad8f6SAlgea Cao 	hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK2);
2160f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0);
2161f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1);
2162f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2);
2163f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0);
2164f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0xfe, HDMI_IH_MUTE_PHY_STAT0);
2165f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0);
2166f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0);
2167f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0);
2168f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
2169f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
2170f5e7d251SAlgea Cao 	hdmi_writeb(hdmi, 0xf1, HDMI_PHY_MASK0);
2171f5e7d251SAlgea Cao 
2172f5e7d251SAlgea Cao 	/*Force output black*/
2173f5e7d251SAlgea Cao 	dw_hdmi_writel(hdmi, 0x00, HDMI_FC_DBGTMDS2);
2174f5e7d251SAlgea Cao 	dw_hdmi_writel(hdmi, 0x00, HDMI_FC_DBGTMDS1);
2175f5e7d251SAlgea Cao 	dw_hdmi_writel(hdmi, 0x00, HDMI_FC_DBGTMDS0);
2176f5e7d251SAlgea Cao }
2177f5e7d251SAlgea Cao 
2178f5e7d251SAlgea Cao static void dw_hdmi_dev_init(struct dw_hdmi *hdmi)
2179f5e7d251SAlgea Cao {
2180f5e7d251SAlgea Cao 	hdmi->version = (hdmi_readb(hdmi, HDMI_DESIGN_ID) << 8)
2181f5e7d251SAlgea Cao 		      | (hdmi_readb(hdmi, HDMI_REVISION_ID) << 0);
2182f5e7d251SAlgea Cao 
2183f5e7d251SAlgea Cao 	initialize_hdmi_mutes(hdmi);
2184f5e7d251SAlgea Cao }
2185f5e7d251SAlgea Cao 
21868e2bab3fSAlgea Cao static void dw_hdmi_i2c_set_divs(struct dw_hdmi *hdmi)
2187f5e7d251SAlgea Cao {
21888e2bab3fSAlgea Cao 	unsigned long low_ns, high_ns;
21898e2bab3fSAlgea Cao 	unsigned long div_low, div_high;
2190f5e7d251SAlgea Cao 
21918e2bab3fSAlgea Cao 	/* Standard-mode */
21928e2bab3fSAlgea Cao 	if (hdmi->i2c->scl_high_ns < 4000)
21938e2bab3fSAlgea Cao 		high_ns = 4708;
2194f5e7d251SAlgea Cao 	else
21958e2bab3fSAlgea Cao 		high_ns = hdmi->i2c->scl_high_ns;
2196f5e7d251SAlgea Cao 
21978e2bab3fSAlgea Cao 	if (hdmi->i2c->scl_low_ns < 4700)
21988e2bab3fSAlgea Cao 		low_ns = 4916;
21998e2bab3fSAlgea Cao 	else
22008e2bab3fSAlgea Cao 		low_ns = hdmi->i2c->scl_low_ns;
22018e2bab3fSAlgea Cao 
22028e2bab3fSAlgea Cao 	div_low = (24000 * low_ns) / 1000000;
22038e2bab3fSAlgea Cao 	if ((24000 * low_ns) % 1000000)
22048e2bab3fSAlgea Cao 		div_low++;
22058e2bab3fSAlgea Cao 
22068e2bab3fSAlgea Cao 	div_high = (24000 * high_ns) / 1000000;
22078e2bab3fSAlgea Cao 	if ((24000 * high_ns) % 1000000)
22088e2bab3fSAlgea Cao 		div_high++;
22098e2bab3fSAlgea Cao 
22108e2bab3fSAlgea Cao 	/* Maximum divider supported by hw is 0xffff */
22118e2bab3fSAlgea Cao 	if (div_low > 0xffff)
22128e2bab3fSAlgea Cao 		div_low = 0xffff;
22138e2bab3fSAlgea Cao 
22148e2bab3fSAlgea Cao 	if (div_high > 0xffff)
22158e2bab3fSAlgea Cao 		div_high = 0xffff;
22168e2bab3fSAlgea Cao 
22178e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, div_high & 0xff, HDMI_I2CM_SS_SCL_HCNT_0_ADDR);
22188e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, (div_high >> 8) & 0xff,
22198e2bab3fSAlgea Cao 		    HDMI_I2CM_SS_SCL_HCNT_1_ADDR);
22208e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, div_low & 0xff, HDMI_I2CM_SS_SCL_LCNT_0_ADDR);
22218e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, (div_low >> 8) & 0xff,
22228e2bab3fSAlgea Cao 		    HDMI_I2CM_SS_SCL_LCNT_1_ADDR);
2223f5e7d251SAlgea Cao }
2224f5e7d251SAlgea Cao 
22258e2bab3fSAlgea Cao static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
2226f5e7d251SAlgea Cao {
22278e2bab3fSAlgea Cao 	/* Software reset */
22288e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ);
2229f5e7d251SAlgea Cao 
22308e2bab3fSAlgea Cao 	/* Set Standard Mode speed */
22318e2bab3fSAlgea Cao 	hdmi_modb(hdmi, HDMI_I2CM_DIV_STD_MODE,
22328e2bab3fSAlgea Cao 		  HDMI_I2CM_DIV_FAST_STD_MODE, HDMI_I2CM_DIV);
2233f5e7d251SAlgea Cao 
22348e2bab3fSAlgea Cao 	/* Set done, not acknowledged and arbitration interrupt polarities */
22358e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, HDMI_I2CM_INT_DONE_POL, HDMI_I2CM_INT);
22368e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, HDMI_I2CM_CTLINT_NAC_POL | HDMI_I2CM_CTLINT_ARB_POL,
22378e2bab3fSAlgea Cao 		    HDMI_I2CM_CTLINT);
2238f5e7d251SAlgea Cao 
22398e2bab3fSAlgea Cao 	/* Clear DONE and ERROR interrupts */
22408e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
22418e2bab3fSAlgea Cao 		    HDMI_IH_I2CM_STAT0);
2242f5e7d251SAlgea Cao 
22438e2bab3fSAlgea Cao 	/* Mute DONE and ERROR interrupts */
22448e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
22458e2bab3fSAlgea Cao 		    HDMI_IH_MUTE_I2CM_STAT0);
2246f5e7d251SAlgea Cao 
22478e2bab3fSAlgea Cao 	/* set SDA high level holding time */
22488e2bab3fSAlgea Cao 	hdmi_writeb(hdmi, 0x48, HDMI_I2CM_SDA_HOLD);
2249f5e7d251SAlgea Cao 
22508e2bab3fSAlgea Cao 	dw_hdmi_i2c_set_divs(hdmi);
2251f5e7d251SAlgea Cao }
2252f5e7d251SAlgea Cao 
2253f5e7d251SAlgea Cao void dw_hdmi_audio_enable(struct dw_hdmi *hdmi)
2254f5e7d251SAlgea Cao {
2255f5e7d251SAlgea Cao 	hdmi->audio_enable = true;
2256f5e7d251SAlgea Cao 	hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
2257f5e7d251SAlgea Cao }
2258f5e7d251SAlgea Cao 
2259f5e7d251SAlgea Cao void dw_hdmi_audio_disable(struct dw_hdmi *hdmi)
2260f5e7d251SAlgea Cao {
2261f5e7d251SAlgea Cao 	hdmi->audio_enable = false;
2262f5e7d251SAlgea Cao 	hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
2263f5e7d251SAlgea Cao }
2264f5e7d251SAlgea Cao 
226558c17f51SSandy Huang int rockchip_dw_hdmi_pre_init(struct display_state *state)
226658c17f51SSandy Huang {
226758c17f51SSandy Huang 	struct connector_state *conn_state = &state->conn_state;
226858c17f51SSandy Huang 
226958c17f51SSandy Huang 	conn_state->type = DRM_MODE_CONNECTOR_HDMIA;
227058c17f51SSandy Huang 
227158c17f51SSandy Huang 	return 0;
227258c17f51SSandy Huang }
227358c17f51SSandy Huang 
2274f5e7d251SAlgea Cao int rockchip_dw_hdmi_init(struct display_state *state)
2275f5e7d251SAlgea Cao {
2276f5e7d251SAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
2277f5e7d251SAlgea Cao 	const struct rockchip_connector *connector = conn_state->connector;
2278f5e7d251SAlgea Cao 	const struct dw_hdmi_plat_data *pdata = connector->data;
2279f5e7d251SAlgea Cao 	struct crtc_state *crtc_state = &state->crtc_state;
2280f5e7d251SAlgea Cao 	struct dw_hdmi *hdmi;
2281f5e7d251SAlgea Cao 	struct drm_display_mode *mode_buf;
2282e2bce6e4SKever Yang 	ofnode hdmi_node = conn_state->node;
2283f5e7d251SAlgea Cao 	u32 val;
22841f71919fSShunqing Chen 	struct device_node *ddc_node;
2285*cb17ca6cSSandy Huang 	int id;
2286f5e7d251SAlgea Cao 
2287f5e7d251SAlgea Cao 	hdmi = malloc(sizeof(struct dw_hdmi));
2288f5e7d251SAlgea Cao 	if (!hdmi)
2289f5e7d251SAlgea Cao 		return -ENOMEM;
2290*cb17ca6cSSandy Huang 	id = of_alias_get_id(ofnode_to_np(hdmi_node), "hdmi");
2291*cb17ca6cSSandy Huang 	if (id < 0)
2292*cb17ca6cSSandy Huang 		id = 0;
2293*cb17ca6cSSandy Huang 	hdmi->id = id;
2294*cb17ca6cSSandy Huang 	conn_state->disp_info  = rockchip_get_disp_info(conn_state->type, hdmi->id);
2295f5e7d251SAlgea Cao 
2296f5e7d251SAlgea Cao 	memset(hdmi, 0, sizeof(struct dw_hdmi));
2297f5e7d251SAlgea Cao 	mode_buf = malloc(MODE_LEN * sizeof(struct drm_display_mode));
2298f5e7d251SAlgea Cao 	if (!mode_buf)
2299f5e7d251SAlgea Cao 		return -ENOMEM;
2300*cb17ca6cSSandy Huang 	hdmi->id = of_alias_get_id(ofnode_to_np(hdmi_node), "hdmi");
2301*cb17ca6cSSandy Huang 	if (hdmi->id < 0)
2302*cb17ca6cSSandy Huang 		hdmi->id = 0;
2303*cb17ca6cSSandy Huang 	conn_state->disp_info  = rockchip_get_disp_info(conn_state->type, hdmi->id);
23048e2bab3fSAlgea Cao 
2305f5e7d251SAlgea Cao 	memset(mode_buf, 0, MODE_LEN * sizeof(struct drm_display_mode));
2306f5e7d251SAlgea Cao 
2307e2bce6e4SKever Yang 	hdmi->regs = dev_read_addr_ptr(conn_state->dev);
2308e2bce6e4SKever Yang 	hdmi->io_width = ofnode_read_s32_default(hdmi_node, "reg-io-width", -1);
23098e2bab3fSAlgea Cao 
23108e2bab3fSAlgea Cao 	if (ofnode_read_bool(hdmi_node, "scramble-low-rates"))
23118e2bab3fSAlgea Cao 		hdmi->scramble_low_rates = true;
23128e2bab3fSAlgea Cao 
23138e2bab3fSAlgea Cao 	if (ofnode_read_bool(hdmi_node, "hdcp1x-enable"))
23148e2bab3fSAlgea Cao 		hdmi->hdcp1x_enable = true;
23158e2bab3fSAlgea Cao 	else
23168e2bab3fSAlgea Cao 		hdmi->hdcp1x_enable = false;
23178e2bab3fSAlgea Cao 
23181f71919fSShunqing Chen 	ddc_node = of_parse_phandle(ofnode_to_np(hdmi_node), "ddc-i2c-bus", 0);
23191f71919fSShunqing Chen 	if (ddc_node) {
23201f71919fSShunqing Chen 		uclass_get_device_by_ofnode(UCLASS_I2C, np_to_ofnode(ddc_node),
23211f71919fSShunqing Chen 					    &hdmi->adap.i2c_bus);
23221f71919fSShunqing Chen 		if (hdmi->adap.i2c_bus)
23231f71919fSShunqing Chen 			hdmi->adap.ops = i2c_get_ops(hdmi->adap.i2c_bus);
23241f71919fSShunqing Chen 	}
23251f71919fSShunqing Chen 
2326f5e7d251SAlgea Cao 	hdmi->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
2327f5e7d251SAlgea Cao 	if (hdmi->grf <= 0) {
2328f5e7d251SAlgea Cao 		printf("%s: Get syscon grf failed (ret=%p)\n",
2329f5e7d251SAlgea Cao 		       __func__, hdmi->grf);
2330f5e7d251SAlgea Cao 		return -ENXIO;
2331f5e7d251SAlgea Cao 	}
2332f5e7d251SAlgea Cao 
2333f5e7d251SAlgea Cao 	dw_hdmi_set_reg_wr(hdmi);
2334f5e7d251SAlgea Cao 
23358e2bab3fSAlgea Cao 	if (pdata->grf_vop_sel_reg) {
2336f5e7d251SAlgea Cao 		if (crtc_state->crtc_id)
2337f5e7d251SAlgea Cao 			val = ((1 << pdata->vop_sel_bit) |
2338f5e7d251SAlgea Cao 			       (1 << (16 + pdata->vop_sel_bit)));
2339f5e7d251SAlgea Cao 		else
2340f5e7d251SAlgea Cao 			val = ((0 << pdata->vop_sel_bit) |
2341f5e7d251SAlgea Cao 			       (1 << (16 + pdata->vop_sel_bit)));
2342f5e7d251SAlgea Cao 		writel(val, hdmi->grf + pdata->grf_vop_sel_reg);
23438e2bab3fSAlgea Cao 	}
2344f5e7d251SAlgea Cao 
23458e2bab3fSAlgea Cao 	hdmi->i2c = malloc(sizeof(struct dw_hdmi_i2c));
23468e2bab3fSAlgea Cao 	if (!hdmi->i2c)
23478e2bab3fSAlgea Cao 		return -ENOMEM;
23488e2bab3fSAlgea Cao 	hdmi->adap.ddc_xfer = dw_hdmi_i2c_xfer;
23498e2bab3fSAlgea Cao 
23508e2bab3fSAlgea Cao 	/*
23518e2bab3fSAlgea Cao 	 * Read high and low time from device tree. If not available use
23528e2bab3fSAlgea Cao 	 * the default timing scl clock rate is about 99.6KHz.
23538e2bab3fSAlgea Cao 	 */
23548e2bab3fSAlgea Cao 	hdmi->i2c->scl_high_ns =
23558e2bab3fSAlgea Cao 		ofnode_read_s32_default(hdmi_node,
235619c2faf2SAlgea Cao 					"ddc-i2c-scl-high-time-ns", 4708);
23578e2bab3fSAlgea Cao 	hdmi->i2c->scl_low_ns =
23588e2bab3fSAlgea Cao 		ofnode_read_s32_default(hdmi_node,
235919c2faf2SAlgea Cao 					"ddc-i2c-scl-low-time-ns", 4916);
23608e2bab3fSAlgea Cao 
23618e2bab3fSAlgea Cao 	dw_hdmi_i2c_init(hdmi);
23625ccad8f6SAlgea Cao 	conn_state->output_if |= VOP_OUTPUT_IF_HDMI0;
2363f5e7d251SAlgea Cao 	conn_state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
2364f5e7d251SAlgea Cao 
2365f5e7d251SAlgea Cao 	hdmi->dev_type = pdata->dev_type;
2366f5e7d251SAlgea Cao 	hdmi->plat_data = pdata;
2367f5e7d251SAlgea Cao 	hdmi->edid_data.mode_buf = mode_buf;
2368f5e7d251SAlgea Cao 	hdmi->sample_rate = 48000;
2369f5e7d251SAlgea Cao 
2370f5e7d251SAlgea Cao 	conn_state->private = hdmi;
23718e2bab3fSAlgea Cao 	dw_hdmi_set_iomux(hdmi->grf, hdmi->dev_type);
2372f5e7d251SAlgea Cao 	dw_hdmi_detect_phy(hdmi);
2373f5e7d251SAlgea Cao 	dw_hdmi_dev_init(hdmi);
2374f5e7d251SAlgea Cao 
2375f5e7d251SAlgea Cao 	return 0;
2376f5e7d251SAlgea Cao }
2377f5e7d251SAlgea Cao 
2378f5e7d251SAlgea Cao void rockchip_dw_hdmi_deinit(struct display_state *state)
2379f5e7d251SAlgea Cao {
2380f5e7d251SAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
2381f5e7d251SAlgea Cao 	struct dw_hdmi *hdmi = conn_state->private;
2382f5e7d251SAlgea Cao 
23838e2bab3fSAlgea Cao 	if (hdmi->i2c)
23848e2bab3fSAlgea Cao 		free(hdmi->i2c);
2385f5e7d251SAlgea Cao 	if (hdmi->edid_data.mode_buf)
2386f5e7d251SAlgea Cao 		free(hdmi->edid_data.mode_buf);
2387f5e7d251SAlgea Cao 	if (hdmi)
2388f5e7d251SAlgea Cao 		free(hdmi);
2389f5e7d251SAlgea Cao }
2390f5e7d251SAlgea Cao 
2391f5e7d251SAlgea Cao int rockchip_dw_hdmi_prepare(struct display_state *state)
2392f5e7d251SAlgea Cao {
2393f5e7d251SAlgea Cao 	return 0;
2394f5e7d251SAlgea Cao }
2395f5e7d251SAlgea Cao 
2396f5e7d251SAlgea Cao int rockchip_dw_hdmi_enable(struct display_state *state)
2397f5e7d251SAlgea Cao {
2398f5e7d251SAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
2399f5e7d251SAlgea Cao 	struct drm_display_mode *mode = &conn_state->mode;
2400f5e7d251SAlgea Cao 	struct dw_hdmi *hdmi = conn_state->private;
2401f5e7d251SAlgea Cao 
2402f5e7d251SAlgea Cao 	if (!hdmi)
2403f5e7d251SAlgea Cao 		return -EFAULT;
2404f5e7d251SAlgea Cao 
24058e2bab3fSAlgea Cao 	/* Store the display mode for plugin/DKMS poweron events */
24068e2bab3fSAlgea Cao 	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
24078e2bab3fSAlgea Cao 
24088e2bab3fSAlgea Cao 	dw_hdmi_setup(hdmi, mode, state);
2409f5e7d251SAlgea Cao 
2410f5e7d251SAlgea Cao 	return 0;
2411f5e7d251SAlgea Cao }
2412f5e7d251SAlgea Cao 
2413f5e7d251SAlgea Cao int rockchip_dw_hdmi_disable(struct display_state *state)
2414f5e7d251SAlgea Cao {
2415f5e7d251SAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
2416f5e7d251SAlgea Cao 	struct dw_hdmi *hdmi = conn_state->private;
2417f5e7d251SAlgea Cao 
24188e2bab3fSAlgea Cao 	dw_hdmi_disable(hdmi, state);
2419f5e7d251SAlgea Cao 	return 0;
2420f5e7d251SAlgea Cao }
2421f5e7d251SAlgea Cao 
2422f5e7d251SAlgea Cao int rockchip_dw_hdmi_get_timing(struct display_state *state)
2423f5e7d251SAlgea Cao {
24248e2bab3fSAlgea Cao 	int ret, i;
2425f5e7d251SAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
2426f5e7d251SAlgea Cao 	struct drm_display_mode *mode = &conn_state->mode;
2427f5e7d251SAlgea Cao 	struct dw_hdmi *hdmi = conn_state->private;
24288e2bab3fSAlgea Cao 	struct edid *edid = (struct edid *)conn_state->edid;
24298e2bab3fSAlgea Cao 	unsigned int bus_format;
24305ccad8f6SAlgea Cao 	unsigned long enc_out_encoding;
24318e2bab3fSAlgea Cao 	struct overscan *overscan = &conn_state->overscan;
24328e2bab3fSAlgea Cao 	const u8 def_modes_vic[6] = {4, 16, 2, 17, 31, 19};
2433f5e7d251SAlgea Cao 
2434f5e7d251SAlgea Cao 	if (!hdmi)
2435f5e7d251SAlgea Cao 		return -EFAULT;
2436f5e7d251SAlgea Cao 
24378e2bab3fSAlgea Cao 	ret = drm_do_get_edid(&hdmi->adap, conn_state->edid);
24385ccad8f6SAlgea Cao 
24398e2bab3fSAlgea Cao 	if (!ret) {
2440f5e7d251SAlgea Cao 		hdmi->sink_is_hdmi =
24418e2bab3fSAlgea Cao 			drm_detect_hdmi_monitor(edid);
24428e2bab3fSAlgea Cao 		hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
24438e2bab3fSAlgea Cao 		ret = drm_add_edid_modes(&hdmi->edid_data, conn_state->edid);
24448e2bab3fSAlgea Cao 	}
2445c613b7eaSAlgea Cao 	if (ret < 0) {
24468e2bab3fSAlgea Cao 		hdmi->sink_is_hdmi = true;
24478e2bab3fSAlgea Cao 		hdmi->sink_has_audio = true;
24488e2bab3fSAlgea Cao 		do_cea_modes(&hdmi->edid_data, def_modes_vic,
24498e2bab3fSAlgea Cao 			     sizeof(def_modes_vic));
24508e2bab3fSAlgea Cao 		hdmi->edid_data.preferred_mode = &hdmi->edid_data.mode_buf[0];
24518e2bab3fSAlgea Cao 		printf("failed to get edid\n");
24528e2bab3fSAlgea Cao 	}
24538e2bab3fSAlgea Cao 	drm_rk_filter_whitelist(&hdmi->edid_data);
24548e2bab3fSAlgea Cao 	if (hdmi->phy.ops->mode_valid)
24558e2bab3fSAlgea Cao 		hdmi->phy.ops->mode_valid(hdmi, state);
2456cf53642aSSandy Huang 	drm_mode_max_resolution_filter(&hdmi->edid_data,
2457cf53642aSSandy Huang 				       &state->crtc_state.max_output);
24588e2bab3fSAlgea Cao 	if (!drm_mode_prune_invalid(&hdmi->edid_data)) {
24598e2bab3fSAlgea Cao 		printf("can't find valid hdmi mode\n");
24608e2bab3fSAlgea Cao 		return -EINVAL;
24618e2bab3fSAlgea Cao 	}
24628e2bab3fSAlgea Cao 
24638e2bab3fSAlgea Cao 	for (i = 0; i < hdmi->edid_data.modes; i++)
24648e2bab3fSAlgea Cao 		hdmi->edid_data.mode_buf[i].vrefresh =
24658e2bab3fSAlgea Cao 			drm_mode_vrefresh(&hdmi->edid_data.mode_buf[i]);
24668e2bab3fSAlgea Cao 
24678e2bab3fSAlgea Cao 	drm_mode_sort(&hdmi->edid_data);
24688e2bab3fSAlgea Cao 	drm_rk_selete_output(&hdmi->edid_data, &bus_format,
24698e2bab3fSAlgea Cao 			     overscan, hdmi->dev_type);
24708e2bab3fSAlgea Cao 
2471f5e7d251SAlgea Cao 	*mode = *hdmi->edid_data.preferred_mode;
2472f5e7d251SAlgea Cao 	hdmi->vic = drm_match_cea_mode(mode);
2473f5e7d251SAlgea Cao 
24748e2bab3fSAlgea Cao 	printf("mode:%dx%d\n", mode->hdisplay, mode->vdisplay);
24758e2bab3fSAlgea Cao 	conn_state->bus_format = bus_format;
24768e2bab3fSAlgea Cao 	hdmi->hdmi_data.enc_in_bus_format = bus_format;
24778e2bab3fSAlgea Cao 	hdmi->hdmi_data.enc_out_bus_format = bus_format;
2478f5e7d251SAlgea Cao 
24798e2bab3fSAlgea Cao 	switch (bus_format) {
24808e2bab3fSAlgea Cao 	case MEDIA_BUS_FMT_UYVY10_1X20:
24818e2bab3fSAlgea Cao 		conn_state->bus_format = MEDIA_BUS_FMT_YUV10_1X30;
24828e2bab3fSAlgea Cao 		hdmi->hdmi_data.enc_in_bus_format =
24838e2bab3fSAlgea Cao 			MEDIA_BUS_FMT_YUV10_1X30;
24848e2bab3fSAlgea Cao 		break;
24858e2bab3fSAlgea Cao 	case MEDIA_BUS_FMT_UYVY8_1X16:
24868e2bab3fSAlgea Cao 		conn_state->bus_format = MEDIA_BUS_FMT_YUV8_1X24;
24878e2bab3fSAlgea Cao 		hdmi->hdmi_data.enc_in_bus_format =
24888e2bab3fSAlgea Cao 			MEDIA_BUS_FMT_YUV8_1X24;
24898e2bab3fSAlgea Cao 		break;
24908e2bab3fSAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
24918e2bab3fSAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
24928e2bab3fSAlgea Cao 		conn_state->output_mode = ROCKCHIP_OUT_MODE_YUV420;
24938e2bab3fSAlgea Cao 		break;
24948e2bab3fSAlgea Cao 	}
2495f5e7d251SAlgea Cao 
24965ccad8f6SAlgea Cao 	if (hdmi->vic == 6 || hdmi->vic == 7 || hdmi->vic == 21 ||
24975ccad8f6SAlgea Cao 	    hdmi->vic == 22 || hdmi->vic == 2 || hdmi->vic == 3 ||
24985ccad8f6SAlgea Cao 	    hdmi->vic == 17 || hdmi->vic == 18)
24995ccad8f6SAlgea Cao 		enc_out_encoding = V4L2_YCBCR_ENC_601;
25005ccad8f6SAlgea Cao 	else
25015ccad8f6SAlgea Cao 		enc_out_encoding = V4L2_YCBCR_ENC_709;
25025ccad8f6SAlgea Cao 
25035ccad8f6SAlgea Cao 	if (enc_out_encoding == V4L2_YCBCR_ENC_BT2020)
25045ccad8f6SAlgea Cao 		conn_state->color_space = V4L2_COLORSPACE_BT2020;
25055ccad8f6SAlgea Cao 	else if (bus_format == MEDIA_BUS_FMT_RGB888_1X24 ||
25065ccad8f6SAlgea Cao 		 bus_format == MEDIA_BUS_FMT_RGB101010_1X30)
25075ccad8f6SAlgea Cao 		conn_state->color_space = V4L2_COLORSPACE_DEFAULT;
25085ccad8f6SAlgea Cao 	else if (enc_out_encoding == V4L2_YCBCR_ENC_709)
25095ccad8f6SAlgea Cao 		conn_state->color_space = V4L2_COLORSPACE_REC709;
25105ccad8f6SAlgea Cao 	else
25115ccad8f6SAlgea Cao 		conn_state->color_space = V4L2_COLORSPACE_SMPTE170M;
25125ccad8f6SAlgea Cao 
2513f5e7d251SAlgea Cao 	return 0;
2514f5e7d251SAlgea Cao }
2515f5e7d251SAlgea Cao 
2516f5e7d251SAlgea Cao int rockchip_dw_hdmi_detect(struct display_state *state)
2517f5e7d251SAlgea Cao {
2518f5e7d251SAlgea Cao 	int ret;
2519f5e7d251SAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
2520f5e7d251SAlgea Cao 	struct dw_hdmi *hdmi = conn_state->private;
2521f5e7d251SAlgea Cao 
2522f5e7d251SAlgea Cao 	if (!hdmi)
2523f5e7d251SAlgea Cao 		return -EFAULT;
2524f5e7d251SAlgea Cao 
25258e2bab3fSAlgea Cao 	ret = dw_hdmi_detect_hotplug(hdmi, state);
2526f5e7d251SAlgea Cao 
2527f5e7d251SAlgea Cao 	return ret;
2528f5e7d251SAlgea Cao }
2529f5e7d251SAlgea Cao 
2530f5e7d251SAlgea Cao int rockchip_dw_hdmi_get_edid(struct display_state *state)
2531f5e7d251SAlgea Cao {
2532f5e7d251SAlgea Cao 	int ret;
2533f5e7d251SAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
2534f5e7d251SAlgea Cao 	struct dw_hdmi *hdmi = conn_state->private;
2535f5e7d251SAlgea Cao 
25368e2bab3fSAlgea Cao 	ret = drm_do_get_edid(&hdmi->adap, conn_state->edid);
2537f5e7d251SAlgea Cao 
2538f5e7d251SAlgea Cao 	return ret;
2539f5e7d251SAlgea Cao }
2540f5e7d251SAlgea Cao 
25418e2bab3fSAlgea Cao int inno_dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data)
25428e2bab3fSAlgea Cao {
25438e2bab3fSAlgea Cao 	struct display_state *state = (struct display_state *)data;
25448e2bab3fSAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
25458e2bab3fSAlgea Cao 	u32 color_depth, bus_width;
25468e2bab3fSAlgea Cao 
25478e2bab3fSAlgea Cao 	color_depth =
25488e2bab3fSAlgea Cao 		hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
25498e2bab3fSAlgea Cao 
25508e2bab3fSAlgea Cao 	if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
25518e2bab3fSAlgea Cao 		bus_width = color_depth / 2;
25528e2bab3fSAlgea Cao 	else if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
25538e2bab3fSAlgea Cao 		bus_width = color_depth;
25548e2bab3fSAlgea Cao 	else
25558e2bab3fSAlgea Cao 		bus_width = 8;
255615081c50SWyon Bi 	rockchip_phy_set_bus_width(conn_state->phy, bus_width);
255715081c50SWyon Bi 	rockchip_phy_set_pll(conn_state->phy,
255815081c50SWyon Bi 			     conn_state->mode.crtc_clock * 1000);
25598e2bab3fSAlgea Cao 	if (hdmi->edid_data.display_info.hdmi.scdc.supported)
25608e2bab3fSAlgea Cao 		rockchip_dw_hdmi_scdc_set_tmds_rate(hdmi);
256115081c50SWyon Bi 	rockchip_phy_power_on(conn_state->phy);
25628e2bab3fSAlgea Cao 
25638e2bab3fSAlgea Cao 	return 0;
25648e2bab3fSAlgea Cao }
25658e2bab3fSAlgea Cao 
25668e2bab3fSAlgea Cao void inno_dw_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
25678e2bab3fSAlgea Cao {
25688e2bab3fSAlgea Cao }
25698e2bab3fSAlgea Cao 
25708e2bab3fSAlgea Cao enum drm_connector_status
25718e2bab3fSAlgea Cao inno_dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, void *data)
25728e2bab3fSAlgea Cao {
25738e2bab3fSAlgea Cao 	enum drm_connector_status status;
25748e2bab3fSAlgea Cao 	struct display_state *state = (struct display_state *)data;
25758e2bab3fSAlgea Cao 
25768e2bab3fSAlgea Cao 	status = dw_hdmi_phy_read_hpd(hdmi, state);
25778e2bab3fSAlgea Cao 
25788e2bab3fSAlgea Cao 	if (hdmi->dev_type == RK3328_HDMI) {
25798e2bab3fSAlgea Cao 		if (status == connector_status_connected)
25808e2bab3fSAlgea Cao 			inno_dw_hdmi_set_domain(hdmi->grf, 1);
25818e2bab3fSAlgea Cao 		else
25828e2bab3fSAlgea Cao 			inno_dw_hdmi_set_domain(hdmi->grf, 0);
25838e2bab3fSAlgea Cao 	}
25848e2bab3fSAlgea Cao 
25858e2bab3fSAlgea Cao 	return status;
25868e2bab3fSAlgea Cao }
25878e2bab3fSAlgea Cao 
25888e2bab3fSAlgea Cao void inno_dw_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data)
25898e2bab3fSAlgea Cao {
25908e2bab3fSAlgea Cao 	struct display_state *state = (struct display_state *)data;
259115081c50SWyon Bi 	struct connector_state *conn_state = &state->conn_state;
25928e2bab3fSAlgea Cao 	struct hdmi_edid_data *edid_data = &hdmi->edid_data;
25938e2bab3fSAlgea Cao 	unsigned long rate;
25948e2bab3fSAlgea Cao 	int i, ret;
25958e2bab3fSAlgea Cao 	struct drm_display_mode *mode_buf = edid_data->mode_buf;
25968e2bab3fSAlgea Cao 
25978e2bab3fSAlgea Cao 	for (i = 0; i < edid_data->modes; i++) {
25988e2bab3fSAlgea Cao 		if (edid_data->mode_buf[i].invalid)
25998e2bab3fSAlgea Cao 			continue;
26008e2bab3fSAlgea Cao 		if (edid_data->mode_buf[i].flags & DRM_MODE_FLAG_DBLCLK)
26018e2bab3fSAlgea Cao 			rate = mode_buf[i].clock * 1000 * 2;
26028e2bab3fSAlgea Cao 		else
26038e2bab3fSAlgea Cao 			rate = mode_buf[i].clock * 1000;
26048e2bab3fSAlgea Cao 
26058e2bab3fSAlgea Cao 		/* Check whether mode is out of phy cfg range. */
260615081c50SWyon Bi 		ret = rockchip_phy_round_rate(conn_state->phy, rate);
26078e2bab3fSAlgea Cao 
26088e2bab3fSAlgea Cao 		if (ret < 0)
26098e2bab3fSAlgea Cao 			edid_data->mode_buf[i].invalid = true;
26108e2bab3fSAlgea Cao 	}
26118e2bab3fSAlgea Cao }
2612