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>
10cb24dc0eSAlgea Cao #include <asm/gpio.h>
11f5e7d251SAlgea Cao #include <asm/arch-rockchip/clock.h>
128e2bab3fSAlgea Cao #include <asm/arch/vendor.h>
13f5e7d251SAlgea Cao #include <edid.h>
14e2bce6e4SKever Yang #include <dm/device.h>
151f71919fSShunqing Chen #include <dm/of_access.h>
168e2bab3fSAlgea Cao #include <dm/ofnode.h>
17e2bce6e4SKever Yang #include <dm/read.h>
18f5e7d251SAlgea Cao #include <linux/hdmi.h>
19f5e7d251SAlgea Cao #include <linux/media-bus-format.h>
20f5e7d251SAlgea Cao #include <linux/dw_hdmi.h>
21f5e7d251SAlgea Cao #include <asm/io.h>
22c3a20b0eSChen Shunqing #include "rockchip_bridge.h"
23f5e7d251SAlgea Cao #include "rockchip_display.h"
24f5e7d251SAlgea Cao #include "rockchip_crtc.h"
25f5e7d251SAlgea Cao #include "rockchip_connector.h"
26f5e7d251SAlgea Cao #include "dw_hdmi.h"
278e2bab3fSAlgea Cao #include "rockchip_phy.h"
28f5e7d251SAlgea Cao
298e2bab3fSAlgea Cao #define HDCP_PRIVATE_KEY_SIZE 280
308e2bab3fSAlgea Cao #define HDCP_KEY_SHA_SIZE 20
318e2bab3fSAlgea Cao #define HDMI_HDCP1X_ID 5
32d9939fc9SAlgea Cao #define HDMI_EDID_BLOCK_LEN 128
33f5e7d251SAlgea Cao /*
34f5e7d251SAlgea Cao * Unless otherwise noted, entries in this table are 100% optimization.
35f5e7d251SAlgea Cao * Values can be obtained from hdmi_compute_n() but that function is
36f5e7d251SAlgea Cao * slow so we pre-compute values we expect to see.
37f5e7d251SAlgea Cao *
38f5e7d251SAlgea Cao * All 32k and 48k values are expected to be the same (due to the way
39f5e7d251SAlgea Cao * the math works) for any rate that's an exact kHz.
40f5e7d251SAlgea Cao */
41f5e7d251SAlgea Cao static const struct dw_hdmi_audio_tmds_n common_tmds_n_table[] = {
42f5e7d251SAlgea Cao { .tmds = 25175000, .n_32k = 4096, .n_44k1 = 12854, .n_48k = 6144, },
43f5e7d251SAlgea Cao { .tmds = 25200000, .n_32k = 4096, .n_44k1 = 5656, .n_48k = 6144, },
44f5e7d251SAlgea Cao { .tmds = 27000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
45f5e7d251SAlgea Cao { .tmds = 28320000, .n_32k = 4096, .n_44k1 = 5586, .n_48k = 6144, },
46f5e7d251SAlgea Cao { .tmds = 30240000, .n_32k = 4096, .n_44k1 = 5642, .n_48k = 6144, },
47f5e7d251SAlgea Cao { .tmds = 31500000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
48f5e7d251SAlgea Cao { .tmds = 32000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
49f5e7d251SAlgea Cao { .tmds = 33750000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
50f5e7d251SAlgea Cao { .tmds = 36000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
51f5e7d251SAlgea Cao { .tmds = 40000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
52f5e7d251SAlgea Cao { .tmds = 49500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
53f5e7d251SAlgea Cao { .tmds = 50000000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
54f5e7d251SAlgea Cao { .tmds = 54000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
55f5e7d251SAlgea Cao { .tmds = 65000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
56f5e7d251SAlgea Cao { .tmds = 68250000, .n_32k = 4096, .n_44k1 = 5376, .n_48k = 6144, },
57f5e7d251SAlgea Cao { .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
58f5e7d251SAlgea Cao { .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, },
59f5e7d251SAlgea Cao { .tmds = 73250000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
60f5e7d251SAlgea Cao { .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
61f5e7d251SAlgea Cao { .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, },
62f5e7d251SAlgea Cao { .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
63f5e7d251SAlgea Cao { .tmds = 78800000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
64f5e7d251SAlgea Cao { .tmds = 79500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
65f5e7d251SAlgea Cao { .tmds = 83500000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
66f5e7d251SAlgea Cao { .tmds = 85500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
67f5e7d251SAlgea Cao { .tmds = 88750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
68f5e7d251SAlgea Cao { .tmds = 97750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
69f5e7d251SAlgea Cao { .tmds = 101000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
70f5e7d251SAlgea Cao { .tmds = 106500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
71f5e7d251SAlgea Cao { .tmds = 108000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
72f5e7d251SAlgea Cao { .tmds = 115500000, .n_32k = 4096, .n_44k1 = 5712, .n_48k = 6144, },
73f5e7d251SAlgea Cao { .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
74f5e7d251SAlgea Cao { .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
75f5e7d251SAlgea Cao { .tmds = 146250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
76f5e7d251SAlgea Cao { .tmds = 148500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
77f5e7d251SAlgea Cao { .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
78f5e7d251SAlgea Cao { .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
79f5e7d251SAlgea Cao
80f5e7d251SAlgea Cao /* For 297 MHz+ HDMI spec have some other rule for setting N */
81f5e7d251SAlgea Cao { .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, },
82f5e7d251SAlgea Cao { .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240, },
83f5e7d251SAlgea Cao
84f5e7d251SAlgea Cao /* End of table */
85f5e7d251SAlgea Cao { .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, },
86f5e7d251SAlgea Cao };
87f5e7d251SAlgea Cao
88f5e7d251SAlgea Cao static const u16 csc_coeff_default[3][4] = {
89f5e7d251SAlgea Cao { 0x2000, 0x0000, 0x0000, 0x0000 },
90f5e7d251SAlgea Cao { 0x0000, 0x2000, 0x0000, 0x0000 },
91f5e7d251SAlgea Cao { 0x0000, 0x0000, 0x2000, 0x0000 }
92f5e7d251SAlgea Cao };
93f5e7d251SAlgea Cao
94f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_out_eitu601[3][4] = {
95f5e7d251SAlgea Cao { 0x2000, 0x6926, 0x74fd, 0x010e },
96f5e7d251SAlgea Cao { 0x2000, 0x2cdd, 0x0000, 0x7e9a },
97f5e7d251SAlgea Cao { 0x2000, 0x0000, 0x38b4, 0x7e3b }
98f5e7d251SAlgea Cao };
99f5e7d251SAlgea Cao
100f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_out_eitu709[3][4] = {
101f5e7d251SAlgea Cao { 0x2000, 0x7106, 0x7a02, 0x00a7 },
102f5e7d251SAlgea Cao { 0x2000, 0x3264, 0x0000, 0x7e6d },
103f5e7d251SAlgea Cao { 0x2000, 0x0000, 0x3b61, 0x7e25 }
104f5e7d251SAlgea Cao };
105f5e7d251SAlgea Cao
106f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_in_eitu601[3][4] = {
107f5e7d251SAlgea Cao { 0x2591, 0x1322, 0x074b, 0x0000 },
108f5e7d251SAlgea Cao { 0x6535, 0x2000, 0x7acc, 0x0200 },
109f5e7d251SAlgea Cao { 0x6acd, 0x7534, 0x2000, 0x0200 }
110f5e7d251SAlgea Cao };
111f5e7d251SAlgea Cao
112f5e7d251SAlgea Cao static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
113f5e7d251SAlgea Cao { 0x2dc5, 0x0d9b, 0x049e, 0x0000 },
114f5e7d251SAlgea Cao { 0x62f0, 0x2000, 0x7d11, 0x0200 },
115f5e7d251SAlgea Cao { 0x6756, 0x78ab, 0x2000, 0x0200 }
116f5e7d251SAlgea Cao };
117f5e7d251SAlgea Cao
118b5016cf2SAlgea Cao static const u16 csc_coeff_full_to_limited[3][4] = {
119b5016cf2SAlgea Cao { 0x36f7, 0x0000, 0x0000, 0x0040 },
120b5016cf2SAlgea Cao { 0x0000, 0x36f7, 0x0000, 0x0040 },
121b5016cf2SAlgea Cao { 0x0000, 0x0000, 0x36f7, 0x0040 }
122b5016cf2SAlgea Cao };
123b5016cf2SAlgea Cao
124f5e7d251SAlgea Cao struct hdmi_vmode {
125f5e7d251SAlgea Cao bool mdataenablepolarity;
126f5e7d251SAlgea Cao
127f5e7d251SAlgea Cao unsigned int mpixelclock;
128f5e7d251SAlgea Cao unsigned int mpixelrepetitioninput;
129f5e7d251SAlgea Cao unsigned int mpixelrepetitionoutput;
1308e2bab3fSAlgea Cao unsigned int mtmdsclock;
131f5e7d251SAlgea Cao };
132f5e7d251SAlgea Cao
133f5e7d251SAlgea Cao struct hdmi_data_info {
134f5e7d251SAlgea Cao unsigned int enc_in_bus_format;
135f5e7d251SAlgea Cao unsigned int enc_out_bus_format;
136f5e7d251SAlgea Cao unsigned int enc_in_encoding;
137f5e7d251SAlgea Cao unsigned int enc_out_encoding;
138b5016cf2SAlgea Cao unsigned int quant_range;
139f5e7d251SAlgea Cao unsigned int pix_repet_factor;
140f5e7d251SAlgea Cao struct hdmi_vmode video_mode;
141f5e7d251SAlgea Cao };
142f5e7d251SAlgea Cao
143f5e7d251SAlgea Cao struct dw_hdmi_phy_data {
144f5e7d251SAlgea Cao enum dw_hdmi_phy_type type;
145f5e7d251SAlgea Cao const char *name;
146f5e7d251SAlgea Cao unsigned int gen;
147f5e7d251SAlgea Cao bool has_svsret;
148f5e7d251SAlgea Cao int (*configure)(struct dw_hdmi *hdmi,
149f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *pdata,
150f5e7d251SAlgea Cao unsigned long mpixelclock);
151f5e7d251SAlgea Cao };
152f5e7d251SAlgea Cao
1538e2bab3fSAlgea Cao struct hdcp_keys {
1548e2bab3fSAlgea Cao u8 KSV[8];
1558e2bab3fSAlgea Cao u8 devicekey[HDCP_PRIVATE_KEY_SIZE];
1568e2bab3fSAlgea Cao u8 sha1[HDCP_KEY_SHA_SIZE];
1578e2bab3fSAlgea Cao u8 seeds[2];
1588e2bab3fSAlgea Cao };
1598e2bab3fSAlgea Cao
1608e2bab3fSAlgea Cao struct dw_hdmi_i2c {
1618e2bab3fSAlgea Cao u8 slave_reg;
1628e2bab3fSAlgea Cao bool is_regaddr;
1638e2bab3fSAlgea Cao bool is_segment;
1648e2bab3fSAlgea Cao
1658e2bab3fSAlgea Cao unsigned int scl_high_ns;
1668e2bab3fSAlgea Cao unsigned int scl_low_ns;
1678e2bab3fSAlgea Cao };
1688e2bab3fSAlgea Cao
169f5e7d251SAlgea Cao struct dw_hdmi {
170cb17ca6cSSandy Huang int id;
171f5e7d251SAlgea Cao enum dw_hdmi_devtype dev_type;
172f5e7d251SAlgea Cao unsigned int version;
173f5e7d251SAlgea Cao struct hdmi_data_info hdmi_data;
174f5e7d251SAlgea Cao struct hdmi_edid_data edid_data;
175f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *plat_data;
1768e2bab3fSAlgea Cao struct ddc_adapter adap;
177f5e7d251SAlgea Cao
178f5e7d251SAlgea Cao int vic;
179f5e7d251SAlgea Cao int io_width;
180f5e7d251SAlgea Cao
181f5e7d251SAlgea Cao unsigned long bus_format;
182f5e7d251SAlgea Cao bool cable_plugin;
183f5e7d251SAlgea Cao bool sink_is_hdmi;
184f5e7d251SAlgea Cao bool sink_has_audio;
185b429d5abSAlgea Cao bool force_output;
186f5e7d251SAlgea Cao void *regs;
187f5e7d251SAlgea Cao void *grf;
188cb24dc0eSAlgea Cao void *gpio_base;
1898e2bab3fSAlgea Cao struct dw_hdmi_i2c *i2c;
190f5e7d251SAlgea Cao
191f5e7d251SAlgea Cao struct {
192f5e7d251SAlgea Cao const struct dw_hdmi_phy_ops *ops;
193f5e7d251SAlgea Cao const char *name;
194f5e7d251SAlgea Cao void *data;
195f5e7d251SAlgea Cao bool enabled;
196f5e7d251SAlgea Cao } phy;
197f5e7d251SAlgea Cao
198f5e7d251SAlgea Cao struct drm_display_mode previous_mode;
199f5e7d251SAlgea Cao
200f5e7d251SAlgea Cao unsigned int sample_rate;
201f5e7d251SAlgea Cao unsigned int audio_cts;
202f5e7d251SAlgea Cao unsigned int audio_n;
203f5e7d251SAlgea Cao bool audio_enable;
2048e2bab3fSAlgea Cao bool scramble_low_rates;
205f5e7d251SAlgea Cao
206f5e7d251SAlgea Cao void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
207f5e7d251SAlgea Cao u8 (*read)(struct dw_hdmi *hdmi, int offset);
2088e2bab3fSAlgea Cao
2098e2bab3fSAlgea Cao bool hdcp1x_enable;
21091e56900SLei Chen bool output_bus_format_rgb;
211cb24dc0eSAlgea Cao
212cb24dc0eSAlgea Cao struct gpio_desc hpd_gpiod;
213f5e7d251SAlgea Cao };
214f5e7d251SAlgea Cao
dw_hdmi_writel(struct dw_hdmi * hdmi,u8 val,int offset)215f5e7d251SAlgea Cao static void dw_hdmi_writel(struct dw_hdmi *hdmi, u8 val, int offset)
216f5e7d251SAlgea Cao {
217f5e7d251SAlgea Cao writel(val, hdmi->regs + (offset << 2));
218f5e7d251SAlgea Cao }
219f5e7d251SAlgea Cao
dw_hdmi_readl(struct dw_hdmi * hdmi,int offset)220f5e7d251SAlgea Cao static u8 dw_hdmi_readl(struct dw_hdmi *hdmi, int offset)
221f5e7d251SAlgea Cao {
222f5e7d251SAlgea Cao return readl(hdmi->regs + (offset << 2));
223f5e7d251SAlgea Cao }
224f5e7d251SAlgea Cao
dw_hdmi_writeb(struct dw_hdmi * hdmi,u8 val,int offset)225f5e7d251SAlgea Cao static void dw_hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset)
226f5e7d251SAlgea Cao {
227f5e7d251SAlgea Cao writeb(val, hdmi->regs + offset);
228f5e7d251SAlgea Cao }
229f5e7d251SAlgea Cao
dw_hdmi_readb(struct dw_hdmi * hdmi,int offset)230f5e7d251SAlgea Cao static u8 dw_hdmi_readb(struct dw_hdmi *hdmi, int offset)
231f5e7d251SAlgea Cao {
232f5e7d251SAlgea Cao return readb(hdmi->regs + offset);
233f5e7d251SAlgea Cao }
234f5e7d251SAlgea Cao
hdmi_writeb(struct dw_hdmi * hdmi,u8 val,int offset)235f5e7d251SAlgea Cao static inline void hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset)
236f5e7d251SAlgea Cao {
237f5e7d251SAlgea Cao hdmi->write(hdmi, val, offset);
238f5e7d251SAlgea Cao }
239f5e7d251SAlgea Cao
hdmi_readb(struct dw_hdmi * hdmi,int offset)240f5e7d251SAlgea Cao static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset)
241f5e7d251SAlgea Cao {
242f5e7d251SAlgea Cao return hdmi->read(hdmi, offset);
243f5e7d251SAlgea Cao }
244f5e7d251SAlgea Cao
hdmi_modb(struct dw_hdmi * hdmi,u8 data,u8 mask,unsigned reg)245f5e7d251SAlgea Cao static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
246f5e7d251SAlgea Cao {
247f5e7d251SAlgea Cao u8 val = hdmi_readb(hdmi, reg) & ~mask;
248f5e7d251SAlgea Cao
249f5e7d251SAlgea Cao val |= data & mask;
250f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, reg);
251f5e7d251SAlgea Cao }
252f5e7d251SAlgea Cao
hdmi_mask_writeb(struct dw_hdmi * hdmi,u8 data,unsigned int reg,u8 shift,u8 mask)253f5e7d251SAlgea Cao static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg,
254f5e7d251SAlgea Cao u8 shift, u8 mask)
255f5e7d251SAlgea Cao {
256f5e7d251SAlgea Cao hdmi_modb(hdmi, data << shift, mask, reg);
257f5e7d251SAlgea Cao }
258f5e7d251SAlgea Cao
hdmi_bus_fmt_is_rgb(unsigned int bus_format)259f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format)
260f5e7d251SAlgea Cao {
261f5e7d251SAlgea Cao switch (bus_format) {
262f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB888_1X24:
263f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB101010_1X30:
264f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB121212_1X36:
265f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB161616_1X48:
266f5e7d251SAlgea Cao return true;
267f5e7d251SAlgea Cao
268f5e7d251SAlgea Cao default:
269f5e7d251SAlgea Cao return false;
270f5e7d251SAlgea Cao }
271f5e7d251SAlgea Cao }
272f5e7d251SAlgea Cao
hdmi_bus_fmt_is_yuv444(unsigned int bus_format)273f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format)
274f5e7d251SAlgea Cao {
275f5e7d251SAlgea Cao switch (bus_format) {
276f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV8_1X24:
277f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV10_1X30:
278f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV12_1X36:
279f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV16_1X48:
280f5e7d251SAlgea Cao return true;
281f5e7d251SAlgea Cao
282f5e7d251SAlgea Cao default:
283f5e7d251SAlgea Cao return false;
284f5e7d251SAlgea Cao }
285f5e7d251SAlgea Cao }
286f5e7d251SAlgea Cao
hdmi_bus_fmt_is_yuv422(unsigned int bus_format)287f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format)
288f5e7d251SAlgea Cao {
289f5e7d251SAlgea Cao switch (bus_format) {
290f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY8_1X16:
291f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY10_1X20:
292f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY12_1X24:
293f5e7d251SAlgea Cao return true;
294f5e7d251SAlgea Cao
295f5e7d251SAlgea Cao default:
296f5e7d251SAlgea Cao return false;
297f5e7d251SAlgea Cao }
298f5e7d251SAlgea Cao }
299f5e7d251SAlgea Cao
hdmi_bus_fmt_is_yuv420(unsigned int bus_format)300f5e7d251SAlgea Cao static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format)
301f5e7d251SAlgea Cao {
302f5e7d251SAlgea Cao switch (bus_format) {
303f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
304f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
305f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
306f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
307f5e7d251SAlgea Cao return true;
308f5e7d251SAlgea Cao
309f5e7d251SAlgea Cao default:
310f5e7d251SAlgea Cao return false;
311f5e7d251SAlgea Cao }
312f5e7d251SAlgea Cao }
313f5e7d251SAlgea Cao
hdmi_bus_fmt_color_depth(unsigned int bus_format)314f5e7d251SAlgea Cao static int hdmi_bus_fmt_color_depth(unsigned int bus_format)
315f5e7d251SAlgea Cao {
316f5e7d251SAlgea Cao switch (bus_format) {
317f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB888_1X24:
318f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV8_1X24:
319f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY8_1X16:
320f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
321f5e7d251SAlgea Cao return 8;
322f5e7d251SAlgea Cao
323f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB101010_1X30:
324f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV10_1X30:
325f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY10_1X20:
326f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
327f5e7d251SAlgea Cao return 10;
328f5e7d251SAlgea Cao
329f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB121212_1X36:
330f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV12_1X36:
331f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY12_1X24:
332f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
333f5e7d251SAlgea Cao return 12;
334f5e7d251SAlgea Cao
335f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB161616_1X48:
336f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV16_1X48:
337f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
338f5e7d251SAlgea Cao return 16;
339f5e7d251SAlgea Cao
340f5e7d251SAlgea Cao default:
341f5e7d251SAlgea Cao return 0;
342f5e7d251SAlgea Cao }
343f5e7d251SAlgea Cao }
344f5e7d251SAlgea Cao
is_color_space_conversion(struct dw_hdmi * hdmi)345f5e7d251SAlgea Cao static int is_color_space_conversion(struct dw_hdmi *hdmi)
346f5e7d251SAlgea Cao {
347b5016cf2SAlgea Cao struct drm_display_mode *mode =
348b5016cf2SAlgea Cao hdmi->edid_data.preferred_mode;
349b5016cf2SAlgea Cao bool is_cea_default;
350b5016cf2SAlgea Cao
351b5016cf2SAlgea Cao is_cea_default = (drm_match_cea_mode(mode) > 1) &&
352b5016cf2SAlgea Cao (hdmi->hdmi_data.quant_range ==
353b5016cf2SAlgea Cao HDMI_QUANTIZATION_RANGE_DEFAULT);
354b5016cf2SAlgea Cao
355b5016cf2SAlgea Cao /*
356b5016cf2SAlgea Cao * When output is rgb limited range or default range with
357b5016cf2SAlgea Cao * cea mode, csc should be enabled.
358b5016cf2SAlgea Cao */
359b5016cf2SAlgea Cao if (hdmi->hdmi_data.enc_in_bus_format !=
360b5016cf2SAlgea Cao hdmi->hdmi_data.enc_out_bus_format ||
361b5016cf2SAlgea Cao ((hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_LIMITED ||
362b5016cf2SAlgea Cao is_cea_default) &&
363b5016cf2SAlgea Cao hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format)))
364b5016cf2SAlgea Cao return 1;
365b5016cf2SAlgea Cao
366b5016cf2SAlgea Cao return 0;
367f5e7d251SAlgea Cao }
368f5e7d251SAlgea Cao
is_color_space_decimation(struct dw_hdmi * hdmi)369f5e7d251SAlgea Cao static int is_color_space_decimation(struct dw_hdmi *hdmi)
370f5e7d251SAlgea Cao {
371f5e7d251SAlgea Cao if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
372f5e7d251SAlgea Cao return 0;
373f5e7d251SAlgea Cao
374f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format) ||
375f5e7d251SAlgea Cao hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_in_bus_format))
376f5e7d251SAlgea Cao return 1;
377f5e7d251SAlgea Cao
378f5e7d251SAlgea Cao return 0;
379f5e7d251SAlgea Cao }
380f5e7d251SAlgea Cao
hdmi_phy_test_clear(struct dw_hdmi * hdmi,unsigned char bit)381f5e7d251SAlgea Cao static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi,
382f5e7d251SAlgea Cao unsigned char bit)
383f5e7d251SAlgea Cao {
384f5e7d251SAlgea Cao hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET,
385f5e7d251SAlgea Cao HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0);
386f5e7d251SAlgea Cao }
387f5e7d251SAlgea Cao
hdmi_phy_test_enable(struct dw_hdmi * hdmi,unsigned char bit)388f5e7d251SAlgea Cao static inline void hdmi_phy_test_enable(struct dw_hdmi *hdmi,
389f5e7d251SAlgea Cao unsigned char bit)
390f5e7d251SAlgea Cao {
391f5e7d251SAlgea Cao hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET,
392f5e7d251SAlgea Cao HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0);
393f5e7d251SAlgea Cao }
394f5e7d251SAlgea Cao
hdmi_phy_test_clock(struct dw_hdmi * hdmi,unsigned char bit)395f5e7d251SAlgea Cao static inline void hdmi_phy_test_clock(struct dw_hdmi *hdmi,
396f5e7d251SAlgea Cao unsigned char bit)
397f5e7d251SAlgea Cao {
398f5e7d251SAlgea Cao hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET,
399f5e7d251SAlgea Cao HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0);
400f5e7d251SAlgea Cao }
401f5e7d251SAlgea Cao
hdmi_phy_test_din(struct dw_hdmi * hdmi,unsigned char bit)402f5e7d251SAlgea Cao static inline void hdmi_phy_test_din(struct dw_hdmi *hdmi,
403f5e7d251SAlgea Cao unsigned char bit)
404f5e7d251SAlgea Cao {
405f5e7d251SAlgea Cao hdmi_writeb(hdmi, bit, HDMI_PHY_TST1);
406f5e7d251SAlgea Cao }
407f5e7d251SAlgea Cao
hdmi_phy_test_dout(struct dw_hdmi * hdmi,unsigned char bit)408f5e7d251SAlgea Cao static inline void hdmi_phy_test_dout(struct dw_hdmi *hdmi,
409f5e7d251SAlgea Cao unsigned char bit)
410f5e7d251SAlgea Cao {
411f5e7d251SAlgea Cao hdmi_writeb(hdmi, bit, HDMI_PHY_TST2);
412f5e7d251SAlgea Cao }
413f5e7d251SAlgea Cao
dw_hdmi_i2c_read(struct dw_hdmi * hdmi,unsigned char * buf,unsigned int length)4148e2bab3fSAlgea Cao static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi,
4158e2bab3fSAlgea Cao unsigned char *buf, unsigned int length)
4168e2bab3fSAlgea Cao {
4178e2bab3fSAlgea Cao struct dw_hdmi_i2c *i2c = hdmi->i2c;
4188e2bab3fSAlgea Cao int interrupt = 0, i = 20;
419d9939fc9SAlgea Cao bool read_edid = false;
4208e2bab3fSAlgea Cao
4218e2bab3fSAlgea Cao if (!i2c->is_regaddr) {
4228e2bab3fSAlgea Cao printf("set read register address to 0\n");
4238e2bab3fSAlgea Cao i2c->slave_reg = 0x00;
4248e2bab3fSAlgea Cao i2c->is_regaddr = true;
4258e2bab3fSAlgea Cao }
4268e2bab3fSAlgea Cao
427d9939fc9SAlgea Cao /* edid reads are in 128 bytes. scdc reads are in 1 byte */
428d9939fc9SAlgea Cao if (length == HDMI_EDID_BLOCK_LEN)
429d9939fc9SAlgea Cao read_edid = true;
430d9939fc9SAlgea Cao
431d9939fc9SAlgea Cao while (length > 0) {
432d9939fc9SAlgea Cao hdmi_writeb(hdmi, i2c->slave_reg, HDMI_I2CM_ADDRESS);
433d9939fc9SAlgea Cao
434d9939fc9SAlgea Cao if (read_edid) {
435d9939fc9SAlgea Cao i2c->slave_reg += 8;
436d9939fc9SAlgea Cao length -= 8;
437d9939fc9SAlgea Cao } else {
438d9939fc9SAlgea Cao i2c->slave_reg++;
439d9939fc9SAlgea Cao length--;
440d9939fc9SAlgea Cao }
441d9939fc9SAlgea Cao
442d9939fc9SAlgea Cao if (i2c->is_segment) {
443d9939fc9SAlgea Cao if (read_edid)
444d9939fc9SAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ8_EXT,
445d9939fc9SAlgea Cao HDMI_I2CM_OPERATION);
446d9939fc9SAlgea Cao else
4478e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ_EXT,
4488e2bab3fSAlgea Cao HDMI_I2CM_OPERATION);
449d9939fc9SAlgea Cao } else {
450d9939fc9SAlgea Cao if (read_edid)
451d9939fc9SAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ8,
452d9939fc9SAlgea Cao HDMI_I2CM_OPERATION);
4538e2bab3fSAlgea Cao else
4548e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ,
4558e2bab3fSAlgea Cao HDMI_I2CM_OPERATION);
456d9939fc9SAlgea Cao }
4578e2bab3fSAlgea Cao
4588e2bab3fSAlgea Cao while (i--) {
4598e2bab3fSAlgea Cao udelay(1000);
4608e2bab3fSAlgea Cao interrupt = hdmi_readb(hdmi, HDMI_IH_I2CM_STAT0);
4618e2bab3fSAlgea Cao if (interrupt)
4628e2bab3fSAlgea Cao hdmi_writeb(hdmi, interrupt,
4638e2bab3fSAlgea Cao HDMI_IH_I2CM_STAT0);
4648e2bab3fSAlgea Cao if (interrupt & (m_SCDC_READREQ | m_I2CM_DONE |
4658e2bab3fSAlgea Cao m_I2CM_ERROR))
4668e2bab3fSAlgea Cao break;
4678e2bab3fSAlgea Cao }
4688e2bab3fSAlgea Cao
4698e2bab3fSAlgea Cao if (!interrupt) {
4708e2bab3fSAlgea Cao printf("[%s] i2c read reg[0x%02x] no interrupt\n",
4718e2bab3fSAlgea Cao __func__, i2c->slave_reg);
472d9939fc9SAlgea Cao hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ);
473d9939fc9SAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR,
474d9939fc9SAlgea Cao HDMI_I2CM_OPERATION);
475d9939fc9SAlgea Cao udelay(1000);
4768e2bab3fSAlgea Cao return -EAGAIN;
4778e2bab3fSAlgea Cao }
4788e2bab3fSAlgea Cao
4798e2bab3fSAlgea Cao /* Check for error condition on the bus */
4808e2bab3fSAlgea Cao if (interrupt & HDMI_IH_I2CM_STAT0_ERROR) {
4818e2bab3fSAlgea Cao printf("[%s] read reg[0x%02x] data error:0x%02x\n",
4828e2bab3fSAlgea Cao __func__, i2c->slave_reg, interrupt);
483d9939fc9SAlgea Cao hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ);
484d9939fc9SAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR,
485d9939fc9SAlgea Cao HDMI_I2CM_OPERATION);
486d9939fc9SAlgea Cao udelay(1000);
4878e2bab3fSAlgea Cao return -EIO;
4888e2bab3fSAlgea Cao }
4898e2bab3fSAlgea Cao
4908e2bab3fSAlgea Cao i = 20;
491d9939fc9SAlgea Cao if (read_edid)
492d9939fc9SAlgea Cao for (i = 0; i < 8; i++)
493d9939fc9SAlgea Cao *buf++ = hdmi_readb(hdmi, HDMI_I2CM_READ_BUFF0 + i);
494d9939fc9SAlgea Cao else
4958e2bab3fSAlgea Cao *buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI);
4968e2bab3fSAlgea Cao }
4978e2bab3fSAlgea Cao i2c->is_segment = false;
4988e2bab3fSAlgea Cao
4998e2bab3fSAlgea Cao return 0;
5008e2bab3fSAlgea Cao }
5018e2bab3fSAlgea Cao
dw_hdmi_i2c_write(struct dw_hdmi * hdmi,unsigned char * buf,unsigned int length)5028e2bab3fSAlgea Cao static int dw_hdmi_i2c_write(struct dw_hdmi *hdmi,
5038e2bab3fSAlgea Cao unsigned char *buf, unsigned int length)
5048e2bab3fSAlgea Cao {
5058e2bab3fSAlgea Cao struct dw_hdmi_i2c *i2c = hdmi->i2c;
5068e2bab3fSAlgea Cao int i = 20;
5078e2bab3fSAlgea Cao u8 interrupt = 0;
5088e2bab3fSAlgea Cao
5098e2bab3fSAlgea Cao if (!i2c->is_regaddr) {
5108e2bab3fSAlgea Cao /* Use the first write byte as register address */
5118e2bab3fSAlgea Cao i2c->slave_reg = buf[0];
5128e2bab3fSAlgea Cao length--;
5138e2bab3fSAlgea Cao buf++;
5148e2bab3fSAlgea Cao i2c->is_regaddr = true;
5158e2bab3fSAlgea Cao }
5168e2bab3fSAlgea Cao
5178e2bab3fSAlgea Cao while (length--) {
5188e2bab3fSAlgea Cao hdmi_writeb(hdmi, *buf++, HDMI_I2CM_DATAO);
5198e2bab3fSAlgea Cao hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS);
5208e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_WRITE,
5218e2bab3fSAlgea Cao HDMI_I2CM_OPERATION);
5228e2bab3fSAlgea Cao
5238e2bab3fSAlgea Cao while (i--) {
5248e2bab3fSAlgea Cao udelay(1000);
5258e2bab3fSAlgea Cao interrupt = hdmi_readb(hdmi, HDMI_IH_I2CM_STAT0);
5268e2bab3fSAlgea Cao if (interrupt)
5278e2bab3fSAlgea Cao hdmi_writeb(hdmi,
5288e2bab3fSAlgea Cao interrupt, HDMI_IH_I2CM_STAT0);
5298e2bab3fSAlgea Cao
5308e2bab3fSAlgea Cao if (interrupt & (m_SCDC_READREQ |
5318e2bab3fSAlgea Cao m_I2CM_DONE | m_I2CM_ERROR))
5328e2bab3fSAlgea Cao break;
5338e2bab3fSAlgea Cao }
5348e2bab3fSAlgea Cao
535d9939fc9SAlgea Cao if (!interrupt) {
536d9939fc9SAlgea Cao printf("[%s] i2c write reg[0x%02x] no interrupt\n",
537d9939fc9SAlgea Cao __func__, i2c->slave_reg);
538d9939fc9SAlgea Cao hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ);
539d9939fc9SAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR,
540d9939fc9SAlgea Cao HDMI_I2CM_OPERATION);
541d9939fc9SAlgea Cao udelay(1000);
542d9939fc9SAlgea Cao return -EAGAIN;
543d9939fc9SAlgea Cao }
544d9939fc9SAlgea Cao
5458e2bab3fSAlgea Cao if ((interrupt & m_I2CM_ERROR) || (i == -1)) {
5468e2bab3fSAlgea Cao printf("[%s] write data error\n", __func__);
547d9939fc9SAlgea Cao hdmi_writeb(hdmi, 0, HDMI_I2CM_SOFTRSTZ);
548d9939fc9SAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_BUS_CLEAR,
549d9939fc9SAlgea Cao HDMI_I2CM_OPERATION);
550d9939fc9SAlgea Cao udelay(1000);
5518e2bab3fSAlgea Cao return -EIO;
5528e2bab3fSAlgea Cao } else if (interrupt & m_I2CM_DONE) {
5538e2bab3fSAlgea Cao printf("[%s] write offset %02x success\n",
5548e2bab3fSAlgea Cao __func__, i2c->slave_reg);
5558e2bab3fSAlgea Cao return -EAGAIN;
5568e2bab3fSAlgea Cao }
5578e2bab3fSAlgea Cao
5588e2bab3fSAlgea Cao i = 20;
5598e2bab3fSAlgea Cao }
5608e2bab3fSAlgea Cao
5618e2bab3fSAlgea Cao return 0;
5628e2bab3fSAlgea Cao }
5638e2bab3fSAlgea Cao
dw_hdmi_i2c_xfer(struct ddc_adapter * adap,struct i2c_msg * msgs,int num)5648e2bab3fSAlgea Cao static int dw_hdmi_i2c_xfer(struct ddc_adapter *adap,
5658e2bab3fSAlgea Cao struct i2c_msg *msgs, int num)
5668e2bab3fSAlgea Cao {
5678e2bab3fSAlgea Cao struct dw_hdmi *hdmi = container_of(adap, struct dw_hdmi, adap);
5688e2bab3fSAlgea Cao struct dw_hdmi_i2c *i2c = hdmi->i2c;
5698e2bab3fSAlgea Cao u8 addr = msgs[0].addr;
5708e2bab3fSAlgea Cao int i, ret = 0;
5718e2bab3fSAlgea Cao
5728e2bab3fSAlgea Cao printf("xfer: num: %d, addr: %#x\n", num, addr);
5738e2bab3fSAlgea Cao for (i = 0; i < num; i++) {
5748e2bab3fSAlgea Cao if (msgs[i].len == 0) {
5758e2bab3fSAlgea Cao printf("unsupported transfer %d/%d, no data\n",
5768e2bab3fSAlgea Cao i + 1, num);
5778e2bab3fSAlgea Cao return -EOPNOTSUPP;
5788e2bab3fSAlgea Cao }
5798e2bab3fSAlgea Cao }
5808e2bab3fSAlgea Cao
5818e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0x00, HDMI_IH_MUTE_I2CM_STAT0);
5828e2bab3fSAlgea Cao
5838e2bab3fSAlgea Cao /* Set slave device address taken from the first I2C message */
5848e2bab3fSAlgea Cao if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1)
5858e2bab3fSAlgea Cao addr = DDC_ADDR;
5868e2bab3fSAlgea Cao hdmi_writeb(hdmi, addr, HDMI_I2CM_SLAVE);
5878e2bab3fSAlgea Cao
5888e2bab3fSAlgea Cao /* Set slave device register address on transfer */
5898e2bab3fSAlgea Cao i2c->is_regaddr = false;
5908e2bab3fSAlgea Cao
5918e2bab3fSAlgea Cao /* Set segment pointer for I2C extended read mode operation */
5928e2bab3fSAlgea Cao i2c->is_segment = false;
5938e2bab3fSAlgea Cao
5948e2bab3fSAlgea Cao for (i = 0; i < num; i++) {
5958e2bab3fSAlgea Cao debug("xfer: num: %d/%d, len: %d, flags: %#x\n",
5968e2bab3fSAlgea Cao i + 1, num, msgs[i].len, msgs[i].flags);
5978e2bab3fSAlgea Cao if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) {
5988e2bab3fSAlgea Cao i2c->is_segment = true;
5998e2bab3fSAlgea Cao hdmi_writeb(hdmi, DDC_SEGMENT_ADDR, HDMI_I2CM_SEGADDR);
6008e2bab3fSAlgea Cao hdmi_writeb(hdmi, *msgs[i].buf, HDMI_I2CM_SEGPTR);
6018e2bab3fSAlgea Cao } else {
6028e2bab3fSAlgea Cao if (msgs[i].flags & I2C_M_RD)
6038e2bab3fSAlgea Cao ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf,
6048e2bab3fSAlgea Cao msgs[i].len);
6058e2bab3fSAlgea Cao else
6068e2bab3fSAlgea Cao ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf,
6078e2bab3fSAlgea Cao msgs[i].len);
6088e2bab3fSAlgea Cao }
6098e2bab3fSAlgea Cao if (ret < 0)
6108e2bab3fSAlgea Cao break;
6118e2bab3fSAlgea Cao }
6128e2bab3fSAlgea Cao
6138e2bab3fSAlgea Cao if (!ret)
6148e2bab3fSAlgea Cao ret = num;
6158e2bab3fSAlgea Cao
6168e2bab3fSAlgea Cao /* Mute DONE and ERROR interrupts */
6178e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
6188e2bab3fSAlgea Cao HDMI_IH_MUTE_I2CM_STAT0);
6198e2bab3fSAlgea Cao
6208e2bab3fSAlgea Cao return ret;
6218e2bab3fSAlgea Cao }
6228e2bab3fSAlgea Cao
hdmi_phy_wait_i2c_done(struct dw_hdmi * hdmi,int msec)623f5e7d251SAlgea Cao static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec)
624f5e7d251SAlgea Cao {
625f5e7d251SAlgea Cao u32 val;
626f5e7d251SAlgea Cao
627f5e7d251SAlgea Cao while ((val = hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) {
628f5e7d251SAlgea Cao if (msec-- == 0)
629f5e7d251SAlgea Cao return false;
630f5e7d251SAlgea Cao udelay(1000);
631f5e7d251SAlgea Cao }
632f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_IH_I2CMPHY_STAT0);
633f5e7d251SAlgea Cao
634f5e7d251SAlgea Cao return true;
635f5e7d251SAlgea Cao }
636f5e7d251SAlgea Cao
dw_hdmi_phy_i2c_write(struct dw_hdmi * hdmi,unsigned short data,unsigned char addr)637f5e7d251SAlgea Cao static void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
638f5e7d251SAlgea Cao unsigned char addr)
639f5e7d251SAlgea Cao {
640f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
641f5e7d251SAlgea Cao hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
642f5e7d251SAlgea Cao hdmi_writeb(hdmi, (unsigned char)(data >> 8),
643f5e7d251SAlgea Cao HDMI_PHY_I2CM_DATAO_1_ADDR);
644f5e7d251SAlgea Cao hdmi_writeb(hdmi, (unsigned char)(data >> 0),
645f5e7d251SAlgea Cao HDMI_PHY_I2CM_DATAO_0_ADDR);
646f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
647f5e7d251SAlgea Cao HDMI_PHY_I2CM_OPERATION_ADDR);
648f5e7d251SAlgea Cao hdmi_phy_wait_i2c_done(hdmi, 1000);
649f5e7d251SAlgea Cao }
650f5e7d251SAlgea Cao
dw_hdmi_phy_enable_powerdown(struct dw_hdmi * hdmi,bool enable)651f5e7d251SAlgea Cao static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
652f5e7d251SAlgea Cao {
653f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0,
654f5e7d251SAlgea Cao HDMI_PHY_CONF0_PDZ_OFFSET,
655f5e7d251SAlgea Cao HDMI_PHY_CONF0_PDZ_MASK);
656f5e7d251SAlgea Cao }
657f5e7d251SAlgea Cao
dw_hdmi_phy_enable_tmds(struct dw_hdmi * hdmi,u8 enable)658f5e7d251SAlgea Cao static void dw_hdmi_phy_enable_tmds(struct dw_hdmi *hdmi, u8 enable)
659f5e7d251SAlgea Cao {
660f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
661f5e7d251SAlgea Cao HDMI_PHY_CONF0_ENTMDS_OFFSET,
662f5e7d251SAlgea Cao HDMI_PHY_CONF0_ENTMDS_MASK);
663f5e7d251SAlgea Cao }
664f5e7d251SAlgea Cao
dw_hdmi_phy_enable_svsret(struct dw_hdmi * hdmi,u8 enable)665f5e7d251SAlgea Cao static void dw_hdmi_phy_enable_svsret(struct dw_hdmi *hdmi, u8 enable)
666f5e7d251SAlgea Cao {
667f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
668f5e7d251SAlgea Cao HDMI_PHY_CONF0_SVSRET_OFFSET,
669f5e7d251SAlgea Cao HDMI_PHY_CONF0_SVSRET_MASK);
670f5e7d251SAlgea Cao }
671f5e7d251SAlgea Cao
dw_hdmi_phy_gen2_pddq(struct dw_hdmi * hdmi,u8 enable)672f5e7d251SAlgea Cao static void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable)
673f5e7d251SAlgea Cao {
674f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
675f5e7d251SAlgea Cao HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
676f5e7d251SAlgea Cao HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
677f5e7d251SAlgea Cao }
678f5e7d251SAlgea Cao
dw_hdmi_phy_gen2_txpwron(struct dw_hdmi * hdmi,u8 enable)679f5e7d251SAlgea Cao static void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable)
680f5e7d251SAlgea Cao {
681f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
682f5e7d251SAlgea Cao HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
683f5e7d251SAlgea Cao HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
684f5e7d251SAlgea Cao }
685f5e7d251SAlgea Cao
dw_hdmi_phy_sel_data_en_pol(struct dw_hdmi * hdmi,u8 enable)686f5e7d251SAlgea Cao static void dw_hdmi_phy_sel_data_en_pol(struct dw_hdmi *hdmi, u8 enable)
687f5e7d251SAlgea Cao {
688f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
689f5e7d251SAlgea Cao HDMI_PHY_CONF0_SELDATAENPOL_OFFSET,
690f5e7d251SAlgea Cao HDMI_PHY_CONF0_SELDATAENPOL_MASK);
691f5e7d251SAlgea Cao }
692f5e7d251SAlgea Cao
dw_hdmi_phy_sel_interface_control(struct dw_hdmi * hdmi,u8 enable)693f5e7d251SAlgea Cao static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable)
694f5e7d251SAlgea Cao {
695f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
696f5e7d251SAlgea Cao HDMI_PHY_CONF0_SELDIPIF_OFFSET,
697f5e7d251SAlgea Cao HDMI_PHY_CONF0_SELDIPIF_MASK);
698f5e7d251SAlgea Cao }
699f5e7d251SAlgea Cao
dw_hdmi_phy_power_off(struct dw_hdmi * hdmi)700f5e7d251SAlgea Cao static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi)
701f5e7d251SAlgea Cao {
702f5e7d251SAlgea Cao const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
703f5e7d251SAlgea Cao unsigned int i;
704f5e7d251SAlgea Cao u16 val;
705f5e7d251SAlgea Cao
706f5e7d251SAlgea Cao if (phy->gen == 1) {
707f5e7d251SAlgea Cao dw_hdmi_phy_enable_tmds(hdmi, 0);
708f5e7d251SAlgea Cao dw_hdmi_phy_enable_powerdown(hdmi, true);
709f5e7d251SAlgea Cao return;
710f5e7d251SAlgea Cao }
711f5e7d251SAlgea Cao
712f5e7d251SAlgea Cao dw_hdmi_phy_gen2_txpwron(hdmi, 0);
713f5e7d251SAlgea Cao
714f5e7d251SAlgea Cao /*
715f5e7d251SAlgea Cao * Wait for TX_PHY_LOCK to be deasserted to indicate that the PHY went
716f5e7d251SAlgea Cao * to low power mode.
717f5e7d251SAlgea Cao */
718f5e7d251SAlgea Cao for (i = 0; i < 5; ++i) {
719f5e7d251SAlgea Cao val = hdmi_readb(hdmi, HDMI_PHY_STAT0);
720f5e7d251SAlgea Cao if (!(val & HDMI_PHY_TX_PHY_LOCK))
721f5e7d251SAlgea Cao break;
722f5e7d251SAlgea Cao
723f5e7d251SAlgea Cao udelay(2000);
724f5e7d251SAlgea Cao }
725f5e7d251SAlgea Cao
726f5e7d251SAlgea Cao if (val & HDMI_PHY_TX_PHY_LOCK)
727f5e7d251SAlgea Cao printf("PHY failed to power down\n");
728f5e7d251SAlgea Cao else
729f5e7d251SAlgea Cao printf("PHY powered down in %u iterations\n", i);
730f5e7d251SAlgea Cao
731f5e7d251SAlgea Cao dw_hdmi_phy_gen2_pddq(hdmi, 1);
732f5e7d251SAlgea Cao }
733f5e7d251SAlgea Cao
dw_hdmi_phy_power_on(struct dw_hdmi * hdmi)734f5e7d251SAlgea Cao static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi)
735f5e7d251SAlgea Cao {
736f5e7d251SAlgea Cao const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
737f5e7d251SAlgea Cao unsigned int i;
738f5e7d251SAlgea Cao u8 val;
739f5e7d251SAlgea Cao
740f5e7d251SAlgea Cao if (phy->gen == 1) {
741f5e7d251SAlgea Cao dw_hdmi_phy_enable_powerdown(hdmi, false);
742f5e7d251SAlgea Cao
743f5e7d251SAlgea Cao /* Toggle TMDS enable. */
744f5e7d251SAlgea Cao dw_hdmi_phy_enable_tmds(hdmi, 0);
745f5e7d251SAlgea Cao dw_hdmi_phy_enable_tmds(hdmi, 1);
746f5e7d251SAlgea Cao return 0;
747f5e7d251SAlgea Cao }
748f5e7d251SAlgea Cao
749f5e7d251SAlgea Cao dw_hdmi_phy_gen2_txpwron(hdmi, 1);
750f5e7d251SAlgea Cao dw_hdmi_phy_gen2_pddq(hdmi, 0);
751f5e7d251SAlgea Cao
752f5e7d251SAlgea Cao /* Wait for PHY PLL lock */
753f5e7d251SAlgea Cao for (i = 0; i < 5; ++i) {
754f5e7d251SAlgea Cao val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
755f5e7d251SAlgea Cao if (val)
756f5e7d251SAlgea Cao break;
757f5e7d251SAlgea Cao
758f5e7d251SAlgea Cao udelay(2000);
759f5e7d251SAlgea Cao }
760f5e7d251SAlgea Cao
761f5e7d251SAlgea Cao if (!val) {
762f5e7d251SAlgea Cao printf("PHY PLL failed to lock\n");
763f5e7d251SAlgea Cao return -ETIMEDOUT;
764f5e7d251SAlgea Cao }
765f5e7d251SAlgea Cao printf("PHY PLL locked %u iterations\n", i);
7668e2bab3fSAlgea Cao
767f5e7d251SAlgea Cao return 0;
768f5e7d251SAlgea Cao }
769f5e7d251SAlgea Cao
770f5e7d251SAlgea Cao /*
771f5e7d251SAlgea Cao * PHY configuration function for the DWC HDMI 3D TX PHY. Based on the available
772f5e7d251SAlgea Cao * information the DWC MHL PHY has the same register layout and is thus also
773f5e7d251SAlgea Cao * supported by this function.
774f5e7d251SAlgea Cao */
775f5e7d251SAlgea Cao static
hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi * hdmi,const struct dw_hdmi_plat_data * pdata,unsigned long mpixelclock)776f5e7d251SAlgea Cao int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
777f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *pdata,
778f5e7d251SAlgea Cao unsigned long mpixelclock)
779f5e7d251SAlgea Cao {
780f5e7d251SAlgea Cao const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
781f5e7d251SAlgea Cao const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
782f5e7d251SAlgea Cao const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
7838e2bab3fSAlgea Cao unsigned int tmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
7848e2bab3fSAlgea Cao unsigned int depth =
7858e2bab3fSAlgea Cao hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
7868e2bab3fSAlgea Cao
7878e2bab3fSAlgea Cao if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) &&
7888e2bab3fSAlgea Cao pdata->mpll_cfg_420)
7898e2bab3fSAlgea Cao mpll_config = pdata->mpll_cfg_420;
790f5e7d251SAlgea Cao
791f5e7d251SAlgea Cao /* PLL/MPLL Cfg - always match on final entry */
792f5e7d251SAlgea Cao for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
793f5e7d251SAlgea Cao if (mpixelclock <= mpll_config->mpixelclock)
794f5e7d251SAlgea Cao break;
795f5e7d251SAlgea Cao
796f5e7d251SAlgea Cao for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
7978e2bab3fSAlgea Cao if (tmdsclock <= curr_ctrl->mpixelclock)
798f5e7d251SAlgea Cao break;
799f5e7d251SAlgea Cao
800f5e7d251SAlgea Cao for (; phy_config->mpixelclock != ~0UL; phy_config++)
8018e2bab3fSAlgea Cao if (tmdsclock <= phy_config->mpixelclock)
802f5e7d251SAlgea Cao break;
803f5e7d251SAlgea Cao
804f5e7d251SAlgea Cao if (mpll_config->mpixelclock == ~0UL ||
805f5e7d251SAlgea Cao curr_ctrl->mpixelclock == ~0UL ||
806f5e7d251SAlgea Cao phy_config->mpixelclock == ~0UL)
807f5e7d251SAlgea Cao return -EINVAL;
808f5e7d251SAlgea Cao
8098e2bab3fSAlgea Cao if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
8108e2bab3fSAlgea Cao depth = fls(depth - 8);
811f5e7d251SAlgea Cao else
8128e2bab3fSAlgea Cao depth = 0;
8138e2bab3fSAlgea Cao if (depth)
8148e2bab3fSAlgea Cao depth--;
8158e2bab3fSAlgea Cao
8168e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].cpce,
817f5e7d251SAlgea Cao HDMI_3D_TX_PHY_CPCE_CTRL);
8188e2bab3fSAlgea Cao
8198e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].gmp,
820f5e7d251SAlgea Cao HDMI_3D_TX_PHY_GMPCTRL);
8218e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[depth],
822f5e7d251SAlgea Cao HDMI_3D_TX_PHY_CURRCTRL);
823f5e7d251SAlgea Cao
824f5e7d251SAlgea Cao dw_hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL);
825f5e7d251SAlgea Cao dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK,
826f5e7d251SAlgea Cao HDMI_3D_TX_PHY_MSM_CTRL);
827f5e7d251SAlgea Cao
8288e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, phy_config->term, HDMI_3D_TX_PHY_TXTERM);
8298e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr,
830f5e7d251SAlgea Cao HDMI_3D_TX_PHY_CKSYMTXCTRL);
8318e2bab3fSAlgea Cao dw_hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr,
832f5e7d251SAlgea Cao HDMI_3D_TX_PHY_VLEVCTRL);
833f5e7d251SAlgea Cao
834f5e7d251SAlgea Cao return 0;
835f5e7d251SAlgea Cao }
836f5e7d251SAlgea Cao
837f5e7d251SAlgea Cao static const struct dw_hdmi_phy_data dw_hdmi_phys[] = {
838f5e7d251SAlgea Cao {
839f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_HDMI_TX_PHY,
840f5e7d251SAlgea Cao .name = "DWC HDMI TX PHY",
841f5e7d251SAlgea Cao .gen = 1,
842f5e7d251SAlgea Cao }, {
843f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_MHL_PHY_HEAC,
844f5e7d251SAlgea Cao .name = "DWC MHL PHY + HEAC PHY",
845f5e7d251SAlgea Cao .gen = 2,
846f5e7d251SAlgea Cao .has_svsret = true,
847f5e7d251SAlgea Cao .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
848f5e7d251SAlgea Cao }, {
849f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_MHL_PHY,
850f5e7d251SAlgea Cao .name = "DWC MHL PHY",
851f5e7d251SAlgea Cao .gen = 2,
852f5e7d251SAlgea Cao .has_svsret = true,
853f5e7d251SAlgea Cao .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
854f5e7d251SAlgea Cao }, {
855f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY_HEAC,
856f5e7d251SAlgea Cao .name = "DWC HDMI 3D TX PHY + HEAC PHY",
857f5e7d251SAlgea Cao .gen = 2,
858f5e7d251SAlgea Cao .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
859f5e7d251SAlgea Cao }, {
860f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY,
861f5e7d251SAlgea Cao .name = "DWC HDMI 3D TX PHY",
862f5e7d251SAlgea Cao .gen = 2,
863f5e7d251SAlgea Cao .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
864f5e7d251SAlgea Cao }, {
865f5e7d251SAlgea Cao .type = DW_HDMI_PHY_DWC_HDMI20_TX_PHY,
866f5e7d251SAlgea Cao .name = "DWC HDMI 2.0 TX PHY",
867f5e7d251SAlgea Cao .gen = 2,
868f5e7d251SAlgea Cao .has_svsret = true,
869f5e7d251SAlgea Cao .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
870f5e7d251SAlgea Cao }, {
871f5e7d251SAlgea Cao .type = DW_HDMI_PHY_VENDOR_PHY,
872f5e7d251SAlgea Cao .name = "Vendor PHY",
873f5e7d251SAlgea Cao }
874f5e7d251SAlgea Cao };
875f5e7d251SAlgea Cao
rockchip_dw_hdmi_scrambling_enable(struct dw_hdmi * hdmi,int enable)876f5e7d251SAlgea Cao static int rockchip_dw_hdmi_scrambling_enable(struct dw_hdmi *hdmi,
877f5e7d251SAlgea Cao int enable)
878f5e7d251SAlgea Cao {
8798e2bab3fSAlgea Cao u8 stat;
880f5e7d251SAlgea Cao
8818e2bab3fSAlgea Cao drm_scdc_readb(&hdmi->adap, SCDC_TMDS_CONFIG, &stat);
8828e2bab3fSAlgea Cao
883b429d5abSAlgea Cao if (stat < 0 && !hdmi->force_output) {
884f5e7d251SAlgea Cao debug("Failed to read tmds config\n");
885f5e7d251SAlgea Cao return false;
886f5e7d251SAlgea Cao }
887f5e7d251SAlgea Cao
888f5e7d251SAlgea Cao if (enable == 1) {
889f5e7d251SAlgea Cao /* Write on Rx the bit Scrambling_Enable, register 0x20 */
890f5e7d251SAlgea Cao stat |= SCDC_SCRAMBLING_ENABLE;
8918e2bab3fSAlgea Cao drm_scdc_writeb(&hdmi->adap, SCDC_TMDS_CONFIG, stat);
892f5e7d251SAlgea Cao /* TMDS software reset request */
893f5e7d251SAlgea Cao hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ,
894f5e7d251SAlgea Cao HDMI_MC_SWRSTZ);
895f5e7d251SAlgea Cao /* Enable/Disable Scrambling */
896f5e7d251SAlgea Cao hdmi_writeb(hdmi, 1, HDMI_FC_SCRAMBLER_CTRL);
897f5e7d251SAlgea Cao } else {
898f5e7d251SAlgea Cao /* Enable/Disable Scrambling */
899f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0, HDMI_FC_SCRAMBLER_CTRL);
900f5e7d251SAlgea Cao /* TMDS software reset request */
901f5e7d251SAlgea Cao hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ,
902f5e7d251SAlgea Cao HDMI_MC_SWRSTZ);
903f5e7d251SAlgea Cao /* Write on Rx the bit Scrambling_Enable, register 0x20 */
904f5e7d251SAlgea Cao stat &= ~SCDC_SCRAMBLING_ENABLE;
9058e2bab3fSAlgea Cao drm_scdc_writeb(&hdmi->adap, SCDC_TMDS_CONFIG, stat);
906f5e7d251SAlgea Cao }
907f5e7d251SAlgea Cao
908f5e7d251SAlgea Cao return 0;
909f5e7d251SAlgea Cao }
910f5e7d251SAlgea Cao
rockchip_dw_hdmi_scdc_set_tmds_rate(struct dw_hdmi * hdmi)911f5e7d251SAlgea Cao static void rockchip_dw_hdmi_scdc_set_tmds_rate(struct dw_hdmi *hdmi)
912f5e7d251SAlgea Cao {
9138e2bab3fSAlgea Cao u8 stat;
914f5e7d251SAlgea Cao
9158e2bab3fSAlgea Cao drm_scdc_readb(&hdmi->adap, SCDC_TMDS_CONFIG, &stat);
9168e2bab3fSAlgea Cao if (hdmi->hdmi_data.video_mode.mtmdsclock > 340000000)
917f5e7d251SAlgea Cao stat |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
918f5e7d251SAlgea Cao else
919f5e7d251SAlgea Cao stat &= ~SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
9208e2bab3fSAlgea Cao drm_scdc_writeb(&hdmi->adap, SCDC_TMDS_CONFIG, stat);
921f5e7d251SAlgea Cao }
922f5e7d251SAlgea Cao
hdmi_phy_configure(struct dw_hdmi * hdmi)923f5e7d251SAlgea Cao static int hdmi_phy_configure(struct dw_hdmi *hdmi)
924f5e7d251SAlgea Cao {
925f5e7d251SAlgea Cao const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
926f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
927f5e7d251SAlgea Cao unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
9288e2bab3fSAlgea Cao unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
9298e2bab3fSAlgea Cao int ret;
930f5e7d251SAlgea Cao
931f5e7d251SAlgea Cao dw_hdmi_phy_power_off(hdmi);
932f5e7d251SAlgea Cao
933f5e7d251SAlgea Cao /* Control for TMDS Bit Period/TMDS Clock-Period Ratio */
934f5e7d251SAlgea Cao if (hdmi->edid_data.display_info.hdmi.scdc.supported)
935f5e7d251SAlgea Cao rockchip_dw_hdmi_scdc_set_tmds_rate(hdmi);
936f5e7d251SAlgea Cao
937f5e7d251SAlgea Cao /* Leave low power consumption mode by asserting SVSRET. */
938f5e7d251SAlgea Cao if (phy->has_svsret)
939f5e7d251SAlgea Cao dw_hdmi_phy_enable_svsret(hdmi, 1);
940f5e7d251SAlgea Cao
941f5e7d251SAlgea Cao /* PHY reset. The reset signal is active high on Gen2 PHYs. */
942f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ);
943f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ);
944f5e7d251SAlgea Cao
945f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
946f5e7d251SAlgea Cao
947f5e7d251SAlgea Cao hdmi_phy_test_clear(hdmi, 1);
948f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
949f5e7d251SAlgea Cao HDMI_PHY_I2CM_SLAVE_ADDR);
950f5e7d251SAlgea Cao hdmi_phy_test_clear(hdmi, 0);
951f5e7d251SAlgea Cao
952f5e7d251SAlgea Cao /* Write to the PHY as configured by the platform */
953f5e7d251SAlgea Cao if (pdata->configure_phy)
954f5e7d251SAlgea Cao ret = pdata->configure_phy(hdmi, pdata, mpixelclock);
955f5e7d251SAlgea Cao else
956f5e7d251SAlgea Cao ret = phy->configure(hdmi, pdata, mpixelclock);
957f5e7d251SAlgea Cao if (ret) {
958f5e7d251SAlgea Cao printf("PHY configuration failed (clock %lu)\n",
959f5e7d251SAlgea Cao mpixelclock);
960f5e7d251SAlgea Cao return ret;
961f5e7d251SAlgea Cao }
962f5e7d251SAlgea Cao
963f5e7d251SAlgea Cao /* Wait for resuming transmission of TMDS clock and data */
9648e2bab3fSAlgea Cao if (mtmdsclock > 340000000)
965f5e7d251SAlgea Cao mdelay(100);
966f5e7d251SAlgea Cao
967f5e7d251SAlgea Cao return dw_hdmi_phy_power_on(hdmi);
968f5e7d251SAlgea Cao }
969f5e7d251SAlgea Cao
dw_hdmi_phy_init(struct rockchip_connector * conn,struct dw_hdmi * hdmi,void * data)9700594ce39SZhang Yubing static int dw_hdmi_phy_init(struct rockchip_connector *conn, struct dw_hdmi *hdmi,
9718e2bab3fSAlgea Cao void *data)
972f5e7d251SAlgea Cao {
973f5e7d251SAlgea Cao int i, ret;
974f5e7d251SAlgea Cao
975f5e7d251SAlgea Cao /* HDMI Phy spec says to do the phy initialization sequence twice */
976f5e7d251SAlgea Cao for (i = 0; i < 2; i++) {
977f5e7d251SAlgea Cao dw_hdmi_phy_sel_data_en_pol(hdmi, 1);
978f5e7d251SAlgea Cao dw_hdmi_phy_sel_interface_control(hdmi, 0);
979f5e7d251SAlgea Cao ret = hdmi_phy_configure(hdmi);
980f5e7d251SAlgea Cao if (ret)
981f5e7d251SAlgea Cao return ret;
982f5e7d251SAlgea Cao }
983f5e7d251SAlgea Cao
984f5e7d251SAlgea Cao return 0;
985f5e7d251SAlgea Cao }
986f5e7d251SAlgea Cao
dw_hdmi_phy_disable(struct rockchip_connector * conn,struct dw_hdmi * hdmi,void * data)9870594ce39SZhang Yubing static void dw_hdmi_phy_disable(struct rockchip_connector *conn, struct dw_hdmi *hdmi,
9888e2bab3fSAlgea Cao void *data)
989f5e7d251SAlgea Cao {
990f5e7d251SAlgea Cao dw_hdmi_phy_power_off(hdmi);
991f5e7d251SAlgea Cao }
992f5e7d251SAlgea Cao
9938e2bab3fSAlgea Cao static enum drm_connector_status
dw_hdmi_phy_read_hpd(struct dw_hdmi * hdmi,void * data)9948e2bab3fSAlgea Cao dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, void *data)
995f5e7d251SAlgea Cao {
996f5e7d251SAlgea Cao return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
997f5e7d251SAlgea Cao connector_status_connected : connector_status_disconnected;
998f5e7d251SAlgea Cao }
999f5e7d251SAlgea Cao
1000f5e7d251SAlgea Cao static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = {
1001f5e7d251SAlgea Cao .init = dw_hdmi_phy_init,
1002f5e7d251SAlgea Cao .disable = dw_hdmi_phy_disable,
1003f5e7d251SAlgea Cao .read_hpd = dw_hdmi_phy_read_hpd,
1004f5e7d251SAlgea Cao };
1005f5e7d251SAlgea Cao
dw_hdmi_detect_phy(struct dw_hdmi * hdmi)1006f5e7d251SAlgea Cao static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi)
1007f5e7d251SAlgea Cao {
1008f5e7d251SAlgea Cao unsigned int i;
1009f5e7d251SAlgea Cao u8 phy_type;
1010f5e7d251SAlgea Cao
1011f5e7d251SAlgea Cao phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID);
1012f5e7d251SAlgea Cao
1013f5e7d251SAlgea Cao /*
1014f5e7d251SAlgea Cao * RK3228 and RK3328 phy_type is DW_HDMI_PHY_DWC_HDMI20_TX_PHY,
1015f5e7d251SAlgea Cao * but it has a vedor phy.
1016f5e7d251SAlgea Cao */
1017f5e7d251SAlgea Cao if (phy_type == DW_HDMI_PHY_VENDOR_PHY ||
1018cb24dc0eSAlgea Cao hdmi->dev_type == RK3528_HDMI ||
1019f5e7d251SAlgea Cao hdmi->dev_type == RK3328_HDMI ||
1020f5e7d251SAlgea Cao hdmi->dev_type == RK3228_HDMI) {
1021f5e7d251SAlgea Cao /* Vendor PHYs require support from the glue layer. */
1022f5e7d251SAlgea Cao if (!hdmi->plat_data->phy_ops || !hdmi->plat_data->phy_name) {
1023f5e7d251SAlgea Cao printf(
1024f5e7d251SAlgea Cao "Vendor HDMI PHY not supported by glue layer\n");
1025f5e7d251SAlgea Cao return -ENODEV;
1026f5e7d251SAlgea Cao }
1027f5e7d251SAlgea Cao
1028f5e7d251SAlgea Cao hdmi->phy.ops = hdmi->plat_data->phy_ops;
1029f5e7d251SAlgea Cao hdmi->phy.data = hdmi->plat_data->phy_data;
1030f5e7d251SAlgea Cao hdmi->phy.name = hdmi->plat_data->phy_name;
1031f5e7d251SAlgea Cao return 0;
1032f5e7d251SAlgea Cao }
1033f5e7d251SAlgea Cao
1034f5e7d251SAlgea Cao /* Synopsys PHYs are handled internally. */
1035f5e7d251SAlgea Cao for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); ++i) {
1036f5e7d251SAlgea Cao if (dw_hdmi_phys[i].type == phy_type) {
1037f5e7d251SAlgea Cao hdmi->phy.ops = &dw_hdmi_synopsys_phy_ops;
1038f5e7d251SAlgea Cao hdmi->phy.name = dw_hdmi_phys[i].name;
1039f5e7d251SAlgea Cao hdmi->phy.data = (void *)&dw_hdmi_phys[i];
1040f5e7d251SAlgea Cao
1041f5e7d251SAlgea Cao if (!dw_hdmi_phys[i].configure &&
1042f5e7d251SAlgea Cao !hdmi->plat_data->configure_phy) {
1043f5e7d251SAlgea Cao printf("%s requires platform support\n",
1044f5e7d251SAlgea Cao hdmi->phy.name);
1045f5e7d251SAlgea Cao return -ENODEV;
1046f5e7d251SAlgea Cao }
1047f5e7d251SAlgea Cao
1048f5e7d251SAlgea Cao return 0;
1049f5e7d251SAlgea Cao }
1050f5e7d251SAlgea Cao }
1051f5e7d251SAlgea Cao
1052f5e7d251SAlgea Cao printf("Unsupported HDMI PHY type (%02x)\n", phy_type);
1053f5e7d251SAlgea Cao return -ENODEV;
1054f5e7d251SAlgea Cao }
1055f5e7d251SAlgea Cao
10568e2bab3fSAlgea Cao static unsigned int
hdmi_get_tmdsclock(struct dw_hdmi * hdmi,unsigned long mpixelclock)10578e2bab3fSAlgea Cao hdmi_get_tmdsclock(struct dw_hdmi *hdmi, unsigned long mpixelclock)
10588e2bab3fSAlgea Cao {
10598e2bab3fSAlgea Cao unsigned int tmdsclock = mpixelclock;
10608e2bab3fSAlgea Cao unsigned int depth =
10618e2bab3fSAlgea Cao hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
10628e2bab3fSAlgea Cao
10638e2bab3fSAlgea Cao if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) {
10648e2bab3fSAlgea Cao switch (depth) {
10658e2bab3fSAlgea Cao case 16:
10668e2bab3fSAlgea Cao tmdsclock = mpixelclock * 2;
10678e2bab3fSAlgea Cao break;
10688e2bab3fSAlgea Cao case 12:
10698e2bab3fSAlgea Cao tmdsclock = mpixelclock * 3 / 2;
10708e2bab3fSAlgea Cao break;
10718e2bab3fSAlgea Cao case 10:
10728e2bab3fSAlgea Cao tmdsclock = mpixelclock * 5 / 4;
10738e2bab3fSAlgea Cao break;
10748e2bab3fSAlgea Cao default:
10758e2bab3fSAlgea Cao break;
10768e2bab3fSAlgea Cao }
10778e2bab3fSAlgea Cao }
10788e2bab3fSAlgea Cao
10798e2bab3fSAlgea Cao return tmdsclock;
10808e2bab3fSAlgea Cao }
10818e2bab3fSAlgea Cao
hdmi_av_composer(struct dw_hdmi * hdmi,const struct drm_display_mode * mode)1082f5e7d251SAlgea Cao static void hdmi_av_composer(struct dw_hdmi *hdmi,
1083f5e7d251SAlgea Cao const struct drm_display_mode *mode)
1084f5e7d251SAlgea Cao {
10858e2bab3fSAlgea Cao u8 bytes = 0, inv_val = 0;
1086f5e7d251SAlgea Cao struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
1087f5e7d251SAlgea Cao struct drm_hdmi_info *hdmi_info = &hdmi->edid_data.display_info.hdmi;
10888e2bab3fSAlgea Cao int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
1089f5e7d251SAlgea Cao unsigned int hdisplay, vdisplay;
1090f5e7d251SAlgea Cao
10918e2bab3fSAlgea Cao vmode->mpixelclock = mode->crtc_clock * 1000;
1092f5e7d251SAlgea Cao if ((mode->flags & DRM_MODE_FLAG_3D_MASK) ==
1093f5e7d251SAlgea Cao DRM_MODE_FLAG_3D_FRAME_PACKING)
1094f5e7d251SAlgea Cao vmode->mpixelclock *= 2;
10958e2bab3fSAlgea Cao vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock);
10968e2bab3fSAlgea Cao if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
10978e2bab3fSAlgea Cao vmode->mtmdsclock /= 2;
10988e2bab3fSAlgea Cao printf("final pixclk = %d tmdsclk = %d\n",
10998e2bab3fSAlgea Cao vmode->mpixelclock, vmode->mtmdsclock);
1100f5e7d251SAlgea Cao
1101f5e7d251SAlgea Cao /* Set up HDMI_FC_INVIDCONF
11020fbedd06SAlgea Cao * Some display equipments require that the interval
11030fbedd06SAlgea Cao * between Video Data and Data island must be at least 58 pixels,
11040fbedd06SAlgea Cao * and fc_invidconf.HDCP_keepout set (1'b1) can meet the requirement.
1105f5e7d251SAlgea Cao */
11060fbedd06SAlgea Cao inv_val = HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE;
1107f5e7d251SAlgea Cao
1108f5e7d251SAlgea Cao inv_val |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
1109f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
1110f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW;
1111f5e7d251SAlgea Cao
1112f5e7d251SAlgea Cao inv_val |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
1113f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH :
1114f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW;
1115f5e7d251SAlgea Cao
1116f5e7d251SAlgea Cao inv_val |= (vmode->mdataenablepolarity ?
1117f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH :
1118f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW);
1119f5e7d251SAlgea Cao
1120f5e7d251SAlgea Cao if (hdmi->vic == 39)
1121f5e7d251SAlgea Cao inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH;
1122f5e7d251SAlgea Cao else
1123f5e7d251SAlgea Cao inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
1124f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH :
1125f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW;
1126f5e7d251SAlgea Cao
1127f5e7d251SAlgea Cao inv_val |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
1128f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
1129f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE;
1130f5e7d251SAlgea Cao
1131f5e7d251SAlgea Cao inv_val |= hdmi->sink_is_hdmi ?
1132f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
1133f5e7d251SAlgea Cao HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE;
1134f5e7d251SAlgea Cao
1135f5e7d251SAlgea Cao hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
1136f5e7d251SAlgea Cao
1137f5e7d251SAlgea Cao hdisplay = mode->hdisplay;
1138f5e7d251SAlgea Cao hblank = mode->htotal - mode->hdisplay;
1139f5e7d251SAlgea Cao h_de_hs = mode->hsync_start - mode->hdisplay;
1140f5e7d251SAlgea Cao hsync_len = mode->hsync_end - mode->hsync_start;
1141f5e7d251SAlgea Cao
1142f5e7d251SAlgea Cao /*
1143f5e7d251SAlgea Cao * When we're setting a YCbCr420 mode, we need
1144f5e7d251SAlgea Cao * to adjust the horizontal timing to suit.
1145f5e7d251SAlgea Cao */
1146f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) {
1147f5e7d251SAlgea Cao hdisplay /= 2;
1148f5e7d251SAlgea Cao hblank /= 2;
1149f5e7d251SAlgea Cao h_de_hs /= 2;
1150f5e7d251SAlgea Cao hsync_len /= 2;
1151f5e7d251SAlgea Cao }
1152f5e7d251SAlgea Cao
1153f5e7d251SAlgea Cao vdisplay = mode->vdisplay;
1154f5e7d251SAlgea Cao vblank = mode->vtotal - mode->vdisplay;
1155f5e7d251SAlgea Cao v_de_vs = mode->vsync_start - mode->vdisplay;
1156f5e7d251SAlgea Cao vsync_len = mode->vsync_end - mode->vsync_start;
1157f5e7d251SAlgea Cao
1158f5e7d251SAlgea Cao /*
1159f5e7d251SAlgea Cao * When we're setting an interlaced mode, we need
1160f5e7d251SAlgea Cao * to adjust the vertical timing to suit.
1161f5e7d251SAlgea Cao */
1162f5e7d251SAlgea Cao if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
1163f5e7d251SAlgea Cao vdisplay /= 2;
1164f5e7d251SAlgea Cao vblank /= 2;
1165f5e7d251SAlgea Cao v_de_vs /= 2;
1166f5e7d251SAlgea Cao vsync_len /= 2;
1167f5e7d251SAlgea Cao } else if ((mode->flags & DRM_MODE_FLAG_3D_MASK) ==
1168f5e7d251SAlgea Cao DRM_MODE_FLAG_3D_FRAME_PACKING) {
1169f5e7d251SAlgea Cao vdisplay += mode->vtotal;
1170f5e7d251SAlgea Cao }
1171f5e7d251SAlgea Cao
1172f5e7d251SAlgea Cao /* Scrambling Control */
1173b429d5abSAlgea Cao if (hdmi_info->scdc.supported || hdmi->force_output) {
11748e2bab3fSAlgea Cao if (vmode->mtmdsclock > 340000000 ||
11758e2bab3fSAlgea Cao (hdmi_info->scdc.scrambling.low_rates &&
11768e2bab3fSAlgea Cao hdmi->scramble_low_rates)) {
11778e2bab3fSAlgea Cao drm_scdc_readb(&hdmi->adap, SCDC_SINK_VERSION, &bytes);
11788e2bab3fSAlgea Cao drm_scdc_writeb(&hdmi->adap, SCDC_SOURCE_VERSION,
11798e2bab3fSAlgea Cao bytes);
1180f5e7d251SAlgea Cao rockchip_dw_hdmi_scrambling_enable(hdmi, 1);
1181f5e7d251SAlgea Cao } else {
1182f5e7d251SAlgea Cao rockchip_dw_hdmi_scrambling_enable(hdmi, 0);
1183f5e7d251SAlgea Cao }
1184f5e7d251SAlgea Cao }
1185f5e7d251SAlgea Cao
1186f5e7d251SAlgea Cao /* Set up horizontal active pixel width */
1187f5e7d251SAlgea Cao hdmi_writeb(hdmi, hdisplay >> 8, HDMI_FC_INHACTV1);
1188f5e7d251SAlgea Cao hdmi_writeb(hdmi, hdisplay, HDMI_FC_INHACTV0);
1189f5e7d251SAlgea Cao
1190f5e7d251SAlgea Cao /* Set up vertical active lines */
1191f5e7d251SAlgea Cao hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1);
1192f5e7d251SAlgea Cao hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0);
1193f5e7d251SAlgea Cao
1194f5e7d251SAlgea Cao /* Set up horizontal blanking pixel region width */
1195f5e7d251SAlgea Cao hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
1196f5e7d251SAlgea Cao hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
1197f5e7d251SAlgea Cao
1198f5e7d251SAlgea Cao /* Set up vertical blanking pixel region width */
1199f5e7d251SAlgea Cao hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
1200f5e7d251SAlgea Cao
1201f5e7d251SAlgea Cao /* Set up HSYNC active edge delay width (in pixel clks) */
1202f5e7d251SAlgea Cao hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
1203f5e7d251SAlgea Cao hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
1204f5e7d251SAlgea Cao
1205f5e7d251SAlgea Cao /* Set up VSYNC active edge delay (in lines) */
1206f5e7d251SAlgea Cao hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
1207f5e7d251SAlgea Cao
1208f5e7d251SAlgea Cao /* Set up HSYNC active pulse width (in pixel clks) */
1209f5e7d251SAlgea Cao hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
1210f5e7d251SAlgea Cao hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
1211f5e7d251SAlgea Cao
1212f5e7d251SAlgea Cao /* Set up VSYNC active edge delay (in lines) */
1213f5e7d251SAlgea Cao hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
1214f5e7d251SAlgea Cao }
1215f5e7d251SAlgea Cao
dw_hdmi_update_csc_coeffs(struct dw_hdmi * hdmi)1216f5e7d251SAlgea Cao static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi)
1217f5e7d251SAlgea Cao {
1218f5e7d251SAlgea Cao const u16 (*csc_coeff)[3][4] = &csc_coeff_default;
1219f5e7d251SAlgea Cao unsigned i;
1220f5e7d251SAlgea Cao u32 csc_scale = 1;
1221b5016cf2SAlgea Cao int enc_out_rgb, enc_in_rgb;
1222b5016cf2SAlgea Cao
1223b5016cf2SAlgea Cao enc_out_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format);
1224b5016cf2SAlgea Cao enc_in_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format);
1225f5e7d251SAlgea Cao
1226f5e7d251SAlgea Cao if (is_color_space_conversion(hdmi)) {
12273f6d16abSAlgea Cao if (enc_out_rgb && enc_in_rgb) {
1228b5016cf2SAlgea Cao csc_coeff = &csc_coeff_full_to_limited;
1229b5016cf2SAlgea Cao csc_scale = 0;
1230b5016cf2SAlgea Cao } else if (enc_out_rgb) {
1231f5e7d251SAlgea Cao if (hdmi->hdmi_data.enc_out_encoding ==
1232f5e7d251SAlgea Cao V4L2_YCBCR_ENC_601)
1233f5e7d251SAlgea Cao csc_coeff = &csc_coeff_rgb_out_eitu601;
1234f5e7d251SAlgea Cao else
1235f5e7d251SAlgea Cao csc_coeff = &csc_coeff_rgb_out_eitu709;
1236b5016cf2SAlgea Cao } else if (enc_in_rgb) {
1237f5e7d251SAlgea Cao if (hdmi->hdmi_data.enc_out_encoding ==
1238f5e7d251SAlgea Cao V4L2_YCBCR_ENC_601)
1239f5e7d251SAlgea Cao csc_coeff = &csc_coeff_rgb_in_eitu601;
1240f5e7d251SAlgea Cao else
1241f5e7d251SAlgea Cao csc_coeff = &csc_coeff_rgb_in_eitu709;
1242f5e7d251SAlgea Cao csc_scale = 0;
1243f5e7d251SAlgea Cao }
1244f5e7d251SAlgea Cao }
1245f5e7d251SAlgea Cao
1246f5e7d251SAlgea Cao /* The CSC registers are sequential, alternating MSB then LSB */
1247f5e7d251SAlgea Cao for (i = 0; i < ARRAY_SIZE(csc_coeff_default[0]); i++) {
1248f5e7d251SAlgea Cao u16 coeff_a = (*csc_coeff)[0][i];
1249f5e7d251SAlgea Cao u16 coeff_b = (*csc_coeff)[1][i];
1250f5e7d251SAlgea Cao u16 coeff_c = (*csc_coeff)[2][i];
1251f5e7d251SAlgea Cao
1252f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_a & 0xff, HDMI_CSC_COEF_A1_LSB + i * 2);
1253f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
1254f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
1255f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
1256f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_c & 0xff, HDMI_CSC_COEF_C1_LSB + i * 2);
1257f5e7d251SAlgea Cao hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
1258f5e7d251SAlgea Cao }
1259f5e7d251SAlgea Cao
1260f5e7d251SAlgea Cao hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
1261f5e7d251SAlgea Cao HDMI_CSC_SCALE);
1262f5e7d251SAlgea Cao }
1263f5e7d251SAlgea Cao
is_color_space_interpolation(struct dw_hdmi * hdmi)1264f5e7d251SAlgea Cao static int is_color_space_interpolation(struct dw_hdmi *hdmi)
1265f5e7d251SAlgea Cao {
1266f5e7d251SAlgea Cao if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_in_bus_format))
1267f5e7d251SAlgea Cao return 0;
1268f5e7d251SAlgea Cao
1269f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
1270f5e7d251SAlgea Cao hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
1271f5e7d251SAlgea Cao return 1;
1272f5e7d251SAlgea Cao
1273f5e7d251SAlgea Cao return 0;
1274f5e7d251SAlgea Cao }
1275f5e7d251SAlgea Cao
hdmi_video_csc(struct dw_hdmi * hdmi)1276f5e7d251SAlgea Cao static void hdmi_video_csc(struct dw_hdmi *hdmi)
1277f5e7d251SAlgea Cao {
1278f5e7d251SAlgea Cao int color_depth = 0;
1279f5e7d251SAlgea Cao int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE;
1280f5e7d251SAlgea Cao int decimation = 0;
1281f5e7d251SAlgea Cao
1282f5e7d251SAlgea Cao /* YCC422 interpolation to 444 mode */
1283f5e7d251SAlgea Cao if (is_color_space_interpolation(hdmi))
1284f5e7d251SAlgea Cao interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1;
1285f5e7d251SAlgea Cao else if (is_color_space_decimation(hdmi))
1286f5e7d251SAlgea Cao decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3;
1287f5e7d251SAlgea Cao
1288f5e7d251SAlgea Cao switch (hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format)) {
1289f5e7d251SAlgea Cao case 8:
1290f5e7d251SAlgea Cao color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP;
1291f5e7d251SAlgea Cao break;
1292f5e7d251SAlgea Cao case 10:
1293f5e7d251SAlgea Cao color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP;
1294f5e7d251SAlgea Cao break;
1295f5e7d251SAlgea Cao case 12:
1296f5e7d251SAlgea Cao color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP;
1297f5e7d251SAlgea Cao break;
1298f5e7d251SAlgea Cao case 16:
1299f5e7d251SAlgea Cao color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP;
1300f5e7d251SAlgea Cao break;
1301f5e7d251SAlgea Cao
1302f5e7d251SAlgea Cao default:
1303f5e7d251SAlgea Cao return;
1304f5e7d251SAlgea Cao }
1305f5e7d251SAlgea Cao
1306f5e7d251SAlgea Cao /* Configure the CSC registers */
1307f5e7d251SAlgea Cao hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG);
1308f5e7d251SAlgea Cao hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
1309f5e7d251SAlgea Cao HDMI_CSC_SCALE);
1310f5e7d251SAlgea Cao
1311f5e7d251SAlgea Cao dw_hdmi_update_csc_coeffs(hdmi);
1312f5e7d251SAlgea Cao }
1313f5e7d251SAlgea Cao
dw_hdmi_enable_video_path(struct dw_hdmi * hdmi)1314f5e7d251SAlgea Cao static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
1315f5e7d251SAlgea Cao {
1316f5e7d251SAlgea Cao u8 clkdis;
1317f5e7d251SAlgea Cao
1318f5e7d251SAlgea Cao /* control period minimum duration */
1319f5e7d251SAlgea Cao hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
1320f5e7d251SAlgea Cao hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
1321f5e7d251SAlgea Cao hdmi_writeb(hdmi, 1, HDMI_FC_EXCTRLSPAC);
1322f5e7d251SAlgea Cao
1323f5e7d251SAlgea Cao /* Set to fill TMDS data channels */
1324f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0B, HDMI_FC_CH0PREAM);
1325f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x16, HDMI_FC_CH1PREAM);
1326f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
1327f5e7d251SAlgea Cao
1328f5e7d251SAlgea Cao /* Enable pixel clock and tmds data path */
1329f5e7d251SAlgea Cao clkdis = 0x7F;
1330f5e7d251SAlgea Cao clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
1331f5e7d251SAlgea Cao hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
1332f5e7d251SAlgea Cao
1333f5e7d251SAlgea Cao clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
1334f5e7d251SAlgea Cao hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
1335f5e7d251SAlgea Cao
1336f5e7d251SAlgea Cao /* Enable csc path */
1337f5e7d251SAlgea Cao if (is_color_space_conversion(hdmi)) {
1338f5e7d251SAlgea Cao clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
1339f5e7d251SAlgea Cao hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
1340f5e7d251SAlgea Cao }
1341f5e7d251SAlgea Cao
1342f5e7d251SAlgea Cao /* Enable pixel repetition path */
1343f5e7d251SAlgea Cao if (hdmi->hdmi_data.video_mode.mpixelrepetitioninput) {
1344f5e7d251SAlgea Cao clkdis &= ~HDMI_MC_CLKDIS_PREPCLK_DISABLE;
1345f5e7d251SAlgea Cao hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
1346f5e7d251SAlgea Cao }
1347f5e7d251SAlgea Cao
1348f5e7d251SAlgea Cao /* Enable color space conversion if needed */
1349f5e7d251SAlgea Cao if (is_color_space_conversion(hdmi))
1350f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH,
1351f5e7d251SAlgea Cao HDMI_MC_FLOWCTRL);
1352f5e7d251SAlgea Cao else
1353f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS,
1354f5e7d251SAlgea Cao HDMI_MC_FLOWCTRL);
1355f5e7d251SAlgea Cao }
1356f5e7d251SAlgea Cao
dw_hdmi_clear_overflow(struct dw_hdmi * hdmi)1357f5e7d251SAlgea Cao static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
1358f5e7d251SAlgea Cao {
1359f5e7d251SAlgea Cao unsigned int count;
1360f5e7d251SAlgea Cao unsigned int i;
1361f5e7d251SAlgea Cao u8 val;
1362f5e7d251SAlgea Cao
1363f5e7d251SAlgea Cao /*
1364f5e7d251SAlgea Cao * Under some circumstances the Frame Composer arithmetic unit can miss
1365f5e7d251SAlgea Cao * an FC register write due to being busy processing the previous one.
1366f5e7d251SAlgea Cao * The issue can be worked around by issuing a TMDS software reset and
1367f5e7d251SAlgea Cao * then write one of the FC registers several times.
1368f5e7d251SAlgea Cao *
1369f5e7d251SAlgea Cao * The number of iterations matters and depends on the HDMI TX revision
1370f5e7d251SAlgea Cao * (and possibly on the platform). So far only i.MX6Q (v1.30a) and
1371f5e7d251SAlgea Cao * i.MX6DL (v1.31a) have been identified as needing the workaround, with
1372f5e7d251SAlgea Cao * 4 and 1 iterations respectively.
1373f5e7d251SAlgea Cao */
1374f5e7d251SAlgea Cao
1375f5e7d251SAlgea Cao switch (hdmi->version) {
1376f5e7d251SAlgea Cao case 0x130a:
1377f5e7d251SAlgea Cao count = 4;
1378f5e7d251SAlgea Cao break;
1379f5e7d251SAlgea Cao case 0x131a:
13808e2bab3fSAlgea Cao case 0x200a:
13818e2bab3fSAlgea Cao case 0x201a:
13828e2bab3fSAlgea Cao case 0x211a:
1383f5e7d251SAlgea Cao count = 1;
1384f5e7d251SAlgea Cao break;
1385f5e7d251SAlgea Cao default:
1386f5e7d251SAlgea Cao return;
1387f5e7d251SAlgea Cao }
1388f5e7d251SAlgea Cao
1389f5e7d251SAlgea Cao /* TMDS software reset */
1390f5e7d251SAlgea Cao hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
1391f5e7d251SAlgea Cao
1392f5e7d251SAlgea Cao val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF);
1393f5e7d251SAlgea Cao for (i = 0; i < count; i++)
1394f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
1395f5e7d251SAlgea Cao }
1396f5e7d251SAlgea Cao
hdmi_disable_overflow_interrupts(struct dw_hdmi * hdmi)1397f5e7d251SAlgea Cao static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi)
1398f5e7d251SAlgea Cao {
1399f5e7d251SAlgea Cao hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
1400f5e7d251SAlgea Cao HDMI_IH_MUTE_FC_STAT2);
1401f5e7d251SAlgea Cao }
1402f5e7d251SAlgea Cao
hdmi_video_packetize(struct dw_hdmi * hdmi)1403f5e7d251SAlgea Cao static void hdmi_video_packetize(struct dw_hdmi *hdmi)
1404f5e7d251SAlgea Cao {
1405f5e7d251SAlgea Cao unsigned int color_depth = 0;
1406f5e7d251SAlgea Cao unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit;
1407f5e7d251SAlgea Cao unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP;
1408f5e7d251SAlgea Cao struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
1409f5e7d251SAlgea Cao u8 val, vp_conf;
1410f5e7d251SAlgea Cao
1411f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
1412f5e7d251SAlgea Cao hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) ||
1413f5e7d251SAlgea Cao hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) {
1414f5e7d251SAlgea Cao switch (hdmi_bus_fmt_color_depth(
1415f5e7d251SAlgea Cao hdmi->hdmi_data.enc_out_bus_format)) {
1416f5e7d251SAlgea Cao case 8:
1417f5e7d251SAlgea Cao color_depth = 0;
1418f5e7d251SAlgea Cao output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
1419f5e7d251SAlgea Cao break;
1420f5e7d251SAlgea Cao case 10:
1421f5e7d251SAlgea Cao color_depth = 5;
1422f5e7d251SAlgea Cao break;
1423f5e7d251SAlgea Cao case 12:
1424f5e7d251SAlgea Cao color_depth = 6;
1425f5e7d251SAlgea Cao break;
1426f5e7d251SAlgea Cao case 16:
1427f5e7d251SAlgea Cao color_depth = 7;
1428f5e7d251SAlgea Cao break;
1429f5e7d251SAlgea Cao default:
1430f5e7d251SAlgea Cao output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
1431f5e7d251SAlgea Cao }
1432f5e7d251SAlgea Cao } else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) {
1433f5e7d251SAlgea Cao switch (hdmi_bus_fmt_color_depth(
1434f5e7d251SAlgea Cao hdmi->hdmi_data.enc_out_bus_format)) {
1435f5e7d251SAlgea Cao case 0:
1436f5e7d251SAlgea Cao case 8:
1437f5e7d251SAlgea Cao remap_size = HDMI_VP_REMAP_YCC422_16bit;
1438f5e7d251SAlgea Cao break;
1439f5e7d251SAlgea Cao case 10:
1440f5e7d251SAlgea Cao remap_size = HDMI_VP_REMAP_YCC422_20bit;
1441f5e7d251SAlgea Cao break;
1442f5e7d251SAlgea Cao case 12:
1443f5e7d251SAlgea Cao remap_size = HDMI_VP_REMAP_YCC422_24bit;
1444f5e7d251SAlgea Cao break;
1445f5e7d251SAlgea Cao
1446f5e7d251SAlgea Cao default:
1447f5e7d251SAlgea Cao return;
1448f5e7d251SAlgea Cao }
1449f5e7d251SAlgea Cao output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422;
1450f5e7d251SAlgea Cao } else {
1451f5e7d251SAlgea Cao return;
1452f5e7d251SAlgea Cao }
1453f5e7d251SAlgea Cao
1454f5e7d251SAlgea Cao /* set the packetizer registers */
14558e2bab3fSAlgea Cao val = (color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
14568e2bab3fSAlgea Cao HDMI_VP_PR_CD_COLOR_DEPTH_MASK;
1457f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_VP_PR_CD);
1458f5e7d251SAlgea Cao
1459f5e7d251SAlgea Cao hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
1460f5e7d251SAlgea Cao HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF);
1461f5e7d251SAlgea Cao
1462f5e7d251SAlgea Cao /* Data from pixel repeater block */
1463f5e7d251SAlgea Cao if (hdmi_data->pix_repet_factor > 0) {
1464f5e7d251SAlgea Cao vp_conf = HDMI_VP_CONF_PR_EN_ENABLE |
1465f5e7d251SAlgea Cao HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER;
1466f5e7d251SAlgea Cao } else { /* data from packetizer block */
1467f5e7d251SAlgea Cao vp_conf = HDMI_VP_CONF_PR_EN_DISABLE |
1468f5e7d251SAlgea Cao HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
1469f5e7d251SAlgea Cao }
1470f5e7d251SAlgea Cao
1471f5e7d251SAlgea Cao hdmi_modb(hdmi, vp_conf,
1472f5e7d251SAlgea Cao HDMI_VP_CONF_PR_EN_MASK |
1473f5e7d251SAlgea Cao HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF);
1474f5e7d251SAlgea Cao
14758e2bab3fSAlgea Cao hdmi_modb(hdmi, 0, HDMI_VP_STUFF_IDEFAULT_PHASE_MASK,
14768e2bab3fSAlgea Cao HDMI_VP_STUFF);
1477f5e7d251SAlgea Cao
1478f5e7d251SAlgea Cao hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP);
1479f5e7d251SAlgea Cao
1480f5e7d251SAlgea Cao if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) {
1481f5e7d251SAlgea Cao vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
1482f5e7d251SAlgea Cao HDMI_VP_CONF_PP_EN_ENABLE |
1483f5e7d251SAlgea Cao HDMI_VP_CONF_YCC422_EN_DISABLE;
1484f5e7d251SAlgea Cao } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) {
1485f5e7d251SAlgea Cao vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
1486f5e7d251SAlgea Cao HDMI_VP_CONF_PP_EN_DISABLE |
1487f5e7d251SAlgea Cao HDMI_VP_CONF_YCC422_EN_ENABLE;
1488f5e7d251SAlgea Cao } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) {
1489f5e7d251SAlgea Cao vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE |
1490f5e7d251SAlgea Cao HDMI_VP_CONF_PP_EN_DISABLE |
1491f5e7d251SAlgea Cao HDMI_VP_CONF_YCC422_EN_DISABLE;
1492f5e7d251SAlgea Cao } else {
1493f5e7d251SAlgea Cao return;
1494f5e7d251SAlgea Cao }
1495f5e7d251SAlgea Cao
1496f5e7d251SAlgea Cao hdmi_modb(hdmi, vp_conf,
1497f5e7d251SAlgea Cao HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK |
1498f5e7d251SAlgea Cao HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF);
1499f5e7d251SAlgea Cao
1500f5e7d251SAlgea Cao hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
1501f5e7d251SAlgea Cao HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE,
1502f5e7d251SAlgea Cao HDMI_VP_STUFF_PP_STUFFING_MASK |
1503f5e7d251SAlgea Cao HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF);
1504f5e7d251SAlgea Cao
1505f5e7d251SAlgea Cao hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
1506f5e7d251SAlgea Cao HDMI_VP_CONF);
1507f5e7d251SAlgea Cao }
1508f5e7d251SAlgea Cao
hdmi_video_sample(struct dw_hdmi * hdmi)1509f5e7d251SAlgea Cao static void hdmi_video_sample(struct dw_hdmi *hdmi)
1510f5e7d251SAlgea Cao {
1511f5e7d251SAlgea Cao int color_format = 0;
1512f5e7d251SAlgea Cao u8 val;
1513f5e7d251SAlgea Cao
1514f5e7d251SAlgea Cao switch (hdmi->hdmi_data.enc_in_bus_format) {
1515f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB888_1X24:
1516f5e7d251SAlgea Cao color_format = 0x01;
1517f5e7d251SAlgea Cao break;
1518f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB101010_1X30:
1519f5e7d251SAlgea Cao color_format = 0x03;
1520f5e7d251SAlgea Cao break;
1521f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB121212_1X36:
1522f5e7d251SAlgea Cao color_format = 0x05;
1523f5e7d251SAlgea Cao break;
1524f5e7d251SAlgea Cao case MEDIA_BUS_FMT_RGB161616_1X48:
1525f5e7d251SAlgea Cao color_format = 0x07;
1526f5e7d251SAlgea Cao break;
1527f5e7d251SAlgea Cao
1528f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV8_1X24:
1529f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
1530f5e7d251SAlgea Cao color_format = 0x09;
1531f5e7d251SAlgea Cao break;
1532f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV10_1X30:
1533f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
1534f5e7d251SAlgea Cao color_format = 0x0B;
1535f5e7d251SAlgea Cao break;
1536f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV12_1X36:
1537f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
1538f5e7d251SAlgea Cao color_format = 0x0D;
1539f5e7d251SAlgea Cao break;
1540f5e7d251SAlgea Cao case MEDIA_BUS_FMT_YUV16_1X48:
1541f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
1542f5e7d251SAlgea Cao color_format = 0x0F;
1543f5e7d251SAlgea Cao break;
1544f5e7d251SAlgea Cao
1545f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY8_1X16:
1546f5e7d251SAlgea Cao color_format = 0x16;
1547f5e7d251SAlgea Cao break;
1548f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY10_1X20:
1549f5e7d251SAlgea Cao color_format = 0x14;
1550f5e7d251SAlgea Cao break;
1551f5e7d251SAlgea Cao case MEDIA_BUS_FMT_UYVY12_1X24:
1552f5e7d251SAlgea Cao color_format = 0x12;
1553f5e7d251SAlgea Cao break;
1554f5e7d251SAlgea Cao
1555f5e7d251SAlgea Cao default:
1556f5e7d251SAlgea Cao return;
1557f5e7d251SAlgea Cao }
1558f5e7d251SAlgea Cao
1559f5e7d251SAlgea Cao val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
1560f5e7d251SAlgea Cao ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
1561f5e7d251SAlgea Cao HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
1562f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_TX_INVID0);
1563f5e7d251SAlgea Cao
1564f5e7d251SAlgea Cao /* Enable TX stuffing: When DE is inactive, fix the output data to 0 */
1565f5e7d251SAlgea Cao val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
1566f5e7d251SAlgea Cao HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
1567f5e7d251SAlgea Cao HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
1568f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_TX_INSTUFFING);
1569f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA0);
1570f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA1);
1571f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA0);
1572f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA1);
1573f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA0);
1574f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1);
1575f5e7d251SAlgea Cao }
1576f5e7d251SAlgea Cao
dw_hdmi_disable(struct rockchip_connector * conn,struct dw_hdmi * hdmi,struct display_state * state)15770594ce39SZhang Yubing static void dw_hdmi_disable(struct rockchip_connector *conn, struct dw_hdmi *hdmi,
15780594ce39SZhang Yubing struct display_state *state)
1579f5e7d251SAlgea Cao {
1580f5e7d251SAlgea Cao if (hdmi->phy.enabled) {
15810594ce39SZhang Yubing hdmi->phy.ops->disable(conn, hdmi, state);
1582f5e7d251SAlgea Cao hdmi->phy.enabled = false;
1583f5e7d251SAlgea Cao }
1584f5e7d251SAlgea Cao }
1585f5e7d251SAlgea Cao
hdmi_config_AVI(struct dw_hdmi * hdmi,struct drm_display_mode * mode)1586f5e7d251SAlgea Cao static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
1587f5e7d251SAlgea Cao {
1588f5e7d251SAlgea Cao struct hdmi_avi_infoframe frame;
1589f5e7d251SAlgea Cao u8 val;
1590f5e7d251SAlgea Cao bool is_hdmi2 = false;
1591b5016cf2SAlgea Cao enum hdmi_quantization_range rgb_quant_range =
1592b5016cf2SAlgea Cao hdmi->hdmi_data.quant_range;
1593f5e7d251SAlgea Cao
1594f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) ||
1595f5e7d251SAlgea Cao hdmi->edid_data.display_info.hdmi.scdc.supported)
1596f5e7d251SAlgea Cao is_hdmi2 = true;
1597f5e7d251SAlgea Cao /* Initialise info frame from DRM mode */
1598f5e7d251SAlgea Cao drm_hdmi_avi_infoframe_from_display_mode(&frame, mode, is_hdmi2);
1599f5e7d251SAlgea Cao
1600b5016cf2SAlgea Cao /*
1601b5016cf2SAlgea Cao * Ignore monitor selectable quantization, use quantization set
1602b5016cf2SAlgea Cao * by the user
1603b5016cf2SAlgea Cao */
1604b5016cf2SAlgea Cao drm_hdmi_avi_infoframe_quant_range(&frame, mode, rgb_quant_range,
1605b692eb6fSAlgea Cao true, is_hdmi2);
1606f5e7d251SAlgea Cao if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
1607f5e7d251SAlgea Cao frame.colorspace = HDMI_COLORSPACE_YUV444;
1608f5e7d251SAlgea Cao else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
1609f5e7d251SAlgea Cao frame.colorspace = HDMI_COLORSPACE_YUV422;
1610f5e7d251SAlgea Cao else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
1611f5e7d251SAlgea Cao frame.colorspace = HDMI_COLORSPACE_YUV420;
1612f5e7d251SAlgea Cao else
1613f5e7d251SAlgea Cao frame.colorspace = HDMI_COLORSPACE_RGB;
1614f5e7d251SAlgea Cao
1615f5e7d251SAlgea Cao /* Set up colorimetry */
1616f5e7d251SAlgea Cao switch (hdmi->hdmi_data.enc_out_encoding) {
1617f5e7d251SAlgea Cao case V4L2_YCBCR_ENC_601:
1618f5e7d251SAlgea Cao if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV601)
1619f5e7d251SAlgea Cao frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
1620f5e7d251SAlgea Cao else
1621f5e7d251SAlgea Cao frame.colorimetry = HDMI_COLORIMETRY_ITU_601;
1622f5e7d251SAlgea Cao frame.extended_colorimetry =
1623f5e7d251SAlgea Cao HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
1624f5e7d251SAlgea Cao break;
1625f5e7d251SAlgea Cao case V4L2_YCBCR_ENC_709:
1626f5e7d251SAlgea Cao if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV709)
1627f5e7d251SAlgea Cao frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
1628f5e7d251SAlgea Cao else
1629f5e7d251SAlgea Cao frame.colorimetry = HDMI_COLORIMETRY_ITU_709;
1630f5e7d251SAlgea Cao frame.extended_colorimetry =
1631f5e7d251SAlgea Cao HDMI_EXTENDED_COLORIMETRY_XV_YCC_709;
1632f5e7d251SAlgea Cao break;
1633f5e7d251SAlgea Cao default: /* Carries no data */
1634f5e7d251SAlgea Cao frame.colorimetry = HDMI_COLORIMETRY_ITU_601;
1635f5e7d251SAlgea Cao frame.extended_colorimetry =
1636f5e7d251SAlgea Cao HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
1637f5e7d251SAlgea Cao break;
1638f5e7d251SAlgea Cao }
1639f5e7d251SAlgea Cao
1640f5e7d251SAlgea Cao frame.scan_mode = HDMI_SCAN_MODE_NONE;
1641f5e7d251SAlgea Cao
1642f5e7d251SAlgea Cao /*
1643f5e7d251SAlgea Cao * The Designware IP uses a different byte format from standard
1644f5e7d251SAlgea Cao * AVI info frames, though generally the bits are in the correct
1645f5e7d251SAlgea Cao * bytes.
1646f5e7d251SAlgea Cao */
1647f5e7d251SAlgea Cao
1648f5e7d251SAlgea Cao /*
1649f5e7d251SAlgea Cao * AVI data byte 1 differences: Colorspace in bits 0,1,7 rather than
1650f5e7d251SAlgea Cao * 5,6,7, active aspect present in bit 6 rather than 4.
1651f5e7d251SAlgea Cao */
1652f5e7d251SAlgea Cao val = (frame.scan_mode & 3) << 4 | (frame.colorspace & 0x3);
1653f5e7d251SAlgea Cao if (frame.active_aspect & 15)
1654f5e7d251SAlgea Cao val |= HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT;
1655f5e7d251SAlgea Cao if (frame.top_bar || frame.bottom_bar)
1656f5e7d251SAlgea Cao val |= HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR;
1657f5e7d251SAlgea Cao if (frame.left_bar || frame.right_bar)
1658f5e7d251SAlgea Cao val |= HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR;
1659f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0);
1660f5e7d251SAlgea Cao
1661f5e7d251SAlgea Cao /* AVI data byte 2 differences: none */
1662f5e7d251SAlgea Cao val = ((frame.colorimetry & 0x3) << 6) |
1663f5e7d251SAlgea Cao ((frame.picture_aspect & 0x3) << 4) |
1664f5e7d251SAlgea Cao (frame.active_aspect & 0xf);
1665f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_AVICONF1);
1666f5e7d251SAlgea Cao
1667f5e7d251SAlgea Cao /* AVI data byte 3 differences: none */
1668f5e7d251SAlgea Cao val = ((frame.extended_colorimetry & 0x7) << 4) |
1669f5e7d251SAlgea Cao ((frame.quantization_range & 0x3) << 2) |
1670f5e7d251SAlgea Cao (frame.nups & 0x3);
1671f5e7d251SAlgea Cao if (frame.itc)
1672f5e7d251SAlgea Cao val |= HDMI_FC_AVICONF2_IT_CONTENT_VALID;
1673f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2);
1674f5e7d251SAlgea Cao
1675f5e7d251SAlgea Cao /* AVI data byte 4 differences: none */
1676f5e7d251SAlgea Cao val = frame.video_code & 0x7f;
1677f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_AVIVID);
1678f5e7d251SAlgea Cao
1679f5e7d251SAlgea Cao /* AVI Data Byte 5- set up input and output pixel repetition */
1680f5e7d251SAlgea Cao val = (((hdmi->hdmi_data.video_mode.mpixelrepetitioninput + 1) <<
1681f5e7d251SAlgea Cao HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET) &
1682f5e7d251SAlgea Cao HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK) |
1683f5e7d251SAlgea Cao ((hdmi->hdmi_data.video_mode.mpixelrepetitionoutput <<
1684f5e7d251SAlgea Cao HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) &
1685f5e7d251SAlgea Cao HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK);
1686f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_PRCONF);
1687f5e7d251SAlgea Cao
1688f5e7d251SAlgea Cao /*
1689f5e7d251SAlgea Cao * AVI data byte 5 differences: content type in 0,1 rather than 4,5,
1690f5e7d251SAlgea Cao * ycc range in bits 2,3 rather than 6,7
1691f5e7d251SAlgea Cao */
1692f5e7d251SAlgea Cao val = ((frame.ycc_quantization_range & 0x3) << 2) |
1693f5e7d251SAlgea Cao (frame.content_type & 0x3);
1694f5e7d251SAlgea Cao hdmi_writeb(hdmi, val, HDMI_FC_AVICONF3);
1695f5e7d251SAlgea Cao
1696f5e7d251SAlgea Cao /* AVI Data Bytes 6-13 */
1697f5e7d251SAlgea Cao hdmi_writeb(hdmi, frame.top_bar & 0xff, HDMI_FC_AVIETB0);
1698f5e7d251SAlgea Cao hdmi_writeb(hdmi, (frame.top_bar >> 8) & 0xff, HDMI_FC_AVIETB1);
1699f5e7d251SAlgea Cao hdmi_writeb(hdmi, frame.bottom_bar & 0xff, HDMI_FC_AVISBB0);
1700f5e7d251SAlgea Cao hdmi_writeb(hdmi, (frame.bottom_bar >> 8) & 0xff, HDMI_FC_AVISBB1);
1701f5e7d251SAlgea Cao hdmi_writeb(hdmi, frame.left_bar & 0xff, HDMI_FC_AVIELB0);
1702f5e7d251SAlgea Cao hdmi_writeb(hdmi, (frame.left_bar >> 8) & 0xff, HDMI_FC_AVIELB1);
1703f5e7d251SAlgea Cao hdmi_writeb(hdmi, frame.right_bar & 0xff, HDMI_FC_AVISRB0);
1704f5e7d251SAlgea Cao hdmi_writeb(hdmi, (frame.right_bar >> 8) & 0xff, HDMI_FC_AVISRB1);
1705f5e7d251SAlgea Cao }
1706f5e7d251SAlgea Cao
hdmi_config_vendor_specific_infoframe(struct dw_hdmi * hdmi,struct drm_display_mode * mode)1707f5e7d251SAlgea Cao static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi,
1708f5e7d251SAlgea Cao struct drm_display_mode *mode)
1709f5e7d251SAlgea Cao {
1710f5e7d251SAlgea Cao struct hdmi_vendor_infoframe frame;
1711f5e7d251SAlgea Cao u8 buffer[10];
1712f5e7d251SAlgea Cao ssize_t err;
1713f5e7d251SAlgea Cao
1714f5e7d251SAlgea Cao /* Disable HDMI vendor specific infoframe send */
1715f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, 0, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET,
1716f5e7d251SAlgea Cao HDMI_FC_DATAUTO0_VSD_MASK);
1717f5e7d251SAlgea Cao
1718f5e7d251SAlgea Cao err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, mode);
1719f5e7d251SAlgea Cao if (err < 0)
1720f5e7d251SAlgea Cao /*
1721f5e7d251SAlgea Cao * Going into that statement does not means vendor infoframe
1722f5e7d251SAlgea Cao * fails. It just informed us that vendor infoframe is not
1723f5e7d251SAlgea Cao * needed for the selected mode. Only 4k or stereoscopic 3D
1724f5e7d251SAlgea Cao * mode requires vendor infoframe. So just simply return.
1725f5e7d251SAlgea Cao */
1726f5e7d251SAlgea Cao return;
1727f5e7d251SAlgea Cao
1728f5e7d251SAlgea Cao err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer));
1729f5e7d251SAlgea Cao if (err < 0) {
1730f5e7d251SAlgea Cao printf("Failed to pack vendor infoframe: %zd\n", err);
1731f5e7d251SAlgea Cao return;
1732f5e7d251SAlgea Cao }
1733f5e7d251SAlgea Cao
1734f5e7d251SAlgea Cao /* Set the length of HDMI vendor specific InfoFrame payload */
1735f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[2], HDMI_FC_VSDSIZE);
1736f5e7d251SAlgea Cao
1737f5e7d251SAlgea Cao /* Set 24bit IEEE Registration Identifier */
1738f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[4], HDMI_FC_VSDIEEEID0);
1739f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[5], HDMI_FC_VSDIEEEID1);
1740f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[6], HDMI_FC_VSDIEEEID2);
1741f5e7d251SAlgea Cao
1742f5e7d251SAlgea Cao /* Set HDMI_Video_Format and HDMI_VIC/3D_Structure */
1743f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[7], HDMI_FC_VSDPAYLOAD0);
1744f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[8], HDMI_FC_VSDPAYLOAD1);
1745f5e7d251SAlgea Cao
1746f5e7d251SAlgea Cao if (frame.s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
1747f5e7d251SAlgea Cao hdmi_writeb(hdmi, buffer[9], HDMI_FC_VSDPAYLOAD2);
1748f5e7d251SAlgea Cao
1749f5e7d251SAlgea Cao /* Packet frame interpolation */
1750f5e7d251SAlgea Cao hdmi_writeb(hdmi, 1, HDMI_FC_DATAUTO1);
1751f5e7d251SAlgea Cao
1752f5e7d251SAlgea Cao /* Auto packets per frame and line spacing */
1753f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0x11, HDMI_FC_DATAUTO2);
1754f5e7d251SAlgea Cao
1755f5e7d251SAlgea Cao /* Configures the Frame Composer On RDRB mode */
1756f5e7d251SAlgea Cao hdmi_mask_writeb(hdmi, 1, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET,
1757f5e7d251SAlgea Cao HDMI_FC_DATAUTO0_VSD_MASK);
1758f5e7d251SAlgea Cao }
1759f5e7d251SAlgea Cao
hdmi_set_cts_n(struct dw_hdmi * hdmi,unsigned int cts,unsigned int n)1760f5e7d251SAlgea Cao static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
1761f5e7d251SAlgea Cao unsigned int n)
1762f5e7d251SAlgea Cao {
1763f5e7d251SAlgea Cao /* Must be set/cleared first */
1764f5e7d251SAlgea Cao hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
1765f5e7d251SAlgea Cao
1766f5e7d251SAlgea Cao /* nshift factor = 0 */
1767f5e7d251SAlgea Cao hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
1768f5e7d251SAlgea Cao
1769f5e7d251SAlgea Cao hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
1770f5e7d251SAlgea Cao HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
1771f5e7d251SAlgea Cao hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
1772f5e7d251SAlgea Cao hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
1773f5e7d251SAlgea Cao
1774f5e7d251SAlgea Cao hdmi_writeb(hdmi, (n >> 16) & 0x0f, HDMI_AUD_N3);
1775f5e7d251SAlgea Cao hdmi_writeb(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2);
1776f5e7d251SAlgea Cao hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1);
1777f5e7d251SAlgea Cao }
1778f5e7d251SAlgea Cao
hdmi_match_tmds_n_table(struct dw_hdmi * hdmi,unsigned long pixel_clk,unsigned long freq)1779f5e7d251SAlgea Cao static int hdmi_match_tmds_n_table(struct dw_hdmi *hdmi,
1780f5e7d251SAlgea Cao unsigned long pixel_clk,
1781f5e7d251SAlgea Cao unsigned long freq)
1782f5e7d251SAlgea Cao {
1783f5e7d251SAlgea Cao const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data;
1784f5e7d251SAlgea Cao const struct dw_hdmi_audio_tmds_n *tmds_n = NULL;
1785f5e7d251SAlgea Cao int i;
1786f5e7d251SAlgea Cao
1787f5e7d251SAlgea Cao if (plat_data->tmds_n_table) {
1788f5e7d251SAlgea Cao for (i = 0; plat_data->tmds_n_table[i].tmds != 0; i++) {
1789f5e7d251SAlgea Cao if (pixel_clk == plat_data->tmds_n_table[i].tmds) {
1790f5e7d251SAlgea Cao tmds_n = &plat_data->tmds_n_table[i];
1791f5e7d251SAlgea Cao break;
1792f5e7d251SAlgea Cao }
1793f5e7d251SAlgea Cao }
1794f5e7d251SAlgea Cao }
1795f5e7d251SAlgea Cao
1796f5e7d251SAlgea Cao if (!tmds_n) {
1797f5e7d251SAlgea Cao for (i = 0; common_tmds_n_table[i].tmds != 0; i++) {
1798f5e7d251SAlgea Cao if (pixel_clk == common_tmds_n_table[i].tmds) {
1799f5e7d251SAlgea Cao tmds_n = &common_tmds_n_table[i];
1800f5e7d251SAlgea Cao break;
1801f5e7d251SAlgea Cao }
1802f5e7d251SAlgea Cao }
1803f5e7d251SAlgea Cao }
1804f5e7d251SAlgea Cao
1805f5e7d251SAlgea Cao if (!tmds_n)
1806f5e7d251SAlgea Cao return -ENOENT;
1807f5e7d251SAlgea Cao
1808f5e7d251SAlgea Cao switch (freq) {
1809f5e7d251SAlgea Cao case 32000:
1810f5e7d251SAlgea Cao return tmds_n->n_32k;
1811f5e7d251SAlgea Cao case 44100:
1812f5e7d251SAlgea Cao case 88200:
1813f5e7d251SAlgea Cao case 176400:
1814f5e7d251SAlgea Cao return (freq / 44100) * tmds_n->n_44k1;
1815f5e7d251SAlgea Cao case 48000:
1816f5e7d251SAlgea Cao case 96000:
1817f5e7d251SAlgea Cao case 192000:
1818f5e7d251SAlgea Cao return (freq / 48000) * tmds_n->n_48k;
1819f5e7d251SAlgea Cao default:
1820f5e7d251SAlgea Cao return -ENOENT;
1821f5e7d251SAlgea Cao }
1822f5e7d251SAlgea Cao }
1823f5e7d251SAlgea Cao
hdmi_audio_math_diff(unsigned int freq,unsigned int n,unsigned int pixel_clk)1824f5e7d251SAlgea Cao static u64 hdmi_audio_math_diff(unsigned int freq, unsigned int n,
1825f5e7d251SAlgea Cao unsigned int pixel_clk)
1826f5e7d251SAlgea Cao {
1827f5e7d251SAlgea Cao u64 final, diff;
1828f5e7d251SAlgea Cao u64 cts;
1829f5e7d251SAlgea Cao
1830f5e7d251SAlgea Cao final = (u64)pixel_clk * n;
1831f5e7d251SAlgea Cao
1832f5e7d251SAlgea Cao cts = final;
1833f5e7d251SAlgea Cao do_div(cts, 128 * freq);
1834f5e7d251SAlgea Cao
1835f5e7d251SAlgea Cao diff = final - (u64)cts * (128 * freq);
1836f5e7d251SAlgea Cao
1837f5e7d251SAlgea Cao return diff;
1838f5e7d251SAlgea Cao }
1839f5e7d251SAlgea Cao
hdmi_compute_n(struct dw_hdmi * hdmi,unsigned long pixel_clk,unsigned long freq)1840f5e7d251SAlgea Cao static unsigned int hdmi_compute_n(struct dw_hdmi *hdmi,
1841f5e7d251SAlgea Cao unsigned long pixel_clk,
1842f5e7d251SAlgea Cao unsigned long freq)
1843f5e7d251SAlgea Cao {
1844f5e7d251SAlgea Cao unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500);
1845f5e7d251SAlgea Cao unsigned int max_n = (128 * freq) / 300;
1846f5e7d251SAlgea Cao unsigned int ideal_n = (128 * freq) / 1000;
1847f5e7d251SAlgea Cao unsigned int best_n_distance = ideal_n;
1848f5e7d251SAlgea Cao unsigned int best_n = 0;
1849f5e7d251SAlgea Cao u64 best_diff = U64_MAX;
1850f5e7d251SAlgea Cao int n;
1851f5e7d251SAlgea Cao
1852f5e7d251SAlgea Cao /* If the ideal N could satisfy the audio math, then just take it */
1853f5e7d251SAlgea Cao if (hdmi_audio_math_diff(freq, ideal_n, pixel_clk) == 0)
1854f5e7d251SAlgea Cao return ideal_n;
1855f5e7d251SAlgea Cao
1856f5e7d251SAlgea Cao for (n = min_n; n <= max_n; n++) {
1857f5e7d251SAlgea Cao u64 diff = hdmi_audio_math_diff(freq, n, pixel_clk);
1858f5e7d251SAlgea Cao
1859f5e7d251SAlgea Cao if (diff < best_diff || (diff == best_diff &&
1860f5e7d251SAlgea Cao abs(n - ideal_n) < best_n_distance)) {
1861f5e7d251SAlgea Cao best_n = n;
1862f5e7d251SAlgea Cao best_diff = diff;
1863f5e7d251SAlgea Cao best_n_distance = abs(best_n - ideal_n);
1864f5e7d251SAlgea Cao }
1865f5e7d251SAlgea Cao
1866f5e7d251SAlgea Cao /*
1867f5e7d251SAlgea Cao * The best N already satisfy the audio math, and also be
1868f5e7d251SAlgea Cao * the closest value to ideal N, so just cut the loop.
1869f5e7d251SAlgea Cao */
1870f5e7d251SAlgea Cao if ((best_diff == 0) && (abs(n - ideal_n) > best_n_distance))
1871f5e7d251SAlgea Cao break;
1872f5e7d251SAlgea Cao }
1873f5e7d251SAlgea Cao
1874f5e7d251SAlgea Cao return best_n;
1875f5e7d251SAlgea Cao }
1876f5e7d251SAlgea Cao
hdmi_find_n(struct dw_hdmi * hdmi,unsigned long pixel_clk,unsigned long sample_rate)1877f5e7d251SAlgea Cao static unsigned int hdmi_find_n(struct dw_hdmi *hdmi, unsigned long pixel_clk,
1878f5e7d251SAlgea Cao unsigned long sample_rate)
1879f5e7d251SAlgea Cao {
1880f5e7d251SAlgea Cao int n;
1881f5e7d251SAlgea Cao
1882f5e7d251SAlgea Cao n = hdmi_match_tmds_n_table(hdmi, pixel_clk, sample_rate);
1883f5e7d251SAlgea Cao if (n > 0)
1884f5e7d251SAlgea Cao return n;
1885f5e7d251SAlgea Cao
1886f5e7d251SAlgea Cao printf("Rate %lu missing; compute N dynamically\n",
1887f5e7d251SAlgea Cao pixel_clk);
1888f5e7d251SAlgea Cao
1889f5e7d251SAlgea Cao return hdmi_compute_n(hdmi, pixel_clk, sample_rate);
1890f5e7d251SAlgea Cao }
1891f5e7d251SAlgea Cao
1892f5e7d251SAlgea Cao static
hdmi_set_clk_regenerator(struct dw_hdmi * hdmi,unsigned long pixel_clk,unsigned int sample_rate)1893f5e7d251SAlgea Cao void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, unsigned long pixel_clk,
1894f5e7d251SAlgea Cao unsigned int sample_rate)
1895f5e7d251SAlgea Cao {
1896f5e7d251SAlgea Cao unsigned long ftdms = pixel_clk;
1897f5e7d251SAlgea Cao unsigned int n, cts;
1898f5e7d251SAlgea Cao u64 tmp;
1899f5e7d251SAlgea Cao
1900f5e7d251SAlgea Cao n = hdmi_find_n(hdmi, pixel_clk, sample_rate);
1901f5e7d251SAlgea Cao
1902f5e7d251SAlgea Cao /*
1903f5e7d251SAlgea Cao * Compute the CTS value from the N value. Note that CTS and N
1904f5e7d251SAlgea Cao * can be up to 20 bits in total, so we need 64-bit math. Also
1905f5e7d251SAlgea Cao * note that our TDMS clock is not fully accurate; it is accurate
1906f5e7d251SAlgea Cao * to kHz. This can introduce an unnecessary remainder in the
1907f5e7d251SAlgea Cao * calculation below, so we don't try to warn about that.
1908f5e7d251SAlgea Cao */
1909f5e7d251SAlgea Cao tmp = (u64)ftdms * n;
1910f5e7d251SAlgea Cao do_div(tmp, 128 * sample_rate);
1911f5e7d251SAlgea Cao cts = tmp;
1912f5e7d251SAlgea Cao
1913f5e7d251SAlgea Cao printf("%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n", __func__,
1914f5e7d251SAlgea Cao sample_rate, ftdms / 1000000, (ftdms / 1000) % 1000, n, cts);
1915f5e7d251SAlgea Cao
1916f5e7d251SAlgea Cao hdmi->audio_n = n;
1917f5e7d251SAlgea Cao hdmi->audio_cts = cts;
1918f5e7d251SAlgea Cao hdmi_set_cts_n(hdmi, cts, hdmi->audio_enable ? n : 0);
1919f5e7d251SAlgea Cao }
1920f5e7d251SAlgea Cao
hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi * hdmi)1921f5e7d251SAlgea Cao static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
1922f5e7d251SAlgea Cao {
19238e2bab3fSAlgea Cao hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
1924f5e7d251SAlgea Cao hdmi->sample_rate);
1925f5e7d251SAlgea Cao }
1926f5e7d251SAlgea Cao
hdmi_enable_audio_clk(struct dw_hdmi * hdmi)1927f5e7d251SAlgea Cao static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi)
1928f5e7d251SAlgea Cao {
1929f5e7d251SAlgea Cao hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
1930f5e7d251SAlgea Cao }
1931f5e7d251SAlgea Cao
dw_hdmi_set_sample_rate(struct dw_hdmi * hdmi,unsigned int rate)1932f5e7d251SAlgea Cao void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
1933f5e7d251SAlgea Cao {
1934f5e7d251SAlgea Cao hdmi->sample_rate = rate;
19358e2bab3fSAlgea Cao hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
1936f5e7d251SAlgea Cao hdmi->sample_rate);
1937f5e7d251SAlgea Cao }
1938f5e7d251SAlgea Cao
19396a3f4548SSandy Huang #ifndef CONFIG_SPL_BUILD
dw_hdmi_hdcp_load_key(struct dw_hdmi * hdmi)19408e2bab3fSAlgea Cao static int dw_hdmi_hdcp_load_key(struct dw_hdmi *hdmi)
19418e2bab3fSAlgea Cao {
19428e2bab3fSAlgea Cao int i, j, ret, val;
19438e2bab3fSAlgea Cao struct hdcp_keys *hdcp_keys;
19448e2bab3fSAlgea Cao
19458e2bab3fSAlgea Cao val = sizeof(*hdcp_keys);
19468e2bab3fSAlgea Cao hdcp_keys = malloc(val);
19478e2bab3fSAlgea Cao if (!hdcp_keys)
19488e2bab3fSAlgea Cao return -ENOMEM;
19498e2bab3fSAlgea Cao
19508e2bab3fSAlgea Cao memset(hdcp_keys, 0, val);
19518e2bab3fSAlgea Cao
19528e2bab3fSAlgea Cao ret = vendor_storage_read(HDMI_HDCP1X_ID, hdcp_keys, val);
19538e2bab3fSAlgea Cao if (ret < val) {
19548e2bab3fSAlgea Cao printf("HDCP: read size %d\n", ret);
19558e2bab3fSAlgea Cao free(hdcp_keys);
19568e2bab3fSAlgea Cao return -EINVAL;
19578e2bab3fSAlgea Cao }
19588e2bab3fSAlgea Cao
19598e2bab3fSAlgea Cao if (hdcp_keys->KSV[0] == 0x00 &&
19608e2bab3fSAlgea Cao hdcp_keys->KSV[1] == 0x00 &&
19618e2bab3fSAlgea Cao hdcp_keys->KSV[2] == 0x00 &&
19628e2bab3fSAlgea Cao hdcp_keys->KSV[3] == 0x00 &&
19638e2bab3fSAlgea Cao hdcp_keys->KSV[4] == 0x00) {
19648e2bab3fSAlgea Cao printf("HDCP: Invalid hdcp key\n");
19658e2bab3fSAlgea Cao free(hdcp_keys);
19668e2bab3fSAlgea Cao return -EINVAL;
19678e2bab3fSAlgea Cao }
19688e2bab3fSAlgea Cao
19698e2bab3fSAlgea Cao /* Disable decryption logic */
19708e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0, HDMI_HDCPREG_RMCTL);
19718e2bab3fSAlgea Cao /* Poll untile DPK write is allowed */
19728e2bab3fSAlgea Cao do {
19738e2bab3fSAlgea Cao val = hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS);
19748e2bab3fSAlgea Cao } while ((val & DPK_WR_OK_STS) == 0);
19758e2bab3fSAlgea Cao
19768e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0, HDMI_HDCPREG_DPK6);
19778e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0, HDMI_HDCPREG_DPK5);
19788e2bab3fSAlgea Cao
19798e2bab3fSAlgea Cao /* The useful data in ksv should be 5 byte */
19808e2bab3fSAlgea Cao for (i = 4; i >= 0; i--)
19818e2bab3fSAlgea Cao hdmi_writeb(hdmi, hdcp_keys->KSV[i], HDMI_HDCPREG_DPK0 + i);
19828e2bab3fSAlgea Cao /* Poll untile DPK write is allowed */
19838e2bab3fSAlgea Cao do {
19848e2bab3fSAlgea Cao val = hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS);
19858e2bab3fSAlgea Cao } while ((val & DPK_WR_OK_STS) == 0);
19868e2bab3fSAlgea Cao
19878e2bab3fSAlgea Cao /* Enable decryption logic */
19888e2bab3fSAlgea Cao hdmi_writeb(hdmi, 1, HDMI_HDCPREG_RMCTL);
19898e2bab3fSAlgea Cao hdmi_writeb(hdmi, hdcp_keys->seeds[0], HDMI_HDCPREG_SEED1);
19908e2bab3fSAlgea Cao hdmi_writeb(hdmi, hdcp_keys->seeds[1], HDMI_HDCPREG_SEED0);
19918e2bab3fSAlgea Cao
19928e2bab3fSAlgea Cao /* Write encrypt device private key */
19938e2bab3fSAlgea Cao for (i = 0; i < DW_HDMI_HDCP_DPK_LEN - 6; i += 7) {
19948e2bab3fSAlgea Cao for (j = 6; j >= 0; j--)
19958e2bab3fSAlgea Cao hdmi_writeb(hdmi, hdcp_keys->devicekey[i + j],
19968e2bab3fSAlgea Cao HDMI_HDCPREG_DPK0 + j);
19978e2bab3fSAlgea Cao do {
19988e2bab3fSAlgea Cao val = hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS);
19998e2bab3fSAlgea Cao } while ((val & DPK_WR_OK_STS) == 0);
20008e2bab3fSAlgea Cao }
20018e2bab3fSAlgea Cao
20028e2bab3fSAlgea Cao free(hdcp_keys);
20038e2bab3fSAlgea Cao return 0;
20048e2bab3fSAlgea Cao }
20056a3f4548SSandy Huang #endif
20068e2bab3fSAlgea Cao
hdmi_tx_hdcp_config(struct dw_hdmi * hdmi,const struct drm_display_mode * mode)20078e2bab3fSAlgea Cao static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi,
20088e2bab3fSAlgea Cao const struct drm_display_mode *mode)
20098e2bab3fSAlgea Cao {
20108e2bab3fSAlgea Cao u8 vsync_pol, hsync_pol, data_pol, hdmi_dvi;
20118e2bab3fSAlgea Cao
20128e2bab3fSAlgea Cao if (!hdmi->hdcp1x_enable)
20138e2bab3fSAlgea Cao return;
20148e2bab3fSAlgea Cao
20158e2bab3fSAlgea Cao /* Configure the video polarity */
20168e2bab3fSAlgea Cao vsync_pol = mode->flags & DRM_MODE_FLAG_PVSYNC ?
20178e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH :
20188e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW;
20198e2bab3fSAlgea Cao hsync_pol = mode->flags & DRM_MODE_FLAG_PHSYNC ?
20208e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH :
20218e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW;
20228e2bab3fSAlgea Cao data_pol = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH;
20238e2bab3fSAlgea Cao hdmi_modb(hdmi, vsync_pol | hsync_pol | data_pol,
20248e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_VSYNCPOL_MASK |
20258e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_HSYNCPOL_MASK |
20268e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG_DATAENPOL_MASK,
20278e2bab3fSAlgea Cao HDMI_A_VIDPOLCFG);
20288e2bab3fSAlgea Cao
20298e2bab3fSAlgea Cao /* Config the display mode */
20308e2bab3fSAlgea Cao hdmi_dvi = hdmi->sink_is_hdmi ? HDMI_A_HDCPCFG0_HDMIDVI_HDMI :
20318e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_HDMIDVI_DVI;
20328e2bab3fSAlgea Cao hdmi_modb(hdmi, hdmi_dvi, HDMI_A_HDCPCFG0_HDMIDVI_MASK,
20338e2bab3fSAlgea Cao HDMI_A_HDCPCFG0);
20348e2bab3fSAlgea Cao
20356a3f4548SSandy Huang #ifndef CONFIG_SPL_BUILD
20368e2bab3fSAlgea Cao if (!(hdmi_readb(hdmi, HDMI_HDCPREG_RMSTS) & 0x3f))
20378e2bab3fSAlgea Cao dw_hdmi_hdcp_load_key(hdmi);
20386a3f4548SSandy Huang #endif
20398e2bab3fSAlgea Cao
20408e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE,
20418e2bab3fSAlgea Cao HDMI_FC_INVIDCONF_HDCP_KEEPOUT_MASK,
20428e2bab3fSAlgea Cao HDMI_FC_INVIDCONF);
20438e2bab3fSAlgea Cao
20448e2bab3fSAlgea Cao if (hdmi_readb(hdmi, HDMI_CONFIG1_ID) & HDMI_A_HDCP22_MASK) {
20458e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_HDCP2_OVR_ENABLE |
20468e2bab3fSAlgea Cao HDMI_HDCP2_FORCE_DISABLE,
20478e2bab3fSAlgea Cao HDMI_HDCP2_OVR_EN_MASK |
20488e2bab3fSAlgea Cao HDMI_HDCP2_FORCE_MASK,
20498e2bab3fSAlgea Cao HDMI_HDCP2REG_CTRL);
20508e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_HDCP2REG_MASK);
20518e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_HDCP2REG_MUTE);
20528e2bab3fSAlgea Cao }
20538e2bab3fSAlgea Cao
20548e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0x40, HDMI_A_OESSWCFG);
20558e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_A_HDCPCFG0_BYPENCRYPTION_DISABLE |
20568e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_EN11FEATURE_DISABLE |
20578e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_SYNCRICHECK_ENABLE,
20588e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_BYPENCRYPTION_MASK |
20598e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_EN11FEATURE_MASK |
20608e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_SYNCRICHECK_MASK, HDMI_A_HDCPCFG0);
20618e2bab3fSAlgea Cao
20628e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_ENABLE |
20638e2bab3fSAlgea Cao HDMI_A_HDCPCFG1_PH2UPSHFTENC_ENABLE,
20648e2bab3fSAlgea Cao HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK |
20658e2bab3fSAlgea Cao HDMI_A_HDCPCFG1_PH2UPSHFTENC_MASK, HDMI_A_HDCPCFG1);
20668e2bab3fSAlgea Cao
20678e2bab3fSAlgea Cao /* Reset HDCP Engine */
20688e2bab3fSAlgea Cao if (hdmi_readb(hdmi, HDMI_MC_CLKDIS) & HDMI_MC_CLKDIS_HDCPCLK_MASK) {
20698e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_A_HDCPCFG1_SWRESET_ASSERT,
20708e2bab3fSAlgea Cao HDMI_A_HDCPCFG1_SWRESET_MASK, HDMI_A_HDCPCFG1);
20718e2bab3fSAlgea Cao }
20728e2bab3fSAlgea Cao
20738e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0x00, HDMI_A_APIINTMSK);
20748e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_ENABLE,
20758e2bab3fSAlgea Cao HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0);
20768e2bab3fSAlgea Cao
20778e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_MC_CLKDIS_HDCPCLK_ENABLE,
20788e2bab3fSAlgea Cao HDMI_MC_CLKDIS_HDCPCLK_MASK, HDMI_MC_CLKDIS);
20798e2bab3fSAlgea Cao
20808e2bab3fSAlgea Cao printf("%s success\n", __func__);
20818e2bab3fSAlgea Cao }
20828e2bab3fSAlgea Cao
dw_hdmi_setup(struct dw_hdmi * hdmi,struct rockchip_connector * conn,struct drm_display_mode * mode,struct display_state * state)2083f5e7d251SAlgea Cao static int dw_hdmi_setup(struct dw_hdmi *hdmi,
20840594ce39SZhang Yubing struct rockchip_connector *conn,
20858e2bab3fSAlgea Cao struct drm_display_mode *mode,
20868e2bab3fSAlgea Cao struct display_state *state)
2087f5e7d251SAlgea Cao {
2088f5e7d251SAlgea Cao int ret;
2089f5e7d251SAlgea Cao void *data = hdmi->plat_data->phy_data;
2090f5e7d251SAlgea Cao
2091f5e7d251SAlgea Cao hdmi_disable_overflow_interrupts(hdmi);
2092f5e7d251SAlgea Cao if (!hdmi->vic)
2093f5e7d251SAlgea Cao printf("Non-CEA mode used in HDMI\n");
2094f5e7d251SAlgea Cao else
2095f5e7d251SAlgea Cao printf("CEA mode used vic=%d\n", hdmi->vic);
2096f5e7d251SAlgea Cao
2097f5e7d251SAlgea Cao if (hdmi->plat_data->get_enc_out_encoding)
2098f5e7d251SAlgea Cao hdmi->hdmi_data.enc_out_encoding =
2099f5e7d251SAlgea Cao hdmi->plat_data->get_enc_out_encoding(data);
21005ccad8f6SAlgea Cao else if (hdmi->vic == 6 || hdmi->vic == 7 ||
21015ccad8f6SAlgea Cao hdmi->vic == 21 || hdmi->vic == 22 ||
21025ccad8f6SAlgea Cao hdmi->vic == 2 || hdmi->vic == 3 ||
21035ccad8f6SAlgea Cao hdmi->vic == 17 || hdmi->vic == 18)
2104f5e7d251SAlgea Cao hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601;
2105f5e7d251SAlgea Cao else
2106f5e7d251SAlgea Cao hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709;
2107f5e7d251SAlgea Cao
2108f5e7d251SAlgea Cao if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
2109f5e7d251SAlgea Cao hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
2110f5e7d251SAlgea Cao hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 1;
2111f5e7d251SAlgea Cao } else {
2112f5e7d251SAlgea Cao hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
2113f5e7d251SAlgea Cao hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
2114f5e7d251SAlgea Cao }
2115f5e7d251SAlgea Cao
2116f5e7d251SAlgea Cao /* TOFIX: Get input encoding from plat data or fallback to none */
2117f5e7d251SAlgea Cao if (hdmi->plat_data->get_enc_in_encoding)
2118f5e7d251SAlgea Cao hdmi->hdmi_data.enc_in_encoding =
2119f5e7d251SAlgea Cao hdmi->plat_data->get_enc_in_encoding(data);
2120f5e7d251SAlgea Cao else if (hdmi->plat_data->input_bus_encoding)
2121f5e7d251SAlgea Cao hdmi->hdmi_data.enc_in_encoding =
2122f5e7d251SAlgea Cao hdmi->plat_data->input_bus_encoding;
2123f5e7d251SAlgea Cao else
2124f5e7d251SAlgea Cao hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT;
2125b5016cf2SAlgea Cao
2126b5016cf2SAlgea Cao if (hdmi->plat_data->get_quant_range)
2127b5016cf2SAlgea Cao hdmi->hdmi_data.quant_range =
2128b5016cf2SAlgea Cao hdmi->plat_data->get_quant_range(data);
2129b5016cf2SAlgea Cao else
2130b5016cf2SAlgea Cao hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
2131b5016cf2SAlgea Cao
2132f5e7d251SAlgea Cao /*
2133f5e7d251SAlgea Cao * According to the dw-hdmi specification 6.4.2
2134f5e7d251SAlgea Cao * vp_pr_cd[3:0]:
2135f5e7d251SAlgea Cao * 0000b: No pixel repetition (pixel sent only once)
2136f5e7d251SAlgea Cao * 0001b: Pixel sent two times (pixel repeated once)
2137f5e7d251SAlgea Cao */
2138f5e7d251SAlgea Cao hdmi->hdmi_data.pix_repet_factor =
2139f5e7d251SAlgea Cao (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 1 : 0;
2140f5e7d251SAlgea Cao hdmi->hdmi_data.video_mode.mdataenablepolarity = true;
2141f5e7d251SAlgea Cao
2142f5e7d251SAlgea Cao /* HDMI Initialization Step B.1 */
2143f5e7d251SAlgea Cao hdmi_av_composer(hdmi, mode);
2144f5e7d251SAlgea Cao
2145f5e7d251SAlgea Cao /* HDMI Initialization Step B.2 */
21460594ce39SZhang Yubing ret = hdmi->phy.ops->init(conn, hdmi, state);
2147f5e7d251SAlgea Cao if (ret)
2148f5e7d251SAlgea Cao return ret;
2149f5e7d251SAlgea Cao hdmi->phy.enabled = true;
2150f5e7d251SAlgea Cao
2151f5e7d251SAlgea Cao /* HDMI Initializateion Step B.3 */
2152f5e7d251SAlgea Cao dw_hdmi_enable_video_path(hdmi);
2153f5e7d251SAlgea Cao
2154f5e7d251SAlgea Cao /* HDMI Initialization Step E - Configure audio */
2155f5e7d251SAlgea Cao if (hdmi->sink_has_audio) {
2156f5e7d251SAlgea Cao printf("sink has audio support\n");
2157f5e7d251SAlgea Cao hdmi_clk_regenerator_update_pixel_clock(hdmi);
2158f5e7d251SAlgea Cao hdmi_enable_audio_clk(hdmi);
2159f5e7d251SAlgea Cao }
2160f5e7d251SAlgea Cao
2161f5e7d251SAlgea Cao /* not for DVI mode */
2162f5e7d251SAlgea Cao if (hdmi->sink_is_hdmi) {
2163f5e7d251SAlgea Cao /* HDMI Initialization Step F - Configure AVI InfoFrame */
2164f5e7d251SAlgea Cao hdmi_config_AVI(hdmi, mode);
2165f5e7d251SAlgea Cao hdmi_config_vendor_specific_infoframe(hdmi, mode);
2166b327b539SShunqing Chen hdmi_modb(hdmi, HDMI_A_HDCPCFG0_HDMIDVI_HDMI,
2167b327b539SShunqing Chen HDMI_A_HDCPCFG0_HDMIDVI_MASK,
2168b327b539SShunqing Chen HDMI_A_HDCPCFG0);
2169f5e7d251SAlgea Cao } else {
2170b327b539SShunqing Chen hdmi_modb(hdmi, HDMI_A_HDCPCFG0_HDMIDVI_DVI,
2171b327b539SShunqing Chen HDMI_A_HDCPCFG0_HDMIDVI_MASK,
2172b327b539SShunqing Chen HDMI_A_HDCPCFG0);
2173f5e7d251SAlgea Cao printf("%s DVI mode\n", __func__);
2174f5e7d251SAlgea Cao }
2175f5e7d251SAlgea Cao
2176f5e7d251SAlgea Cao hdmi_video_packetize(hdmi);
2177f5e7d251SAlgea Cao hdmi_video_csc(hdmi);
2178f5e7d251SAlgea Cao hdmi_video_sample(hdmi);
21798e2bab3fSAlgea Cao hdmi_tx_hdcp_config(hdmi, mode);
2180f5e7d251SAlgea Cao dw_hdmi_clear_overflow(hdmi);
2181f5e7d251SAlgea Cao
2182f5e7d251SAlgea Cao return 0;
2183f5e7d251SAlgea Cao }
2184f5e7d251SAlgea Cao
dw_hdmi_detect_hotplug(struct dw_hdmi * hdmi,struct display_state * state)21858e2bab3fSAlgea Cao int dw_hdmi_detect_hotplug(struct dw_hdmi *hdmi,
21868e2bab3fSAlgea Cao struct display_state *state)
2187f5e7d251SAlgea Cao {
2188c3a20b0eSChen Shunqing struct connector_state *conn_state = &state->conn_state;
2189c3a20b0eSChen Shunqing struct rockchip_connector *conn = conn_state->connector;
2190c3a20b0eSChen Shunqing int ret;
2191c3a20b0eSChen Shunqing
2192c3a20b0eSChen Shunqing ret = hdmi->phy.ops->read_hpd(hdmi, state);
2193c3a20b0eSChen Shunqing if (!ret) {
2194c3a20b0eSChen Shunqing if (conn->bridge)
2195c3a20b0eSChen Shunqing ret = rockchip_bridge_detect(conn->bridge);
2196c3a20b0eSChen Shunqing }
2197c3a20b0eSChen Shunqing
2198c3a20b0eSChen Shunqing return ret;
2199f5e7d251SAlgea Cao }
2200f5e7d251SAlgea Cao
dw_hdmi_set_reg_wr(struct dw_hdmi * hdmi)2201f5e7d251SAlgea Cao static int dw_hdmi_set_reg_wr(struct dw_hdmi *hdmi)
2202f5e7d251SAlgea Cao {
2203f5e7d251SAlgea Cao switch (hdmi->io_width) {
2204f5e7d251SAlgea Cao case 4:
2205f5e7d251SAlgea Cao hdmi->write = dw_hdmi_writel;
2206f5e7d251SAlgea Cao hdmi->read = dw_hdmi_readl;
2207f5e7d251SAlgea Cao break;
2208f5e7d251SAlgea Cao case 1:
2209f5e7d251SAlgea Cao hdmi->write = dw_hdmi_writeb;
2210f5e7d251SAlgea Cao hdmi->read = dw_hdmi_readb;
2211f5e7d251SAlgea Cao break;
2212f5e7d251SAlgea Cao default:
2213f5e7d251SAlgea Cao printf("reg-io-width must be 1 or 4\n");
2214f5e7d251SAlgea Cao return -EINVAL;
2215f5e7d251SAlgea Cao }
2216f5e7d251SAlgea Cao
2217f5e7d251SAlgea Cao return 0;
2218f5e7d251SAlgea Cao }
2219f5e7d251SAlgea Cao
initialize_hdmi_mutes(struct dw_hdmi * hdmi)2220f5e7d251SAlgea Cao static void initialize_hdmi_mutes(struct dw_hdmi *hdmi)
2221f5e7d251SAlgea Cao {
2222f5e7d251SAlgea Cao /*mute unnecessary interrupt, only enable hpd */
22235ccad8f6SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK0);
22245ccad8f6SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK1);
22255ccad8f6SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK2);
2226f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0);
2227f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1);
2228f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2);
2229f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0);
2230f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xfe, HDMI_IH_MUTE_PHY_STAT0);
2231f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0);
2232f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0);
2233f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0);
2234f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
2235f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
2236f5e7d251SAlgea Cao hdmi_writeb(hdmi, 0xf1, HDMI_PHY_MASK0);
2237f5e7d251SAlgea Cao
2238f5e7d251SAlgea Cao /*Force output black*/
2239f5e7d251SAlgea Cao dw_hdmi_writel(hdmi, 0x00, HDMI_FC_DBGTMDS2);
2240f5e7d251SAlgea Cao dw_hdmi_writel(hdmi, 0x00, HDMI_FC_DBGTMDS1);
2241f5e7d251SAlgea Cao dw_hdmi_writel(hdmi, 0x00, HDMI_FC_DBGTMDS0);
2242f5e7d251SAlgea Cao }
2243f5e7d251SAlgea Cao
dw_hdmi_dev_init(struct dw_hdmi * hdmi)2244f5e7d251SAlgea Cao static void dw_hdmi_dev_init(struct dw_hdmi *hdmi)
2245f5e7d251SAlgea Cao {
2246f5e7d251SAlgea Cao hdmi->version = (hdmi_readb(hdmi, HDMI_DESIGN_ID) << 8)
2247f5e7d251SAlgea Cao | (hdmi_readb(hdmi, HDMI_REVISION_ID) << 0);
2248f5e7d251SAlgea Cao
2249f5e7d251SAlgea Cao initialize_hdmi_mutes(hdmi);
2250f5e7d251SAlgea Cao }
2251f5e7d251SAlgea Cao
dw_hdmi_i2c_set_divs(struct dw_hdmi * hdmi)22528e2bab3fSAlgea Cao static void dw_hdmi_i2c_set_divs(struct dw_hdmi *hdmi)
2253f5e7d251SAlgea Cao {
22548e2bab3fSAlgea Cao unsigned long low_ns, high_ns;
22558e2bab3fSAlgea Cao unsigned long div_low, div_high;
2256f5e7d251SAlgea Cao
22578e2bab3fSAlgea Cao /* Standard-mode */
22588e2bab3fSAlgea Cao if (hdmi->i2c->scl_high_ns < 4000)
22598e2bab3fSAlgea Cao high_ns = 4708;
2260f5e7d251SAlgea Cao else
22618e2bab3fSAlgea Cao high_ns = hdmi->i2c->scl_high_ns;
2262f5e7d251SAlgea Cao
22638e2bab3fSAlgea Cao if (hdmi->i2c->scl_low_ns < 4700)
22648e2bab3fSAlgea Cao low_ns = 4916;
22658e2bab3fSAlgea Cao else
22668e2bab3fSAlgea Cao low_ns = hdmi->i2c->scl_low_ns;
22678e2bab3fSAlgea Cao
22688e2bab3fSAlgea Cao div_low = (24000 * low_ns) / 1000000;
22698e2bab3fSAlgea Cao if ((24000 * low_ns) % 1000000)
22708e2bab3fSAlgea Cao div_low++;
22718e2bab3fSAlgea Cao
22728e2bab3fSAlgea Cao div_high = (24000 * high_ns) / 1000000;
22738e2bab3fSAlgea Cao if ((24000 * high_ns) % 1000000)
22748e2bab3fSAlgea Cao div_high++;
22758e2bab3fSAlgea Cao
22768e2bab3fSAlgea Cao /* Maximum divider supported by hw is 0xffff */
22778e2bab3fSAlgea Cao if (div_low > 0xffff)
22788e2bab3fSAlgea Cao div_low = 0xffff;
22798e2bab3fSAlgea Cao
22808e2bab3fSAlgea Cao if (div_high > 0xffff)
22818e2bab3fSAlgea Cao div_high = 0xffff;
22828e2bab3fSAlgea Cao
22838e2bab3fSAlgea Cao hdmi_writeb(hdmi, div_high & 0xff, HDMI_I2CM_SS_SCL_HCNT_0_ADDR);
22848e2bab3fSAlgea Cao hdmi_writeb(hdmi, (div_high >> 8) & 0xff,
22858e2bab3fSAlgea Cao HDMI_I2CM_SS_SCL_HCNT_1_ADDR);
22868e2bab3fSAlgea Cao hdmi_writeb(hdmi, div_low & 0xff, HDMI_I2CM_SS_SCL_LCNT_0_ADDR);
22878e2bab3fSAlgea Cao hdmi_writeb(hdmi, (div_low >> 8) & 0xff,
22888e2bab3fSAlgea Cao HDMI_I2CM_SS_SCL_LCNT_1_ADDR);
2289f5e7d251SAlgea Cao }
2290f5e7d251SAlgea Cao
dw_hdmi_i2c_init(struct dw_hdmi * hdmi)22918e2bab3fSAlgea Cao static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
2292f5e7d251SAlgea Cao {
22938e2bab3fSAlgea Cao /* Software reset */
22948e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ);
2295f5e7d251SAlgea Cao
22968e2bab3fSAlgea Cao /* Set Standard Mode speed */
22978e2bab3fSAlgea Cao hdmi_modb(hdmi, HDMI_I2CM_DIV_STD_MODE,
22988e2bab3fSAlgea Cao HDMI_I2CM_DIV_FAST_STD_MODE, HDMI_I2CM_DIV);
2299f5e7d251SAlgea Cao
23008e2bab3fSAlgea Cao /* Set done, not acknowledged and arbitration interrupt polarities */
23018e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_INT_DONE_POL, HDMI_I2CM_INT);
23028e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_I2CM_CTLINT_NAC_POL | HDMI_I2CM_CTLINT_ARB_POL,
23038e2bab3fSAlgea Cao HDMI_I2CM_CTLINT);
2304f5e7d251SAlgea Cao
23058e2bab3fSAlgea Cao /* Clear DONE and ERROR interrupts */
23068e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
23078e2bab3fSAlgea Cao HDMI_IH_I2CM_STAT0);
2308f5e7d251SAlgea Cao
23098e2bab3fSAlgea Cao /* Mute DONE and ERROR interrupts */
23108e2bab3fSAlgea Cao hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
23118e2bab3fSAlgea Cao HDMI_IH_MUTE_I2CM_STAT0);
2312f5e7d251SAlgea Cao
23138e2bab3fSAlgea Cao /* set SDA high level holding time */
23148e2bab3fSAlgea Cao hdmi_writeb(hdmi, 0x48, HDMI_I2CM_SDA_HOLD);
2315f5e7d251SAlgea Cao
23168e2bab3fSAlgea Cao dw_hdmi_i2c_set_divs(hdmi);
2317f5e7d251SAlgea Cao }
2318f5e7d251SAlgea Cao
dw_hdmi_audio_enable(struct dw_hdmi * hdmi)2319f5e7d251SAlgea Cao void dw_hdmi_audio_enable(struct dw_hdmi *hdmi)
2320f5e7d251SAlgea Cao {
2321f5e7d251SAlgea Cao hdmi->audio_enable = true;
2322f5e7d251SAlgea Cao hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
2323f5e7d251SAlgea Cao }
2324f5e7d251SAlgea Cao
dw_hdmi_audio_disable(struct dw_hdmi * hdmi)2325f5e7d251SAlgea Cao void dw_hdmi_audio_disable(struct dw_hdmi *hdmi)
2326f5e7d251SAlgea Cao {
2327f5e7d251SAlgea Cao hdmi->audio_enable = false;
2328f5e7d251SAlgea Cao hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
2329f5e7d251SAlgea Cao }
2330f5e7d251SAlgea Cao
rockchip_dw_hdmi_init(struct rockchip_connector * conn,struct display_state * state)23310594ce39SZhang Yubing int rockchip_dw_hdmi_init(struct rockchip_connector *conn, struct display_state *state)
233258c17f51SSandy Huang {
233358c17f51SSandy Huang struct connector_state *conn_state = &state->conn_state;
23340594ce39SZhang Yubing const struct dw_hdmi_plat_data *pdata =
23356a3f4548SSandy Huang #ifdef CONFIG_SPL_BUILD
23366a3f4548SSandy Huang (const struct dw_hdmi_plat_data *)conn->data;
23376a3f4548SSandy Huang #else
23380594ce39SZhang Yubing (const struct dw_hdmi_plat_data *)dev_get_driver_data(conn->dev);
23396a3f4548SSandy Huang ofnode hdmi_node = conn->dev->node;
23406a3f4548SSandy Huang struct device_node *ddc_node;
23416a3f4548SSandy Huang int ret;
23426a3f4548SSandy Huang #endif
2343f5e7d251SAlgea Cao struct crtc_state *crtc_state = &state->crtc_state;
2344f5e7d251SAlgea Cao struct dw_hdmi *hdmi;
2345f5e7d251SAlgea Cao struct drm_display_mode *mode_buf;
2346f5e7d251SAlgea Cao u32 val;
2347f5e7d251SAlgea Cao
2348f5e7d251SAlgea Cao hdmi = malloc(sizeof(struct dw_hdmi));
2349f5e7d251SAlgea Cao if (!hdmi)
2350f5e7d251SAlgea Cao return -ENOMEM;
2351f5e7d251SAlgea Cao
2352f5e7d251SAlgea Cao memset(hdmi, 0, sizeof(struct dw_hdmi));
2353f5e7d251SAlgea Cao mode_buf = malloc(MODE_LEN * sizeof(struct drm_display_mode));
2354f5e7d251SAlgea Cao if (!mode_buf)
2355f5e7d251SAlgea Cao return -ENOMEM;
23566a3f4548SSandy Huang
23576a3f4548SSandy Huang #ifdef CONFIG_SPL_BUILD
23586a3f4548SSandy Huang hdmi->id = 0;
23596a3f4548SSandy Huang hdmi->regs = (void *)RK3528_HDMI_BASE;
23606a3f4548SSandy Huang hdmi->io_width = 4;
23616a3f4548SSandy Huang hdmi->scramble_low_rates = false;
23626a3f4548SSandy Huang hdmi->hdcp1x_enable = false;
23636a3f4548SSandy Huang hdmi->output_bus_format_rgb = false;
23646a3f4548SSandy Huang conn_state->type = DRM_MODE_CONNECTOR_HDMIA;
23656a3f4548SSandy Huang #else
2366cb17ca6cSSandy Huang hdmi->id = of_alias_get_id(ofnode_to_np(hdmi_node), "hdmi");
2367cb17ca6cSSandy Huang if (hdmi->id < 0)
2368cb17ca6cSSandy Huang hdmi->id = 0;
2369cb17ca6cSSandy Huang conn_state->disp_info = rockchip_get_disp_info(conn_state->type, hdmi->id);
23706a3f4548SSandy Huang #endif
23718e2bab3fSAlgea Cao
2372f5e7d251SAlgea Cao memset(mode_buf, 0, MODE_LEN * sizeof(struct drm_display_mode));
2373f5e7d251SAlgea Cao
23747ff748e1SAlgea Cao hdmi->dev_type = pdata->dev_type;
23757ff748e1SAlgea Cao hdmi->plat_data = pdata;
23767ff748e1SAlgea Cao
23776a3f4548SSandy Huang #ifndef CONFIG_SPL_BUILD
23780594ce39SZhang Yubing hdmi->regs = dev_read_addr_ptr(conn->dev);
2379e2bce6e4SKever Yang hdmi->io_width = ofnode_read_s32_default(hdmi_node, "reg-io-width", -1);
23808e2bab3fSAlgea Cao
23818e2bab3fSAlgea Cao if (ofnode_read_bool(hdmi_node, "scramble-low-rates"))
23828e2bab3fSAlgea Cao hdmi->scramble_low_rates = true;
23838e2bab3fSAlgea Cao
23848e2bab3fSAlgea Cao if (ofnode_read_bool(hdmi_node, "hdcp1x-enable"))
23858e2bab3fSAlgea Cao hdmi->hdcp1x_enable = true;
23868e2bab3fSAlgea Cao else
23878e2bab3fSAlgea Cao hdmi->hdcp1x_enable = false;
23888e2bab3fSAlgea Cao
238991e56900SLei Chen if (ofnode_read_bool(hdmi_node, "force_output_bus_format_RGB") ||
239091e56900SLei Chen ofnode_read_bool(hdmi_node, "unsupported-yuv-input"))
239191e56900SLei Chen hdmi->output_bus_format_rgb = true;
239291e56900SLei Chen else
239391e56900SLei Chen hdmi->output_bus_format_rgb = false;
239491e56900SLei Chen
2395d5869560SAlgea Cao ret = dev_read_size(conn->dev, "rockchip,phy-table");
2396d5869560SAlgea Cao if (ret > 0 && hdmi->plat_data->phy_config) {
2397d5869560SAlgea Cao u32 phy_config[ret / 4];
23987ff748e1SAlgea Cao int i;
23997ff748e1SAlgea Cao
2400d5869560SAlgea Cao dev_read_u32_array(conn->dev, "rockchip,phy-table", phy_config, ret / 4);
24017ff748e1SAlgea Cao
2402d5869560SAlgea Cao for (i = 0; i < ret / 16; i++) {
24037ff748e1SAlgea Cao if (phy_config[i * 4] != 0)
24047ff748e1SAlgea Cao hdmi->plat_data->phy_config[i].mpixelclock = (u64)phy_config[i * 4];
24057ff748e1SAlgea Cao else
24067ff748e1SAlgea Cao hdmi->plat_data->phy_config[i].mpixelclock = ~0UL;
24077ff748e1SAlgea Cao hdmi->plat_data->phy_config[i].sym_ctr = (u16)phy_config[i * 4 + 1];
24087ff748e1SAlgea Cao hdmi->plat_data->phy_config[i].term = (u16)phy_config[i * 4 + 2];
24097ff748e1SAlgea Cao hdmi->plat_data->phy_config[i].vlev_ctr = (u16)phy_config[i * 4 + 3];
24107ff748e1SAlgea Cao }
24117ff748e1SAlgea Cao }
24127ff748e1SAlgea Cao
24131f71919fSShunqing Chen ddc_node = of_parse_phandle(ofnode_to_np(hdmi_node), "ddc-i2c-bus", 0);
24141f71919fSShunqing Chen if (ddc_node) {
24151f71919fSShunqing Chen uclass_get_device_by_ofnode(UCLASS_I2C, np_to_ofnode(ddc_node),
24161f71919fSShunqing Chen &hdmi->adap.i2c_bus);
24171f71919fSShunqing Chen if (hdmi->adap.i2c_bus)
24181f71919fSShunqing Chen hdmi->adap.ops = i2c_get_ops(hdmi->adap.i2c_bus);
24191f71919fSShunqing Chen }
24206a3f4548SSandy Huang #endif
24211f71919fSShunqing Chen
2422f5e7d251SAlgea Cao hdmi->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
2423f5e7d251SAlgea Cao if (hdmi->grf <= 0) {
2424f5e7d251SAlgea Cao printf("%s: Get syscon grf failed (ret=%p)\n",
2425f5e7d251SAlgea Cao __func__, hdmi->grf);
2426f5e7d251SAlgea Cao return -ENXIO;
2427f5e7d251SAlgea Cao }
2428f5e7d251SAlgea Cao
24296a3f4548SSandy Huang #ifdef CONFIG_SPL_BUILD
24306a3f4548SSandy Huang hdmi->gpio_base = (void *)RK3528_GPIO_BASE;
24316a3f4548SSandy Huang #else
2432cb24dc0eSAlgea Cao ret = gpio_request_by_name(conn->dev, "hpd-gpios", 0,
2433cb24dc0eSAlgea Cao &hdmi->hpd_gpiod, GPIOD_IS_IN);
2434cb24dc0eSAlgea Cao if (ret && ret != -ENOENT) {
2435cb24dc0eSAlgea Cao printf("%s: Cannot get HPD GPIO: %d\n", __func__, ret);
2436cb24dc0eSAlgea Cao return ret;
2437cb24dc0eSAlgea Cao }
24386a3f4548SSandy Huang hdmi->gpio_base = (void *)dev_read_addr_index(conn->dev, 1);
24396a3f4548SSandy Huang #endif
24406a3f4548SSandy Huang if (!hdmi->gpio_base)
24416a3f4548SSandy Huang return -ENODEV;
2442cb24dc0eSAlgea Cao
2443f5e7d251SAlgea Cao dw_hdmi_set_reg_wr(hdmi);
2444f5e7d251SAlgea Cao
24458e2bab3fSAlgea Cao if (pdata->grf_vop_sel_reg) {
2446f5e7d251SAlgea Cao if (crtc_state->crtc_id)
2447f5e7d251SAlgea Cao val = ((1 << pdata->vop_sel_bit) |
2448f5e7d251SAlgea Cao (1 << (16 + pdata->vop_sel_bit)));
2449f5e7d251SAlgea Cao else
2450f5e7d251SAlgea Cao val = ((0 << pdata->vop_sel_bit) |
2451f5e7d251SAlgea Cao (1 << (16 + pdata->vop_sel_bit)));
2452f5e7d251SAlgea Cao writel(val, hdmi->grf + pdata->grf_vop_sel_reg);
24538e2bab3fSAlgea Cao }
2454f5e7d251SAlgea Cao
24558e2bab3fSAlgea Cao hdmi->i2c = malloc(sizeof(struct dw_hdmi_i2c));
24568e2bab3fSAlgea Cao if (!hdmi->i2c)
24578e2bab3fSAlgea Cao return -ENOMEM;
24588e2bab3fSAlgea Cao hdmi->adap.ddc_xfer = dw_hdmi_i2c_xfer;
24598e2bab3fSAlgea Cao
24608e2bab3fSAlgea Cao /*
24618e2bab3fSAlgea Cao * Read high and low time from device tree. If not available use
24628e2bab3fSAlgea Cao * the default timing scl clock rate is about 99.6KHz.
24638e2bab3fSAlgea Cao */
24646a3f4548SSandy Huang #ifdef CONFIG_SPL_BUILD
24656a3f4548SSandy Huang hdmi->i2c->scl_high_ns = 9625;
24666a3f4548SSandy Huang hdmi->i2c->scl_low_ns = 10000;
24676a3f4548SSandy Huang #else
24688e2bab3fSAlgea Cao hdmi->i2c->scl_high_ns =
24698e2bab3fSAlgea Cao ofnode_read_s32_default(hdmi_node,
247019c2faf2SAlgea Cao "ddc-i2c-scl-high-time-ns", 4708);
24718e2bab3fSAlgea Cao hdmi->i2c->scl_low_ns =
24728e2bab3fSAlgea Cao ofnode_read_s32_default(hdmi_node,
247319c2faf2SAlgea Cao "ddc-i2c-scl-low-time-ns", 4916);
24746a3f4548SSandy Huang #endif
24758e2bab3fSAlgea Cao
24768e2bab3fSAlgea Cao dw_hdmi_i2c_init(hdmi);
24775ccad8f6SAlgea Cao conn_state->output_if |= VOP_OUTPUT_IF_HDMI0;
2478f5e7d251SAlgea Cao conn_state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
2479f5e7d251SAlgea Cao
2480f5e7d251SAlgea Cao hdmi->edid_data.mode_buf = mode_buf;
2481f5e7d251SAlgea Cao hdmi->sample_rate = 48000;
2482f5e7d251SAlgea Cao
24830594ce39SZhang Yubing conn->data = hdmi;
2484cb24dc0eSAlgea Cao dw_hdmi_set_iomux(hdmi->grf, hdmi->gpio_base,
2485cb24dc0eSAlgea Cao &hdmi->hpd_gpiod, hdmi->dev_type);
2486f5e7d251SAlgea Cao dw_hdmi_detect_phy(hdmi);
2487f5e7d251SAlgea Cao dw_hdmi_dev_init(hdmi);
2488f5e7d251SAlgea Cao
2489f5e7d251SAlgea Cao return 0;
2490f5e7d251SAlgea Cao }
2491f5e7d251SAlgea Cao
rockchip_dw_hdmi_deinit(struct rockchip_connector * conn,struct display_state * state)24920594ce39SZhang Yubing void rockchip_dw_hdmi_deinit(struct rockchip_connector *conn, struct display_state *state)
2493f5e7d251SAlgea Cao {
24940594ce39SZhang Yubing struct dw_hdmi *hdmi = conn->data;
2495f5e7d251SAlgea Cao
24968e2bab3fSAlgea Cao if (hdmi->i2c)
24978e2bab3fSAlgea Cao free(hdmi->i2c);
2498f5e7d251SAlgea Cao if (hdmi->edid_data.mode_buf)
2499f5e7d251SAlgea Cao free(hdmi->edid_data.mode_buf);
2500f5e7d251SAlgea Cao if (hdmi)
2501f5e7d251SAlgea Cao free(hdmi);
2502f5e7d251SAlgea Cao }
2503f5e7d251SAlgea Cao
rockchip_dw_hdmi_config_output(struct rockchip_connector * conn,struct display_state * state)2504c3a20b0eSChen Shunqing static void rockchip_dw_hdmi_config_output(struct rockchip_connector *conn,
2505c3a20b0eSChen Shunqing struct display_state *state)
2506f5e7d251SAlgea Cao {
2507f5e7d251SAlgea Cao struct connector_state *conn_state = &state->conn_state;
2508f5e7d251SAlgea Cao struct drm_display_mode *mode = &conn_state->mode;
25090594ce39SZhang Yubing struct dw_hdmi *hdmi = conn->data;
25108e2bab3fSAlgea Cao unsigned int bus_format;
25115ccad8f6SAlgea Cao unsigned long enc_out_encoding;
25128e2bab3fSAlgea Cao struct overscan *overscan = &conn_state->overscan;
2513f5e7d251SAlgea Cao
25145ccb1b20SAlgea Cao drm_rk_selete_output(&hdmi->edid_data, conn_state, &bus_format,
251591e56900SLei Chen overscan, hdmi->dev_type, hdmi->output_bus_format_rgb);
25168e2bab3fSAlgea Cao
2517f5e7d251SAlgea Cao *mode = *hdmi->edid_data.preferred_mode;
2518f5e7d251SAlgea Cao hdmi->vic = drm_match_cea_mode(mode);
2519f5e7d251SAlgea Cao
2520b429d5abSAlgea Cao if (state->force_output) {
2521b429d5abSAlgea Cao hdmi->force_output = state->force_output;
2522b429d5abSAlgea Cao hdmi->sink_is_hdmi = true;
2523b429d5abSAlgea Cao hdmi->sink_has_audio = true;
25242bfb6166SSandy Huang bus_format = state->force_bus_format;
2525b429d5abSAlgea Cao }
25268e2bab3fSAlgea Cao conn_state->bus_format = bus_format;
25278e2bab3fSAlgea Cao hdmi->hdmi_data.enc_in_bus_format = bus_format;
25288e2bab3fSAlgea Cao hdmi->hdmi_data.enc_out_bus_format = bus_format;
2529f5e7d251SAlgea Cao
25308e2bab3fSAlgea Cao switch (bus_format) {
25318e2bab3fSAlgea Cao case MEDIA_BUS_FMT_UYVY10_1X20:
25328e2bab3fSAlgea Cao conn_state->bus_format = MEDIA_BUS_FMT_YUV10_1X30;
25338e2bab3fSAlgea Cao hdmi->hdmi_data.enc_in_bus_format =
25348e2bab3fSAlgea Cao MEDIA_BUS_FMT_YUV10_1X30;
25358e2bab3fSAlgea Cao break;
25368e2bab3fSAlgea Cao case MEDIA_BUS_FMT_UYVY8_1X16:
25378e2bab3fSAlgea Cao conn_state->bus_format = MEDIA_BUS_FMT_YUV8_1X24;
25388e2bab3fSAlgea Cao hdmi->hdmi_data.enc_in_bus_format =
25398e2bab3fSAlgea Cao MEDIA_BUS_FMT_YUV8_1X24;
25408e2bab3fSAlgea Cao break;
25418e2bab3fSAlgea Cao case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
25428e2bab3fSAlgea Cao case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
25438e2bab3fSAlgea Cao conn_state->output_mode = ROCKCHIP_OUT_MODE_YUV420;
25448e2bab3fSAlgea Cao break;
25458e2bab3fSAlgea Cao }
2546f5e7d251SAlgea Cao
25475ccad8f6SAlgea Cao if (hdmi->vic == 6 || hdmi->vic == 7 || hdmi->vic == 21 ||
25485ccad8f6SAlgea Cao hdmi->vic == 22 || hdmi->vic == 2 || hdmi->vic == 3 ||
25495ccad8f6SAlgea Cao hdmi->vic == 17 || hdmi->vic == 18)
25505ccad8f6SAlgea Cao enc_out_encoding = V4L2_YCBCR_ENC_601;
25515ccad8f6SAlgea Cao else
25525ccad8f6SAlgea Cao enc_out_encoding = V4L2_YCBCR_ENC_709;
25535ccad8f6SAlgea Cao
25545ccad8f6SAlgea Cao if (enc_out_encoding == V4L2_YCBCR_ENC_BT2020)
2555df0a5c43SDamon Ding conn_state->color_encoding = DRM_COLOR_YCBCR_BT2020;
25565ccad8f6SAlgea Cao else if (bus_format == MEDIA_BUS_FMT_RGB888_1X24 ||
25575ccad8f6SAlgea Cao bus_format == MEDIA_BUS_FMT_RGB101010_1X30)
2558df0a5c43SDamon Ding conn_state->color_encoding = DRM_COLOR_YCBCR_BT709;
25595ccad8f6SAlgea Cao else if (enc_out_encoding == V4L2_YCBCR_ENC_709)
2560df0a5c43SDamon Ding conn_state->color_encoding = DRM_COLOR_YCBCR_BT709;
25615ccad8f6SAlgea Cao else
2562df0a5c43SDamon Ding conn_state->color_encoding = DRM_COLOR_YCBCR_BT601;
2563df0a5c43SDamon Ding
2564df0a5c43SDamon Ding if (bus_format == MEDIA_BUS_FMT_RGB888_1X24 ||
2565df0a5c43SDamon Ding bus_format == MEDIA_BUS_FMT_RGB101010_1X30)
2566df0a5c43SDamon Ding conn_state->color_range = hdmi->hdmi_data.quant_range ==
2567df0a5c43SDamon Ding HDMI_QUANTIZATION_RANGE_LIMITED ?
2568df0a5c43SDamon Ding DRM_COLOR_YCBCR_LIMITED_RANGE :
2569df0a5c43SDamon Ding DRM_COLOR_YCBCR_FULL_RANGE;
2570df0a5c43SDamon Ding else
2571df0a5c43SDamon Ding conn_state->color_range = hdmi->hdmi_data.quant_range ==
2572df0a5c43SDamon Ding HDMI_QUANTIZATION_RANGE_FULL ?
2573df0a5c43SDamon Ding DRM_COLOR_YCBCR_FULL_RANGE :
2574df0a5c43SDamon Ding DRM_COLOR_YCBCR_LIMITED_RANGE;
2575c3a20b0eSChen Shunqing }
2576c3a20b0eSChen Shunqing
rockchip_dw_hdmi_prepare(struct rockchip_connector * conn,struct display_state * state)2577c3a20b0eSChen Shunqing int rockchip_dw_hdmi_prepare(struct rockchip_connector *conn, struct display_state *state)
2578c3a20b0eSChen Shunqing {
2579c3a20b0eSChen Shunqing struct connector_state *conn_state = &state->conn_state;
2580c3a20b0eSChen Shunqing struct drm_display_mode *mode = &conn_state->mode;
2581c3a20b0eSChen Shunqing struct dw_hdmi *hdmi = conn->data;
2582c3a20b0eSChen Shunqing
2583c3a20b0eSChen Shunqing if (!hdmi->edid_data.preferred_mode && conn->bridge) {
2584c3a20b0eSChen Shunqing drm_add_hdmi_modes(&hdmi->edid_data, mode);
2585c3a20b0eSChen Shunqing drm_mode_sort(&hdmi->edid_data);
2586c3a20b0eSChen Shunqing hdmi->sink_is_hdmi = true;
2587c3a20b0eSChen Shunqing hdmi->sink_has_audio = true;
2588c3a20b0eSChen Shunqing rockchip_dw_hdmi_config_output(conn, state);
2589c3a20b0eSChen Shunqing }
2590c3a20b0eSChen Shunqing
2591c3a20b0eSChen Shunqing return 0;
2592c3a20b0eSChen Shunqing }
2593c3a20b0eSChen Shunqing
rockchip_dw_hdmi_enable(struct rockchip_connector * conn,struct display_state * state)2594c3a20b0eSChen Shunqing int rockchip_dw_hdmi_enable(struct rockchip_connector *conn, struct display_state *state)
2595c3a20b0eSChen Shunqing {
2596c3a20b0eSChen Shunqing struct connector_state *conn_state = &state->conn_state;
2597c3a20b0eSChen Shunqing struct drm_display_mode *mode = &conn_state->mode;
2598c3a20b0eSChen Shunqing struct dw_hdmi *hdmi = conn->data;
2599c3a20b0eSChen Shunqing
2600c3a20b0eSChen Shunqing if (!hdmi)
2601c3a20b0eSChen Shunqing return -EFAULT;
2602c3a20b0eSChen Shunqing
2603c3a20b0eSChen Shunqing /* Store the display mode for plugin/DKMS poweron events */
2604c3a20b0eSChen Shunqing memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
2605c3a20b0eSChen Shunqing
2606c3a20b0eSChen Shunqing dw_hdmi_setup(hdmi, conn, mode, state);
2607c3a20b0eSChen Shunqing
2608c3a20b0eSChen Shunqing return 0;
2609c3a20b0eSChen Shunqing }
2610c3a20b0eSChen Shunqing
rockchip_dw_hdmi_disable(struct rockchip_connector * conn,struct display_state * state)2611c3a20b0eSChen Shunqing int rockchip_dw_hdmi_disable(struct rockchip_connector *conn, struct display_state *state)
2612c3a20b0eSChen Shunqing {
2613c3a20b0eSChen Shunqing struct dw_hdmi *hdmi = conn->data;
2614c3a20b0eSChen Shunqing
2615c3a20b0eSChen Shunqing dw_hdmi_disable(conn, hdmi, state);
2616c3a20b0eSChen Shunqing return 0;
2617c3a20b0eSChen Shunqing }
2618c3a20b0eSChen Shunqing
rockchip_dw_hdmi_mode_valid(struct dw_hdmi * hdmi)2619c3a20b0eSChen Shunqing static void rockchip_dw_hdmi_mode_valid(struct dw_hdmi *hdmi)
2620c3a20b0eSChen Shunqing {
2621c3a20b0eSChen Shunqing struct hdmi_edid_data *edid_data = &hdmi->edid_data;
2622c3a20b0eSChen Shunqing int i;
2623c3a20b0eSChen Shunqing
2624c3a20b0eSChen Shunqing for (i = 0; i < edid_data->modes; i++) {
2625c3a20b0eSChen Shunqing if (edid_data->mode_buf[i].invalid)
2626c3a20b0eSChen Shunqing continue;
2627c3a20b0eSChen Shunqing
2628c3a20b0eSChen Shunqing if (edid_data->mode_buf[i].clock > 600000)
2629c3a20b0eSChen Shunqing edid_data->mode_buf[i].invalid = true;
2630c3a20b0eSChen Shunqing }
2631c3a20b0eSChen Shunqing }
2632c3a20b0eSChen Shunqing
rockchip_dw_hdmi_get_timing(struct rockchip_connector * conn,struct display_state * state)2633c3a20b0eSChen Shunqing int rockchip_dw_hdmi_get_timing(struct rockchip_connector *conn, struct display_state *state)
2634c3a20b0eSChen Shunqing {
2635*9c170041SAlgea Cao int ret = 0, i, vic;
2636c3a20b0eSChen Shunqing struct connector_state *conn_state = &state->conn_state;
2637c3a20b0eSChen Shunqing struct dw_hdmi *hdmi = conn->data;
2638c3a20b0eSChen Shunqing struct edid *edid = (struct edid *)conn_state->edid;
2639c3a20b0eSChen Shunqing const u8 def_modes_vic[6] = {4, 16, 2, 17, 31, 19};
2640c3a20b0eSChen Shunqing
2641c3a20b0eSChen Shunqing if (!hdmi)
2642c3a20b0eSChen Shunqing return -EFAULT;
2643c3a20b0eSChen Shunqing
2644*9c170041SAlgea Cao conn_state->edid = drm_do_get_edid(&hdmi->adap);
2645c3a20b0eSChen Shunqing
2646*9c170041SAlgea Cao if (conn_state->edid) {
2647c3a20b0eSChen Shunqing hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
2648c3a20b0eSChen Shunqing if (hdmi->sink_has_audio)
2649c3a20b0eSChen Shunqing hdmi->sink_is_hdmi = true;
2650c3a20b0eSChen Shunqing else
2651c3a20b0eSChen Shunqing hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
2652c3a20b0eSChen Shunqing
2653c3a20b0eSChen Shunqing ret = drm_add_edid_modes(&hdmi->edid_data, conn_state->edid);
2654c3a20b0eSChen Shunqing }
2655*9c170041SAlgea Cao if (ret <= 0) {
2656c3a20b0eSChen Shunqing hdmi->sink_is_hdmi = true;
2657c3a20b0eSChen Shunqing hdmi->sink_has_audio = true;
2658c3a20b0eSChen Shunqing do_cea_modes(&hdmi->edid_data, def_modes_vic,
2659c3a20b0eSChen Shunqing sizeof(def_modes_vic));
2660c3a20b0eSChen Shunqing hdmi->edid_data.mode_buf[0].type |= DRM_MODE_TYPE_PREFERRED;
2661c3a20b0eSChen Shunqing hdmi->edid_data.preferred_mode = &hdmi->edid_data.mode_buf[0];
2662c3a20b0eSChen Shunqing printf("failed to get edid\n");
2663c3a20b0eSChen Shunqing }
2664c3a20b0eSChen Shunqing #ifdef CONFIG_SPL_BUILD
2665c3a20b0eSChen Shunqing conn_state->disp_info = rockchip_get_disp_info(conn_state->type, hdmi->id);
2666c3a20b0eSChen Shunqing #endif
2667c3a20b0eSChen Shunqing drm_rk_filter_whitelist(&hdmi->edid_data);
2668c3a20b0eSChen Shunqing rockchip_dw_hdmi_mode_valid(hdmi);
2669c3a20b0eSChen Shunqing if (hdmi->phy.ops->mode_valid)
2670c3a20b0eSChen Shunqing hdmi->phy.ops->mode_valid(conn, hdmi, state);
2671c3a20b0eSChen Shunqing drm_mode_max_resolution_filter(&hdmi->edid_data,
2672c3a20b0eSChen Shunqing &state->crtc_state.max_output);
2673c3a20b0eSChen Shunqing if (!drm_mode_prune_invalid(&hdmi->edid_data)) {
2674c3a20b0eSChen Shunqing printf("can't find valid hdmi mode\n");
2675c3a20b0eSChen Shunqing return -EINVAL;
2676c3a20b0eSChen Shunqing }
2677c3a20b0eSChen Shunqing
2678c3a20b0eSChen Shunqing for (i = 0; i < hdmi->edid_data.modes; i++) {
2679c3a20b0eSChen Shunqing hdmi->edid_data.mode_buf[i].vrefresh =
2680c3a20b0eSChen Shunqing drm_mode_vrefresh(&hdmi->edid_data.mode_buf[i]);
2681c3a20b0eSChen Shunqing
2682c3a20b0eSChen Shunqing vic = drm_match_cea_mode(&hdmi->edid_data.mode_buf[i]);
2683c3a20b0eSChen Shunqing if (hdmi->edid_data.mode_buf[i].picture_aspect_ratio == HDMI_PICTURE_ASPECT_NONE) {
2684c3a20b0eSChen Shunqing if (vic >= 93 && vic <= 95)
2685c3a20b0eSChen Shunqing hdmi->edid_data.mode_buf[i].picture_aspect_ratio =
2686c3a20b0eSChen Shunqing HDMI_PICTURE_ASPECT_16_9;
2687c3a20b0eSChen Shunqing else if (vic == 98)
2688c3a20b0eSChen Shunqing hdmi->edid_data.mode_buf[i].picture_aspect_ratio =
2689c3a20b0eSChen Shunqing HDMI_PICTURE_ASPECT_256_135;
2690c3a20b0eSChen Shunqing }
2691c3a20b0eSChen Shunqing }
2692c3a20b0eSChen Shunqing
2693c3a20b0eSChen Shunqing drm_mode_sort(&hdmi->edid_data);
2694c3a20b0eSChen Shunqing rockchip_dw_hdmi_config_output(conn, state);
26955ccad8f6SAlgea Cao
2696f5e7d251SAlgea Cao return 0;
2697f5e7d251SAlgea Cao }
2698f5e7d251SAlgea Cao
rockchip_dw_hdmi_detect(struct rockchip_connector * conn,struct display_state * state)26990594ce39SZhang Yubing int rockchip_dw_hdmi_detect(struct rockchip_connector *conn, struct display_state *state)
2700f5e7d251SAlgea Cao {
2701f5e7d251SAlgea Cao int ret;
27020594ce39SZhang Yubing struct dw_hdmi *hdmi = conn->data;
2703f5e7d251SAlgea Cao
2704f5e7d251SAlgea Cao if (!hdmi)
2705f5e7d251SAlgea Cao return -EFAULT;
2706f5e7d251SAlgea Cao
27078e2bab3fSAlgea Cao ret = dw_hdmi_detect_hotplug(hdmi, state);
2708f5e7d251SAlgea Cao
2709f5e7d251SAlgea Cao return ret;
2710f5e7d251SAlgea Cao }
2711f5e7d251SAlgea Cao
rockchip_dw_hdmi_get_edid(struct rockchip_connector * conn,struct display_state * state)27120594ce39SZhang Yubing int rockchip_dw_hdmi_get_edid(struct rockchip_connector *conn, struct display_state *state)
2713f5e7d251SAlgea Cao {
2714*9c170041SAlgea Cao int ret = 0;
2715f5e7d251SAlgea Cao struct connector_state *conn_state = &state->conn_state;
27160594ce39SZhang Yubing struct dw_hdmi *hdmi = conn->data;
2717f5e7d251SAlgea Cao
2718*9c170041SAlgea Cao conn_state->edid = drm_do_get_edid(&hdmi->adap);
2719*9c170041SAlgea Cao if (!conn_state->edid)
2720*9c170041SAlgea Cao ret = -EINVAL;
2721f5e7d251SAlgea Cao
2722f5e7d251SAlgea Cao return ret;
2723f5e7d251SAlgea Cao }
2724f5e7d251SAlgea Cao
inno_dw_hdmi_phy_init(struct rockchip_connector * conn,struct dw_hdmi * hdmi,void * data)27250594ce39SZhang Yubing int inno_dw_hdmi_phy_init(struct rockchip_connector *conn, struct dw_hdmi *hdmi, void *data)
27268e2bab3fSAlgea Cao {
27278e2bab3fSAlgea Cao struct display_state *state = (struct display_state *)data;
27288e2bab3fSAlgea Cao struct connector_state *conn_state = &state->conn_state;
27298e2bab3fSAlgea Cao u32 color_depth, bus_width;
27308e2bab3fSAlgea Cao
27318e2bab3fSAlgea Cao color_depth =
27328e2bab3fSAlgea Cao hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
27338e2bab3fSAlgea Cao
27348e2bab3fSAlgea Cao if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
27358e2bab3fSAlgea Cao bus_width = color_depth / 2;
27368e2bab3fSAlgea Cao else if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
27378e2bab3fSAlgea Cao bus_width = color_depth;
27388e2bab3fSAlgea Cao else
27398e2bab3fSAlgea Cao bus_width = 8;
27400594ce39SZhang Yubing rockchip_phy_set_bus_width(conn->phy, bus_width);
27410594ce39SZhang Yubing rockchip_phy_set_pll(conn->phy,
274215081c50SWyon Bi conn_state->mode.crtc_clock * 1000);
27438e2bab3fSAlgea Cao if (hdmi->edid_data.display_info.hdmi.scdc.supported)
27448e2bab3fSAlgea Cao rockchip_dw_hdmi_scdc_set_tmds_rate(hdmi);
27450594ce39SZhang Yubing rockchip_phy_power_on(conn->phy);
27468e2bab3fSAlgea Cao
27478e2bab3fSAlgea Cao return 0;
27488e2bab3fSAlgea Cao }
27498e2bab3fSAlgea Cao
inno_dw_hdmi_phy_disable(struct rockchip_connector * conn,struct dw_hdmi * hdmi,void * data)27500594ce39SZhang Yubing void inno_dw_hdmi_phy_disable(struct rockchip_connector *conn, struct dw_hdmi *hdmi, void *data)
27518e2bab3fSAlgea Cao {
27528e2bab3fSAlgea Cao }
27538e2bab3fSAlgea Cao
27548e2bab3fSAlgea Cao enum drm_connector_status
inno_dw_hdmi_phy_read_hpd(struct dw_hdmi * hdmi,void * data)27558e2bab3fSAlgea Cao inno_dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, void *data)
27568e2bab3fSAlgea Cao {
27578e2bab3fSAlgea Cao enum drm_connector_status status;
27588e2bab3fSAlgea Cao struct display_state *state = (struct display_state *)data;
27598e2bab3fSAlgea Cao
27608e2bab3fSAlgea Cao status = dw_hdmi_phy_read_hpd(hdmi, state);
27618e2bab3fSAlgea Cao
27628e2bab3fSAlgea Cao if (hdmi->dev_type == RK3328_HDMI) {
27638e2bab3fSAlgea Cao if (status == connector_status_connected)
27648e2bab3fSAlgea Cao inno_dw_hdmi_set_domain(hdmi->grf, 1);
27658e2bab3fSAlgea Cao else
27668e2bab3fSAlgea Cao inno_dw_hdmi_set_domain(hdmi->grf, 0);
27678e2bab3fSAlgea Cao }
27688e2bab3fSAlgea Cao
27698e2bab3fSAlgea Cao return status;
27708e2bab3fSAlgea Cao }
27718e2bab3fSAlgea Cao
inno_dw_hdmi_mode_valid(struct rockchip_connector * conn,struct dw_hdmi * hdmi,void * data)27720594ce39SZhang Yubing void inno_dw_hdmi_mode_valid(struct rockchip_connector *conn, struct dw_hdmi *hdmi, void *data)
27738e2bab3fSAlgea Cao {
27748e2bab3fSAlgea Cao struct hdmi_edid_data *edid_data = &hdmi->edid_data;
27758e2bab3fSAlgea Cao unsigned long rate;
27768e2bab3fSAlgea Cao int i, ret;
27778e2bab3fSAlgea Cao struct drm_display_mode *mode_buf = edid_data->mode_buf;
27788e2bab3fSAlgea Cao
27798e2bab3fSAlgea Cao for (i = 0; i < edid_data->modes; i++) {
27808e2bab3fSAlgea Cao if (edid_data->mode_buf[i].invalid)
27818e2bab3fSAlgea Cao continue;
27828e2bab3fSAlgea Cao if (edid_data->mode_buf[i].flags & DRM_MODE_FLAG_DBLCLK)
27838e2bab3fSAlgea Cao rate = mode_buf[i].clock * 1000 * 2;
27848e2bab3fSAlgea Cao else
27858e2bab3fSAlgea Cao rate = mode_buf[i].clock * 1000;
27868e2bab3fSAlgea Cao
27878e2bab3fSAlgea Cao /* Check whether mode is out of phy cfg range. */
27880594ce39SZhang Yubing ret = rockchip_phy_round_rate(conn->phy, rate);
27898e2bab3fSAlgea Cao
27908e2bab3fSAlgea Cao if (ret < 0)
27918e2bab3fSAlgea Cao edid_data->mode_buf[i].invalid = true;
27928e2bab3fSAlgea Cao }
27938e2bab3fSAlgea Cao }
2794