xref: /rk3399_rockchip-uboot/drivers/video/drm/dw_hdmi_qp.c (revision 9c170041dcf97ed3fe5bbd76e70cda2ed3fcbd92)
128671edaSAlgea Cao // SPDX-License-Identifier: GPL-2.0
228671edaSAlgea Cao /*
328671edaSAlgea Cao  * Copyright (c) 2021 Fuzhou Rockchip Electronics Co., Ltd
428671edaSAlgea Cao  * Author: Algea Cao <algea.cao@rock-chips.com>
528671edaSAlgea Cao  */
628671edaSAlgea Cao 
728671edaSAlgea Cao #include <common.h>
828671edaSAlgea Cao #include <malloc.h>
928671edaSAlgea Cao #include <syscon.h>
1028671edaSAlgea Cao #include <asm/arch-rockchip/clock.h>
1128671edaSAlgea Cao #include <asm/arch/vendor.h>
1228671edaSAlgea Cao #include <edid.h>
1328671edaSAlgea Cao #include <dm/device.h>
1428671edaSAlgea Cao #include <dm/of_access.h>
1528671edaSAlgea Cao #include <dm/ofnode.h>
1628671edaSAlgea Cao #include <dm/read.h>
1728671edaSAlgea Cao #include <linux/hdmi.h>
1828671edaSAlgea Cao #include <linux/media-bus-format.h>
1928671edaSAlgea Cao #include <linux/dw_hdmi.h>
2028671edaSAlgea Cao #include <asm/io.h>
21af006bcbSChen Shunqing #include "rockchip_bridge.h"
2228671edaSAlgea Cao #include "rockchip_display.h"
2328671edaSAlgea Cao #include "rockchip_crtc.h"
2428671edaSAlgea Cao #include "rockchip_connector.h"
2528671edaSAlgea Cao #include "dw_hdmi_qp.h"
2628671edaSAlgea Cao #include "rockchip_phy.h"
2728671edaSAlgea Cao 
2828671edaSAlgea Cao enum frl_mask {
2928671edaSAlgea Cao 	FRL_3GBPS_3LANE = 1,
3028671edaSAlgea Cao 	FRL_6GBPS_3LANE,
3128671edaSAlgea Cao 	FRL_6GBPS_4LANE,
3228671edaSAlgea Cao 	FRL_8GBPS_4LANE,
3328671edaSAlgea Cao 	FRL_10GBPS_4LANE,
3428671edaSAlgea Cao 	FRL_12GBPS_4LANE,
3528671edaSAlgea Cao };
3628671edaSAlgea Cao 
3728671edaSAlgea Cao #define DDC_CI_ADDR		0x37
3828671edaSAlgea Cao #define DDC_SEGMENT_ADDR	0x30
3928671edaSAlgea Cao 
4028671edaSAlgea Cao #define HDMI_EDID_LEN		512
41eec52208SAlgea Cao #define HDMI_EDID_BLOCK_LEN	128
4228671edaSAlgea Cao 
4328671edaSAlgea Cao /* DW-HDMI Controller >= 0x200a are at least compliant with SCDC version 1 */
4428671edaSAlgea Cao #define SCDC_MIN_SOURCE_VERSION	0x1
4528671edaSAlgea Cao 
4628671edaSAlgea Cao #define HDMI14_MAX_TMDSCLK	340000000
4728671edaSAlgea Cao 
4828671edaSAlgea Cao struct hdmi_vmode {
4928671edaSAlgea Cao 	bool mdataenablepolarity;
5028671edaSAlgea Cao 
5128671edaSAlgea Cao 	unsigned int mpixelclock;
5228671edaSAlgea Cao 	unsigned int mpixelrepetitioninput;
5328671edaSAlgea Cao 	unsigned int mpixelrepetitionoutput;
5428671edaSAlgea Cao 	unsigned int mtmdsclock;
5528671edaSAlgea Cao };
5628671edaSAlgea Cao 
5728671edaSAlgea Cao struct hdmi_data_info {
5828671edaSAlgea Cao 	unsigned int enc_in_bus_format;
5928671edaSAlgea Cao 	unsigned int enc_out_bus_format;
6028671edaSAlgea Cao 	unsigned int enc_in_encoding;
6128671edaSAlgea Cao 	unsigned int enc_out_encoding;
6228671edaSAlgea Cao 	unsigned int quant_range;
6328671edaSAlgea Cao 	unsigned int pix_repet_factor;
6428671edaSAlgea Cao 	struct hdmi_vmode video_mode;
6528671edaSAlgea Cao };
6628671edaSAlgea Cao 
6728671edaSAlgea Cao struct dw_hdmi_phy_data {
6828671edaSAlgea Cao 	enum dw_hdmi_phy_type type;
6928671edaSAlgea Cao 	const char *name;
7028671edaSAlgea Cao 	unsigned int gen;
7128671edaSAlgea Cao 	bool has_svsret;
7228671edaSAlgea Cao 	int (*configure)(struct dw_hdmi *hdmi,
7328671edaSAlgea Cao 			 const struct dw_hdmi_plat_data *pdata,
7428671edaSAlgea Cao 			 unsigned long mpixelclock);
7528671edaSAlgea Cao };
7628671edaSAlgea Cao 
7728671edaSAlgea Cao struct dw_hdmi_i2c {
7828671edaSAlgea Cao 	u8			slave_reg;
7928671edaSAlgea Cao 	bool			is_regaddr;
8028671edaSAlgea Cao 	bool			is_segment;
8128671edaSAlgea Cao 
8228671edaSAlgea Cao 	unsigned int		scl_high_ns;
8328671edaSAlgea Cao 	unsigned int		scl_low_ns;
8428671edaSAlgea Cao };
8528671edaSAlgea Cao 
8628671edaSAlgea Cao struct dw_hdmi_qp {
8728671edaSAlgea Cao 	enum dw_hdmi_devtype dev_type;
8828671edaSAlgea Cao 	unsigned int version;
8928671edaSAlgea Cao 	struct hdmi_data_info hdmi_data;
9028671edaSAlgea Cao 	struct hdmi_edid_data edid_data;
9128671edaSAlgea Cao 	const struct dw_hdmi_plat_data *plat_data;
9228671edaSAlgea Cao 	struct ddc_adapter adap;
9328671edaSAlgea Cao 
9428671edaSAlgea Cao 	int vic;
9528671edaSAlgea Cao 	int id;
9628671edaSAlgea Cao 
9728671edaSAlgea Cao 	unsigned long bus_format;
9828671edaSAlgea Cao 	bool cable_plugin;
9928671edaSAlgea Cao 	bool sink_is_hdmi;
10028671edaSAlgea Cao 	bool sink_has_audio;
10128671edaSAlgea Cao 	void *regs;
10228671edaSAlgea Cao 	void *rk_hdmi;
10328671edaSAlgea Cao 	struct dw_hdmi_i2c *i2c;
10428671edaSAlgea Cao 
10528671edaSAlgea Cao 	struct {
10628671edaSAlgea Cao 		const struct dw_hdmi_qp_phy_ops *ops;
10728671edaSAlgea Cao 		const char *name;
10828671edaSAlgea Cao 		void *data;
10928671edaSAlgea Cao 		bool enabled;
11028671edaSAlgea Cao 	} phy;
11128671edaSAlgea Cao 
11228671edaSAlgea Cao 	struct drm_display_mode previous_mode;
11328671edaSAlgea Cao 
11428671edaSAlgea Cao 	unsigned int sample_rate;
11528671edaSAlgea Cao 	unsigned int audio_cts;
11628671edaSAlgea Cao 	unsigned int audio_n;
11728671edaSAlgea Cao 	bool audio_enable;
11828671edaSAlgea Cao 	bool scramble_low_rates;
11928671edaSAlgea Cao 
12028671edaSAlgea Cao 	void (*write)(struct dw_hdmi_qp *hdmi, u32 val, int offset);
12128671edaSAlgea Cao 	u8 (*read)(struct dw_hdmi_qp *hdmi, int offset);
12228671edaSAlgea Cao 
12328671edaSAlgea Cao 	bool hdcp1x_enable;
12428671edaSAlgea Cao 	bool output_bus_format_rgb;
12528671edaSAlgea Cao };
12628671edaSAlgea Cao 
hdmi_writel(struct dw_hdmi_qp * hdmi,u32 val,int offset)12728671edaSAlgea Cao static inline void hdmi_writel(struct dw_hdmi_qp *hdmi, u32 val, int offset)
12828671edaSAlgea Cao {
12928671edaSAlgea Cao 	writel(val, hdmi->regs + offset);
13028671edaSAlgea Cao }
13128671edaSAlgea Cao 
hdmi_readl(struct dw_hdmi_qp * hdmi,int offset)13228671edaSAlgea Cao static inline u32 hdmi_readl(struct dw_hdmi_qp *hdmi, int offset)
13328671edaSAlgea Cao {
13428671edaSAlgea Cao 	return readl(hdmi->regs + offset);
13528671edaSAlgea Cao }
13628671edaSAlgea Cao 
13728671edaSAlgea Cao static void
hdmi_modb(struct dw_hdmi_qp * hdmi,u32 data,u32 mask,unsigned int reg)13828671edaSAlgea Cao hdmi_modb(struct dw_hdmi_qp *hdmi, u32 data, u32 mask, unsigned int reg)
13928671edaSAlgea Cao {
14028671edaSAlgea Cao 	u32 val = hdmi_readl(hdmi, reg) & ~mask;
14128671edaSAlgea Cao 
14228671edaSAlgea Cao 	val |= data & mask;
14328671edaSAlgea Cao 	hdmi_writel(hdmi, val, reg);
14428671edaSAlgea Cao }
14528671edaSAlgea Cao 
hdmi_bus_fmt_is_rgb(unsigned int bus_format)14628671edaSAlgea Cao static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format)
14728671edaSAlgea Cao {
14828671edaSAlgea Cao 	switch (bus_format) {
14928671edaSAlgea Cao 	case MEDIA_BUS_FMT_RGB888_1X24:
15028671edaSAlgea Cao 	case MEDIA_BUS_FMT_RGB101010_1X30:
15128671edaSAlgea Cao 	case MEDIA_BUS_FMT_RGB121212_1X36:
15228671edaSAlgea Cao 	case MEDIA_BUS_FMT_RGB161616_1X48:
15328671edaSAlgea Cao 		return true;
15428671edaSAlgea Cao 
15528671edaSAlgea Cao 	default:
15628671edaSAlgea Cao 		return false;
15728671edaSAlgea Cao 	}
15828671edaSAlgea Cao }
15928671edaSAlgea Cao 
hdmi_bus_fmt_is_yuv444(unsigned int bus_format)16028671edaSAlgea Cao static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format)
16128671edaSAlgea Cao {
16228671edaSAlgea Cao 	switch (bus_format) {
16328671edaSAlgea Cao 	case MEDIA_BUS_FMT_YUV8_1X24:
16428671edaSAlgea Cao 	case MEDIA_BUS_FMT_YUV10_1X30:
16528671edaSAlgea Cao 	case MEDIA_BUS_FMT_YUV12_1X36:
16628671edaSAlgea Cao 	case MEDIA_BUS_FMT_YUV16_1X48:
16728671edaSAlgea Cao 		return true;
16828671edaSAlgea Cao 
16928671edaSAlgea Cao 	default:
17028671edaSAlgea Cao 		return false;
17128671edaSAlgea Cao 	}
17228671edaSAlgea Cao }
17328671edaSAlgea Cao 
hdmi_bus_fmt_is_yuv422(unsigned int bus_format)17428671edaSAlgea Cao static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format)
17528671edaSAlgea Cao {
17628671edaSAlgea Cao 	switch (bus_format) {
17728671edaSAlgea Cao 	case MEDIA_BUS_FMT_UYVY8_1X16:
17828671edaSAlgea Cao 	case MEDIA_BUS_FMT_UYVY10_1X20:
17928671edaSAlgea Cao 	case MEDIA_BUS_FMT_UYVY12_1X24:
180bc291652SAlgea Cao 	case MEDIA_BUS_FMT_YUYV8_1X16:
181bc291652SAlgea Cao 	case MEDIA_BUS_FMT_YUYV10_1X20:
182bc291652SAlgea Cao 	case MEDIA_BUS_FMT_YUYV12_1X24:
18328671edaSAlgea Cao 		return true;
18428671edaSAlgea Cao 
18528671edaSAlgea Cao 	default:
18628671edaSAlgea Cao 		return false;
18728671edaSAlgea Cao 	}
18828671edaSAlgea Cao }
18928671edaSAlgea Cao 
hdmi_bus_fmt_is_yuv420(unsigned int bus_format)19028671edaSAlgea Cao static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format)
19128671edaSAlgea Cao {
19228671edaSAlgea Cao 	switch (bus_format) {
19328671edaSAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
19428671edaSAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
19528671edaSAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
19628671edaSAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
19728671edaSAlgea Cao 		return true;
19828671edaSAlgea Cao 
19928671edaSAlgea Cao 	default:
20028671edaSAlgea Cao 		return false;
20128671edaSAlgea Cao 	}
20228671edaSAlgea Cao }
20328671edaSAlgea Cao 
hdmi_bus_fmt_color_depth(unsigned int bus_format)20428671edaSAlgea Cao static int hdmi_bus_fmt_color_depth(unsigned int bus_format)
20528671edaSAlgea Cao {
20628671edaSAlgea Cao 	switch (bus_format) {
20728671edaSAlgea Cao 	case MEDIA_BUS_FMT_RGB888_1X24:
20828671edaSAlgea Cao 	case MEDIA_BUS_FMT_YUV8_1X24:
20928671edaSAlgea Cao 	case MEDIA_BUS_FMT_UYVY8_1X16:
21028671edaSAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
21128671edaSAlgea Cao 		return 8;
21228671edaSAlgea Cao 
21328671edaSAlgea Cao 	case MEDIA_BUS_FMT_RGB101010_1X30:
21428671edaSAlgea Cao 	case MEDIA_BUS_FMT_YUV10_1X30:
21528671edaSAlgea Cao 	case MEDIA_BUS_FMT_UYVY10_1X20:
21628671edaSAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
21728671edaSAlgea Cao 		return 10;
21828671edaSAlgea Cao 
21928671edaSAlgea Cao 	case MEDIA_BUS_FMT_RGB121212_1X36:
22028671edaSAlgea Cao 	case MEDIA_BUS_FMT_YUV12_1X36:
22128671edaSAlgea Cao 	case MEDIA_BUS_FMT_UYVY12_1X24:
22228671edaSAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
22328671edaSAlgea Cao 		return 12;
22428671edaSAlgea Cao 
22528671edaSAlgea Cao 	case MEDIA_BUS_FMT_RGB161616_1X48:
22628671edaSAlgea Cao 	case MEDIA_BUS_FMT_YUV16_1X48:
22728671edaSAlgea Cao 	case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
22828671edaSAlgea Cao 		return 16;
22928671edaSAlgea Cao 
23028671edaSAlgea Cao 	default:
23128671edaSAlgea Cao 		return 0;
23228671edaSAlgea Cao 	}
23328671edaSAlgea Cao }
23428671edaSAlgea Cao 
drm_scdc_set_scrambling(struct ddc_adapter * adapter,bool enable)23528671edaSAlgea Cao static bool drm_scdc_set_scrambling(struct ddc_adapter *adapter, bool enable)
23628671edaSAlgea Cao {
23728671edaSAlgea Cao 	u8 config;
23828671edaSAlgea Cao 	int ret;
23928671edaSAlgea Cao 
24028671edaSAlgea Cao 	ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
24128671edaSAlgea Cao 	if (ret < 0) {
24228671edaSAlgea Cao 		debug("Failed to read TMDS config: %d\n", ret);
24328671edaSAlgea Cao 		return false;
24428671edaSAlgea Cao 	}
24528671edaSAlgea Cao 
24628671edaSAlgea Cao 	if (enable)
24728671edaSAlgea Cao 		config |= SCDC_SCRAMBLING_ENABLE;
24828671edaSAlgea Cao 	else
24928671edaSAlgea Cao 		config &= ~SCDC_SCRAMBLING_ENABLE;
25028671edaSAlgea Cao 
25128671edaSAlgea Cao 	ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
25228671edaSAlgea Cao 	if (ret < 0) {
25328671edaSAlgea Cao 		debug("Failed to enable scrambling: %d\n", ret);
25428671edaSAlgea Cao 		return false;
25528671edaSAlgea Cao 	}
25628671edaSAlgea Cao 
25728671edaSAlgea Cao 	return true;
25828671edaSAlgea Cao }
25928671edaSAlgea Cao 
26028671edaSAlgea Cao static bool
drm_scdc_set_high_tmds_clock_ratio(struct ddc_adapter * adapter,bool set)26128671edaSAlgea Cao drm_scdc_set_high_tmds_clock_ratio(struct ddc_adapter *adapter, bool set)
26228671edaSAlgea Cao {
26328671edaSAlgea Cao 	u8 config;
26428671edaSAlgea Cao 	int ret;
26528671edaSAlgea Cao 
26628671edaSAlgea Cao 	ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
26728671edaSAlgea Cao 	if (ret < 0) {
26828671edaSAlgea Cao 		debug("Failed to read TMDS config: %d\n", ret);
26928671edaSAlgea Cao 		return false;
27028671edaSAlgea Cao 	}
27128671edaSAlgea Cao 
27228671edaSAlgea Cao 	if (set)
27328671edaSAlgea Cao 		config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
27428671edaSAlgea Cao 	else
27528671edaSAlgea Cao 		config &= ~SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
27628671edaSAlgea Cao 
27728671edaSAlgea Cao 	ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
27828671edaSAlgea Cao 	if (ret < 0) {
27928671edaSAlgea Cao 		debug("Failed to set TMDS clock ratio: %d\n", ret);
28028671edaSAlgea Cao 		return false;
28128671edaSAlgea Cao 	}
28228671edaSAlgea Cao 
28328671edaSAlgea Cao 	/*
28428671edaSAlgea Cao 	 * The spec says that a source should wait minimum 1ms and maximum
28528671edaSAlgea Cao 	 * 100ms after writing the TMDS config for clock ratio. Lets allow a
28628671edaSAlgea Cao 	 * wait of up to 2ms here.
28728671edaSAlgea Cao 	 */
28828671edaSAlgea Cao 	udelay(2000);
28928671edaSAlgea Cao 	return true;
29028671edaSAlgea Cao }
29128671edaSAlgea Cao 
dw_hdmi_i2c_init(struct dw_hdmi_qp * hdmi)29228671edaSAlgea Cao static void dw_hdmi_i2c_init(struct dw_hdmi_qp *hdmi)
29328671edaSAlgea Cao {
29428671edaSAlgea Cao 	/* Software reset */
29528671edaSAlgea Cao 	hdmi_writel(hdmi, 0x01, I2CM_CONTROL0);
29628671edaSAlgea Cao 
29728671edaSAlgea Cao 	hdmi_writel(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0);
29828671edaSAlgea Cao 
29928671edaSAlgea Cao 	hdmi_modb(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0);
30028671edaSAlgea Cao 
30128671edaSAlgea Cao 	/* Clear DONE and ERROR interrupts */
30228671edaSAlgea Cao 	hdmi_writel(hdmi, I2CM_OP_DONE_CLEAR | I2CM_NACK_RCVD_CLEAR,
30328671edaSAlgea Cao 		    MAINUNIT_1_INT_CLEAR);
30428671edaSAlgea Cao }
30528671edaSAlgea Cao 
dw_hdmi_i2c_read(struct dw_hdmi_qp * hdmi,unsigned char * buf,unsigned int length)30628671edaSAlgea Cao static int dw_hdmi_i2c_read(struct dw_hdmi_qp *hdmi,
30728671edaSAlgea Cao 			    unsigned char *buf, unsigned int length)
30828671edaSAlgea Cao {
30928671edaSAlgea Cao 	struct dw_hdmi_i2c *i2c = hdmi->i2c;
310eec52208SAlgea Cao 	int i = 20, retry;
31128671edaSAlgea Cao 	u32 intr = 0;
312eec52208SAlgea Cao 	bool read_edid = false;
31328671edaSAlgea Cao 
31428671edaSAlgea Cao 	if (!i2c->is_regaddr) {
31528671edaSAlgea Cao 		printf("set read register address to 0\n");
31628671edaSAlgea Cao 		i2c->slave_reg = 0x00;
31728671edaSAlgea Cao 		i2c->is_regaddr = true;
31828671edaSAlgea Cao 	}
31928671edaSAlgea Cao 
320eec52208SAlgea Cao 	/* edid reads are in 128 bytes. scdc reads are in 1 byte */
321eec52208SAlgea Cao 	if (length == HDMI_EDID_BLOCK_LEN)
322eec52208SAlgea Cao 		read_edid = true;
323eec52208SAlgea Cao 
324eec52208SAlgea Cao 	while (length > 0) {
325eec52208SAlgea Cao 		retry = 100;
326eec52208SAlgea Cao 		hdmi_modb(hdmi, i2c->slave_reg << 12, I2CM_ADDR,
32728671edaSAlgea Cao 			  I2CM_INTERFACE_CONTROL0);
32828671edaSAlgea Cao 
329eec52208SAlgea Cao 		if (read_edid) {
330eec52208SAlgea Cao 			hdmi_modb(hdmi, I2CM_16BYTES, I2CM_NBYTES_MASK,
331eec52208SAlgea Cao 				  I2CM_INTERFACE_CONTROL0);
332eec52208SAlgea Cao 			i2c->slave_reg += 16;
333eec52208SAlgea Cao 			length -= 16;
334eec52208SAlgea Cao 		} else {
335eec52208SAlgea Cao 			hdmi_modb(hdmi, I2CM_1BYTES, I2CM_NBYTES_MASK,
336eec52208SAlgea Cao 				  I2CM_INTERFACE_CONTROL0);
337eec52208SAlgea Cao 			i2c->slave_reg++;
338eec52208SAlgea Cao 			length--;
339eec52208SAlgea Cao 		}
340eec52208SAlgea Cao 
341eec52208SAlgea Cao 		while (retry > 0) {
342eec52208SAlgea Cao 			if (!hdmi->phy.ops->read_hpd(hdmi->rk_hdmi)) {
343eec52208SAlgea Cao 				debug("hdmi disconnect, stop ddc read\n");
344eec52208SAlgea Cao 				return -EPERM;
345eec52208SAlgea Cao 			}
346eec52208SAlgea Cao 
347eec52208SAlgea Cao 			i = 20;
348*9c170041SAlgea Cao 			if (i2c->is_segment)
349*9c170041SAlgea Cao 				hdmi_modb(hdmi, I2CM_EXT_READ, I2CM_WR_MASK,
350*9c170041SAlgea Cao 					  I2CM_INTERFACE_CONTROL0);
351*9c170041SAlgea Cao 			else
35228671edaSAlgea Cao 				hdmi_modb(hdmi, I2CM_FM_READ, I2CM_WR_MASK,
35328671edaSAlgea Cao 					  I2CM_INTERFACE_CONTROL0);
35428671edaSAlgea Cao 
35528671edaSAlgea Cao 			while (i--) {
35628671edaSAlgea Cao 				udelay(1000);
357eec52208SAlgea Cao 				intr = hdmi_readl(hdmi, MAINUNIT_1_INT_STATUS);
358eec52208SAlgea Cao 				intr &= (I2CM_OP_DONE_IRQ |
359eec52208SAlgea Cao 					 I2CM_READ_REQUEST_IRQ |
36028671edaSAlgea Cao 					 I2CM_NACK_RCVD_IRQ);
36128671edaSAlgea Cao 				if (intr) {
362eec52208SAlgea Cao 					hdmi_writel(hdmi, intr,
363eec52208SAlgea Cao 						    MAINUNIT_1_INT_CLEAR);
36428671edaSAlgea Cao 					break;
36528671edaSAlgea Cao 				}
36628671edaSAlgea Cao 			}
36728671edaSAlgea Cao 
36828671edaSAlgea Cao 			if (!i) {
36928671edaSAlgea Cao 				printf("i2c read time out!\n");
37028671edaSAlgea Cao 				hdmi_writel(hdmi, 0x01, I2CM_CONTROL0);
371eec52208SAlgea Cao 				retry -= 10;
372eec52208SAlgea Cao 				continue;
37328671edaSAlgea Cao 			}
37428671edaSAlgea Cao 
37528671edaSAlgea Cao 			/* Check for error condition on the bus */
37628671edaSAlgea Cao 			if (intr & I2CM_NACK_RCVD_IRQ) {
37728671edaSAlgea Cao 				printf("i2c read err!\n");
37828671edaSAlgea Cao 				hdmi_writel(hdmi, 0x01, I2CM_CONTROL0);
379eec52208SAlgea Cao 				retry--;
380eec52208SAlgea Cao 				mdelay(10);
381eec52208SAlgea Cao 				continue;
382eec52208SAlgea Cao 			}
383eec52208SAlgea Cao 
384eec52208SAlgea Cao 			/* read success */
385eec52208SAlgea Cao 			break;
386eec52208SAlgea Cao 		}
387eec52208SAlgea Cao 
388eec52208SAlgea Cao 		if (retry <= 0) {
389eec52208SAlgea Cao 			printf("ddc read failed offset:0x%x\n", i2c->slave_reg);
39028671edaSAlgea Cao 			return -EIO;
39128671edaSAlgea Cao 		}
39228671edaSAlgea Cao 
393eec52208SAlgea Cao 		if (read_edid) {
394eec52208SAlgea Cao 			u8 reg_offset, val_offset, i;
395eec52208SAlgea Cao 			u32 val, reg;
396eec52208SAlgea Cao 
397eec52208SAlgea Cao 			for (i = 0; i < 16; i++) {
398eec52208SAlgea Cao 				reg_offset = i / 4;
399eec52208SAlgea Cao 				val_offset = (i % 4) * 8;
400eec52208SAlgea Cao 				reg = I2CM_INTERFACE_RDDATA_0_3 + 4 *
401eec52208SAlgea Cao 					reg_offset;
402eec52208SAlgea Cao 				val = hdmi_readl(hdmi, reg);
403eec52208SAlgea Cao 				*buf++ = (val & (0xff << val_offset)) >>
404eec52208SAlgea Cao 					val_offset;
405eec52208SAlgea Cao 				debug("i2c read done! 0x%02x\n",
406eec52208SAlgea Cao 				      (val & (0xff << val_offset)) >>
407eec52208SAlgea Cao 				       val_offset);
408eec52208SAlgea Cao 			}
409eec52208SAlgea Cao 		} else {
410eec52208SAlgea Cao 			*buf++ = hdmi_readl(hdmi, I2CM_INTERFACE_RDDATA_0_3) &
411eec52208SAlgea Cao 				0xff;
412eec52208SAlgea Cao 			debug("i2c read done! 0x%02x\n",
413eec52208SAlgea Cao 			      hdmi_readl(hdmi, I2CM_INTERFACE_RDDATA_0_3));
414eec52208SAlgea Cao 		}
415eec52208SAlgea Cao 
41628671edaSAlgea Cao 		hdmi_modb(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0);
41728671edaSAlgea Cao 	}
41828671edaSAlgea Cao 	i2c->is_segment = false;
41928671edaSAlgea Cao 
42028671edaSAlgea Cao 	return 0;
42128671edaSAlgea Cao }
42228671edaSAlgea Cao 
dw_hdmi_i2c_write(struct dw_hdmi_qp * hdmi,unsigned char * buf,unsigned int length)42328671edaSAlgea Cao static int dw_hdmi_i2c_write(struct dw_hdmi_qp *hdmi,
42428671edaSAlgea Cao 			     unsigned char *buf, unsigned int length)
42528671edaSAlgea Cao {
42628671edaSAlgea Cao 	struct dw_hdmi_i2c *i2c = hdmi->i2c;
427eec52208SAlgea Cao 	int i = 20, retry;
42828671edaSAlgea Cao 	u32 intr = 0;
42928671edaSAlgea Cao 
43028671edaSAlgea Cao 	if (!i2c->is_regaddr) {
43128671edaSAlgea Cao 		/* Use the first write byte as register address */
43228671edaSAlgea Cao 		i2c->slave_reg = buf[0];
43328671edaSAlgea Cao 		length--;
43428671edaSAlgea Cao 		buf++;
43528671edaSAlgea Cao 		i2c->is_regaddr = true;
43628671edaSAlgea Cao 	}
43728671edaSAlgea Cao 
43828671edaSAlgea Cao 	while (length--) {
439eec52208SAlgea Cao 		retry = 100;
440eec52208SAlgea Cao 
441eec52208SAlgea Cao 		while (retry > 0) {
442eec52208SAlgea Cao 			if (!hdmi->phy.ops->read_hpd(hdmi->rk_hdmi)) {
443eec52208SAlgea Cao 				debug("hdmi disconnect, stop ddc read\n");
444eec52208SAlgea Cao 				return -EPERM;
445eec52208SAlgea Cao 			}
446eec52208SAlgea Cao 
447eec52208SAlgea Cao 			i = 20;
44828671edaSAlgea Cao 			hdmi_writel(hdmi, *buf++, I2CM_INTERFACE_WRDATA_0_3);
44928671edaSAlgea Cao 			hdmi_modb(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR,
45028671edaSAlgea Cao 				I2CM_INTERFACE_CONTROL0);
45128671edaSAlgea Cao 			hdmi_modb(hdmi, I2CM_FM_WRITE, I2CM_WR_MASK,
45228671edaSAlgea Cao 				I2CM_INTERFACE_CONTROL0);
45328671edaSAlgea Cao 
45428671edaSAlgea Cao 			while (i--) {
45528671edaSAlgea Cao 				udelay(1000);
456eec52208SAlgea Cao 				intr = hdmi_readl(hdmi, MAINUNIT_1_INT_STATUS);
457eec52208SAlgea Cao 				intr &= (I2CM_OP_DONE_IRQ |
458eec52208SAlgea Cao 					 I2CM_READ_REQUEST_IRQ |
45928671edaSAlgea Cao 					 I2CM_NACK_RCVD_IRQ);
46028671edaSAlgea Cao 				if (intr) {
461eec52208SAlgea Cao 					hdmi_writel(hdmi, intr,
462eec52208SAlgea Cao 						    MAINUNIT_1_INT_CLEAR);
46328671edaSAlgea Cao 					break;
46428671edaSAlgea Cao 				}
46528671edaSAlgea Cao 			}
46628671edaSAlgea Cao 
46728671edaSAlgea Cao 			if (!i) {
46828671edaSAlgea Cao 				printf("i2c write time out!\n");
46928671edaSAlgea Cao 				hdmi_writel(hdmi, 0x01, I2CM_CONTROL0);
470eec52208SAlgea Cao 				retry -= 10;
471eec52208SAlgea Cao 				continue;
47228671edaSAlgea Cao 			}
47328671edaSAlgea Cao 
47428671edaSAlgea Cao 			/* Check for error condition on the bus */
47528671edaSAlgea Cao 			if (intr & I2CM_NACK_RCVD_IRQ) {
47628671edaSAlgea Cao 				printf("i2c write nack!\n");
47728671edaSAlgea Cao 				hdmi_writel(hdmi, 0x01, I2CM_CONTROL0);
478eec52208SAlgea Cao 				retry--;
479eec52208SAlgea Cao 				mdelay(10);
480eec52208SAlgea Cao 				continue;
481eec52208SAlgea Cao 			}
482eec52208SAlgea Cao 			/* write success */
483eec52208SAlgea Cao 			break;
48428671edaSAlgea Cao 		}
48528671edaSAlgea Cao 		hdmi_modb(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0);
486eec52208SAlgea Cao 		if (retry <= 0) {
487eec52208SAlgea Cao 			printf("ddc write failed\n");
488eec52208SAlgea Cao 			return -EIO;
489eec52208SAlgea Cao 		}
49028671edaSAlgea Cao 	}
49128671edaSAlgea Cao 
49228671edaSAlgea Cao 	return 0;
49328671edaSAlgea Cao }
49428671edaSAlgea Cao 
dw_hdmi_i2c_xfer(struct ddc_adapter * adap,struct i2c_msg * msgs,int num)49528671edaSAlgea Cao static int dw_hdmi_i2c_xfer(struct ddc_adapter *adap,
49628671edaSAlgea Cao 			    struct i2c_msg *msgs, int num)
49728671edaSAlgea Cao {
49828671edaSAlgea Cao 	struct dw_hdmi_qp *hdmi = container_of(adap, struct dw_hdmi_qp, adap);
49928671edaSAlgea Cao 	struct dw_hdmi_i2c *i2c = hdmi->i2c;
50028671edaSAlgea Cao 	u8 addr = msgs[0].addr;
50128671edaSAlgea Cao 	int i, ret = 0;
50228671edaSAlgea Cao 
50328671edaSAlgea Cao 	debug("i2c xfer: num: %d, addr: %#x\n", num, addr);
50428671edaSAlgea Cao 
50528671edaSAlgea Cao 	for (i = 0; i < num; i++) {
50628671edaSAlgea Cao 		if (msgs[i].len == 0) {
50728671edaSAlgea Cao 			printf("unsupported transfer %d/%d, no data\n",
50828671edaSAlgea Cao 			       i + 1, num);
50928671edaSAlgea Cao 			return -EOPNOTSUPP;
51028671edaSAlgea Cao 		}
51128671edaSAlgea Cao 	}
51228671edaSAlgea Cao 
51328671edaSAlgea Cao 	/* Unmute DONE and ERROR interrupts */
51428671edaSAlgea Cao 	hdmi_modb(hdmi, I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N,
51528671edaSAlgea Cao 		  I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N,
51628671edaSAlgea Cao 		  MAINUNIT_1_INT_MASK_N);
51728671edaSAlgea Cao 
51828671edaSAlgea Cao 	/* Set slave device address taken from the first I2C message */
51928671edaSAlgea Cao 	if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1)
52028671edaSAlgea Cao 		addr = DDC_ADDR;
52128671edaSAlgea Cao 
52228671edaSAlgea Cao 	hdmi_modb(hdmi, addr << 5, I2CM_SLVADDR, I2CM_INTERFACE_CONTROL0);
52328671edaSAlgea Cao 
52428671edaSAlgea Cao 	/* Set slave device register address on transfer */
52528671edaSAlgea Cao 	i2c->is_regaddr = false;
52628671edaSAlgea Cao 
52728671edaSAlgea Cao 	/* Set segment pointer for I2C extended read mode operation */
52828671edaSAlgea Cao 	i2c->is_segment = false;
52928671edaSAlgea Cao 
53028671edaSAlgea Cao 	for (i = 0; i < num; i++) {
53128671edaSAlgea Cao 		debug("xfer: num: %d/%d, len: %d, flags: %#x\n",
53228671edaSAlgea Cao 		      i + 1, num, msgs[i].len, msgs[i].flags);
53328671edaSAlgea Cao 
53428671edaSAlgea Cao 		if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) {
53528671edaSAlgea Cao 			i2c->is_segment = true;
53628671edaSAlgea Cao 			hdmi_modb(hdmi, DDC_SEGMENT_ADDR, I2CM_SEG_ADDR,
53728671edaSAlgea Cao 				  I2CM_INTERFACE_CONTROL1);
538*9c170041SAlgea Cao 			hdmi_modb(hdmi, *msgs[i].buf << 7, I2CM_SEG_PTR,
53928671edaSAlgea Cao 				  I2CM_INTERFACE_CONTROL1);
54028671edaSAlgea Cao 		} else {
54128671edaSAlgea Cao 			if (msgs[i].flags & I2C_M_RD)
54228671edaSAlgea Cao 				ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf,
54328671edaSAlgea Cao 						       msgs[i].len);
54428671edaSAlgea Cao 			else
54528671edaSAlgea Cao 				ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf,
54628671edaSAlgea Cao 							msgs[i].len);
54728671edaSAlgea Cao 		}
54828671edaSAlgea Cao 		if (ret < 0)
54928671edaSAlgea Cao 			break;
55028671edaSAlgea Cao 	}
55128671edaSAlgea Cao 
55228671edaSAlgea Cao 	if (!ret)
55328671edaSAlgea Cao 		ret = num;
55428671edaSAlgea Cao 
55528671edaSAlgea Cao 	/* Mute DONE and ERROR interrupts */
55628671edaSAlgea Cao 	hdmi_modb(hdmi, 0, I2CM_OP_DONE_MASK_N | I2CM_NACK_RCVD_MASK_N,
55728671edaSAlgea Cao 		  MAINUNIT_1_INT_MASK_N);
55828671edaSAlgea Cao 
55928671edaSAlgea Cao 	return ret;
56028671edaSAlgea Cao }
56128671edaSAlgea Cao 
dw_hdmi_detect_phy(struct dw_hdmi_qp * hdmi)56228671edaSAlgea Cao static int dw_hdmi_detect_phy(struct dw_hdmi_qp *hdmi)
56328671edaSAlgea Cao {
56428671edaSAlgea Cao 	/* Vendor PHYs require support from the glue layer. */
56528671edaSAlgea Cao 	if (!hdmi->plat_data->qp_phy_ops || !hdmi->plat_data->phy_name) {
56628671edaSAlgea Cao 		dev_err(hdmi->dev,
56728671edaSAlgea Cao 			"Vendor HDMI PHY not supported by glue layer\n");
56828671edaSAlgea Cao 		return -ENODEV;
56928671edaSAlgea Cao 	}
57028671edaSAlgea Cao 
57128671edaSAlgea Cao 	hdmi->phy.ops = hdmi->plat_data->qp_phy_ops;
57228671edaSAlgea Cao 	hdmi->phy.data = hdmi->plat_data->phy_data;
57328671edaSAlgea Cao 	hdmi->phy.name = hdmi->plat_data->phy_name;
57428671edaSAlgea Cao 
57528671edaSAlgea Cao 	return 0;
57628671edaSAlgea Cao }
57728671edaSAlgea Cao 
57828671edaSAlgea Cao static unsigned int
hdmi_get_tmdsclock(struct dw_hdmi_qp * hdmi,unsigned long mpixelclock)57928671edaSAlgea Cao hdmi_get_tmdsclock(struct dw_hdmi_qp *hdmi, unsigned long mpixelclock)
58028671edaSAlgea Cao {
58128671edaSAlgea Cao 	unsigned int tmdsclock = mpixelclock;
58228671edaSAlgea Cao 	unsigned int depth =
58328671edaSAlgea Cao 		hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
58428671edaSAlgea Cao 
58528671edaSAlgea Cao 	if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) {
58628671edaSAlgea Cao 		switch (depth) {
58728671edaSAlgea Cao 		case 16:
58828671edaSAlgea Cao 			tmdsclock = mpixelclock * 2;
58928671edaSAlgea Cao 			break;
59028671edaSAlgea Cao 		case 12:
59128671edaSAlgea Cao 			tmdsclock = mpixelclock * 3 / 2;
59228671edaSAlgea Cao 			break;
59328671edaSAlgea Cao 		case 10:
59428671edaSAlgea Cao 			tmdsclock = mpixelclock * 5 / 4;
59528671edaSAlgea Cao 			break;
59628671edaSAlgea Cao 		default:
59728671edaSAlgea Cao 			break;
59828671edaSAlgea Cao 		}
59928671edaSAlgea Cao 	}
60028671edaSAlgea Cao 
60128671edaSAlgea Cao 	return tmdsclock;
60228671edaSAlgea Cao }
60328671edaSAlgea Cao 
hdmi_infoframe_set_checksum(u8 * ptr,int size)604e7f3b804SAlgea Cao static void hdmi_infoframe_set_checksum(u8 *ptr, int size)
605e7f3b804SAlgea Cao {
606e7f3b804SAlgea Cao 	u8 csum = 0;
607e7f3b804SAlgea Cao 	int i;
608e7f3b804SAlgea Cao 
609e7f3b804SAlgea Cao 	ptr[3] = 0;
610e7f3b804SAlgea Cao 	/* compute checksum */
611e7f3b804SAlgea Cao 	for (i = 0; i < size; i++)
612e7f3b804SAlgea Cao 		csum += ptr[i];
613e7f3b804SAlgea Cao 
614e7f3b804SAlgea Cao 	ptr[3] = 256 - csum;
615e7f3b804SAlgea Cao }
616e7f3b804SAlgea Cao 
is_hdmi2_sink(struct dw_hdmi_qp * hdmi)617cdcef590SAlgea Cao static bool is_hdmi2_sink(struct dw_hdmi_qp *hdmi)
618cdcef590SAlgea Cao {
619cdcef590SAlgea Cao 	return hdmi->edid_data.display_info.hdmi.scdc.supported ||
620cdcef590SAlgea Cao 		hdmi->edid_data.display_info.color_formats & DRM_COLOR_FORMAT_YCRCB420;
621cdcef590SAlgea Cao }
622cdcef590SAlgea Cao 
hdmi_config_AVI(struct dw_hdmi_qp * hdmi,struct drm_display_mode * mode)62328671edaSAlgea Cao static void hdmi_config_AVI(struct dw_hdmi_qp *hdmi, struct drm_display_mode *mode)
62428671edaSAlgea Cao {
62528671edaSAlgea Cao 	struct hdmi_avi_infoframe frame;
62628671edaSAlgea Cao 	u32 val, i, j;
62728671edaSAlgea Cao 	u8 buff[17];
62828671edaSAlgea Cao 	bool is_hdmi2 = false;
62928671edaSAlgea Cao 	enum hdmi_quantization_range rgb_quant_range =
63028671edaSAlgea Cao 		hdmi->hdmi_data.quant_range;
63128671edaSAlgea Cao 
63228671edaSAlgea Cao 	if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) ||
63328671edaSAlgea Cao 	    hdmi->edid_data.display_info.hdmi.scdc.supported)
63428671edaSAlgea Cao 		is_hdmi2 = true;
63528671edaSAlgea Cao 	/* Initialise info frame from DRM mode */
63628671edaSAlgea Cao 	drm_hdmi_avi_infoframe_from_display_mode(&frame, mode, is_hdmi2);
63728671edaSAlgea Cao 
63828671edaSAlgea Cao 	/*
63928671edaSAlgea Cao 	 * Ignore monitor selectable quantization, use quantization set
64028671edaSAlgea Cao 	 * by the user
64128671edaSAlgea Cao 	 */
64228671edaSAlgea Cao 	drm_hdmi_avi_infoframe_quant_range(&frame, mode, rgb_quant_range,
643b692eb6fSAlgea Cao 					   true, is_hdmi2);
64428671edaSAlgea Cao 	if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
64528671edaSAlgea Cao 		frame.colorspace = HDMI_COLORSPACE_YUV444;
64628671edaSAlgea Cao 	else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
64728671edaSAlgea Cao 		frame.colorspace = HDMI_COLORSPACE_YUV422;
64828671edaSAlgea Cao 	else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
64928671edaSAlgea Cao 		frame.colorspace = HDMI_COLORSPACE_YUV420;
65028671edaSAlgea Cao 	else
65128671edaSAlgea Cao 		frame.colorspace = HDMI_COLORSPACE_RGB;
65228671edaSAlgea Cao 
653cdcef590SAlgea Cao 	/* Set up colorimetry and quant range */
65428671edaSAlgea Cao 	if (!hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
65528671edaSAlgea Cao 		switch (hdmi->hdmi_data.enc_out_encoding) {
65628671edaSAlgea Cao 		case V4L2_YCBCR_ENC_601:
65728671edaSAlgea Cao 			if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV601)
65828671edaSAlgea Cao 				frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
65928671edaSAlgea Cao 			else
66028671edaSAlgea Cao 				frame.colorimetry = HDMI_COLORIMETRY_ITU_601;
66128671edaSAlgea Cao 			frame.extended_colorimetry =
66228671edaSAlgea Cao 					HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
66328671edaSAlgea Cao 			break;
66428671edaSAlgea Cao 		case V4L2_YCBCR_ENC_709:
66528671edaSAlgea Cao 			if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV709)
66628671edaSAlgea Cao 				frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
66728671edaSAlgea Cao 			else
66828671edaSAlgea Cao 				frame.colorimetry = HDMI_COLORIMETRY_ITU_709;
66928671edaSAlgea Cao 			frame.extended_colorimetry =
67028671edaSAlgea Cao 					HDMI_EXTENDED_COLORIMETRY_XV_YCC_709;
67128671edaSAlgea Cao 			break;
672cdcef590SAlgea Cao 		case V4L2_YCBCR_ENC_BT2020:
673cdcef590SAlgea Cao 			if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_BT2020)
674cdcef590SAlgea Cao 				frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
675cdcef590SAlgea Cao 			else
676cdcef590SAlgea Cao 				frame.colorimetry = HDMI_COLORIMETRY_ITU_709;
677cdcef590SAlgea Cao 			frame.extended_colorimetry =
678cdcef590SAlgea Cao 					HDMI_EXTENDED_COLORIMETRY_BT2020;
679cdcef590SAlgea Cao 			break;
68028671edaSAlgea Cao 		default: /* Carries no data */
68128671edaSAlgea Cao 			frame.colorimetry = HDMI_COLORIMETRY_ITU_601;
68228671edaSAlgea Cao 			frame.extended_colorimetry =
68328671edaSAlgea Cao 					HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
68428671edaSAlgea Cao 			break;
68528671edaSAlgea Cao 		}
686cdcef590SAlgea Cao 
687cdcef590SAlgea Cao 		frame.ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
688cdcef590SAlgea Cao 	} else {
689cdcef590SAlgea Cao 		if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_BT2020) {
690cdcef590SAlgea Cao 			frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
691cdcef590SAlgea Cao 			frame.extended_colorimetry =
692cdcef590SAlgea Cao 				HDMI_EXTENDED_COLORIMETRY_BT2020;
693cdcef590SAlgea Cao 		} else {
694cdcef590SAlgea Cao 			frame.colorimetry = HDMI_COLORIMETRY_NONE;
695cdcef590SAlgea Cao 			frame.extended_colorimetry =
696cdcef590SAlgea Cao 				HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
697cdcef590SAlgea Cao 		}
698cdcef590SAlgea Cao 
699cdcef590SAlgea Cao 		if (is_hdmi2_sink(hdmi) &&
700cdcef590SAlgea Cao 		    frame.quantization_range == HDMI_QUANTIZATION_RANGE_FULL)
701cdcef590SAlgea Cao 			frame.ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_FULL;
702cdcef590SAlgea Cao 		else
703cdcef590SAlgea Cao 			frame.ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
70428671edaSAlgea Cao 	}
70528671edaSAlgea Cao 
70628671edaSAlgea Cao 	frame.scan_mode = HDMI_SCAN_MODE_NONE;
70728671edaSAlgea Cao 
70828671edaSAlgea Cao 	hdmi_avi_infoframe_pack_only(&frame, buff, 17);
70928671edaSAlgea Cao 
710e7f3b804SAlgea Cao 	/* mode which vic >= 128 must use avi version 3 */
711e7f3b804SAlgea Cao 	if (hdmi->vic >= 128) {
712e7f3b804SAlgea Cao 		frame.version = 3;
713e7f3b804SAlgea Cao 		buff[1] = frame.version;
714e7f3b804SAlgea Cao 		buff[4] &= 0x1f;
715e7f3b804SAlgea Cao 		buff[4] |= ((frame.colorspace & 0x7) << 5);
71699bfa312SAlgea Cao 		buff[7] = hdmi->vic;
717e7f3b804SAlgea Cao 		hdmi_infoframe_set_checksum(buff, 17);
718e7f3b804SAlgea Cao 	}
719e7f3b804SAlgea Cao 
72028671edaSAlgea Cao 	/*
72128671edaSAlgea Cao 	 * The Designware IP uses a different byte format from standard
72228671edaSAlgea Cao 	 * AVI info frames, though generally the bits are in the correct
72328671edaSAlgea Cao 	 * bytes.
72428671edaSAlgea Cao 	 */
72528671edaSAlgea Cao 
72628671edaSAlgea Cao 	val = (frame.version << 8) | (frame.length << 16);
72728671edaSAlgea Cao 	hdmi_writel(hdmi, val, PKT_AVI_CONTENTS0);
72828671edaSAlgea Cao 
72928671edaSAlgea Cao 	for (i = 0; i < 4; i++) {
73028671edaSAlgea Cao 		for (j = 0; j < 4; j++) {
73128671edaSAlgea Cao 			if (i * 4 + j >= 14)
73228671edaSAlgea Cao 				break;
73328671edaSAlgea Cao 			if (!j)
73428671edaSAlgea Cao 				val = buff[i * 4 + j + 3];
73528671edaSAlgea Cao 			val |= buff[i * 4 + j + 3] << (8 * j);
73628671edaSAlgea Cao 		}
73728671edaSAlgea Cao 
73828671edaSAlgea Cao 		hdmi_writel(hdmi, val, PKT_AVI_CONTENTS1 + i * 4);
73928671edaSAlgea Cao 	}
74028671edaSAlgea Cao 
741626a3bccSAlgea Cao 	hdmi_modb(hdmi, 0, PKTSCHED_AVI_FIELDRATE, PKTSCHED_PKT_CONFIG1);
742626a3bccSAlgea Cao 
7438c597bcaSAlgea Cao 	hdmi_modb(hdmi, PKTSCHED_AVI_TX_EN, PKTSCHED_AVI_TX_EN,
74428671edaSAlgea Cao 		  PKTSCHED_PKT_EN);
74528671edaSAlgea Cao }
74628671edaSAlgea Cao 
7472afea1f0SAlgea Cao #define VSI_PKT_TYPE		0x81
7482afea1f0SAlgea Cao #define VSI_PKT_VERSION		1
7492afea1f0SAlgea Cao #define HDMI_FORUM_OUI		0xc45dd8
7502afea1f0SAlgea Cao #define ALLM_MODE		BIT(1)
7512afea1f0SAlgea Cao #define HDMI_FORUM_LEN		9
7522afea1f0SAlgea Cao 
hdmi_config_vendor_specific_infoframe(struct dw_hdmi_qp * hdmi,struct drm_display_mode * mode)75399bfa312SAlgea Cao static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi_qp *hdmi,
75499bfa312SAlgea Cao 						  struct drm_display_mode *mode)
75599bfa312SAlgea Cao {
75699bfa312SAlgea Cao 	struct hdmi_vendor_infoframe frame;
7572afea1f0SAlgea Cao 	struct dw_hdmi_link_config *link_cfg = NULL;
75899bfa312SAlgea Cao 	u8 buffer[10];
75999bfa312SAlgea Cao 	u32 val;
76099bfa312SAlgea Cao 	ssize_t err;
76199bfa312SAlgea Cao 	int i, reg;
76299bfa312SAlgea Cao 
7632afea1f0SAlgea Cao 	link_cfg = dw_hdmi_rockchip_get_link_cfg(hdmi->rk_hdmi);
7642afea1f0SAlgea Cao 
765cd2307e7SAlgea Cao 	hdmi_modb(hdmi, 0, PKTSCHED_VSI_TX_EN, PKTSCHED_PKT_EN);
7662afea1f0SAlgea Cao 
7672afea1f0SAlgea Cao 	for (i = 0; i <= 7; i++)
7682afea1f0SAlgea Cao 		hdmi_writel(hdmi, 0, PKT_VSI_CONTENTS0 + i * 4);
7692afea1f0SAlgea Cao 
7702afea1f0SAlgea Cao 	if (link_cfg->allm_en) {
7712afea1f0SAlgea Cao 		buffer[0] = VSI_PKT_TYPE;
7722afea1f0SAlgea Cao 		buffer[1] = VSI_PKT_VERSION;
7732afea1f0SAlgea Cao 		buffer[2] = 5;
7742afea1f0SAlgea Cao 		buffer[4] = HDMI_FORUM_OUI & 0xff;
7752afea1f0SAlgea Cao 		buffer[5] = (HDMI_FORUM_OUI >> 8) & 0xff;
7762afea1f0SAlgea Cao 		buffer[6] = (HDMI_FORUM_OUI >> 16) & 0xff;
7772afea1f0SAlgea Cao 		buffer[7] = VSI_PKT_VERSION;
7782afea1f0SAlgea Cao 		buffer[8] = ALLM_MODE;
7792afea1f0SAlgea Cao 
7802afea1f0SAlgea Cao 		hdmi_infoframe_set_checksum(buffer, HDMI_FORUM_LEN);
7812afea1f0SAlgea Cao 
7822afea1f0SAlgea Cao 		err = 9;
7832afea1f0SAlgea Cao 	} else {
78499bfa312SAlgea Cao 		err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, mode);
78599bfa312SAlgea Cao 		if (err < 0)
78699bfa312SAlgea Cao 			/*
78799bfa312SAlgea Cao 			 * Going into that statement does not means vendor infoframe
78899bfa312SAlgea Cao 			 * fails. It just informed us that vendor infoframe is not
78999bfa312SAlgea Cao 			 * needed for the selected mode. Only 4k or stereoscopic 3D
79099bfa312SAlgea Cao 			 * mode requires vendor infoframe. So just simply return.
79199bfa312SAlgea Cao 			 */
79299bfa312SAlgea Cao 			return;
79399bfa312SAlgea Cao 
79499bfa312SAlgea Cao 		err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer));
79599bfa312SAlgea Cao 		if (err < 0) {
79699bfa312SAlgea Cao 			dev_err(hdmi->dev, "Failed to pack vendor infoframe: %zd\n",
79799bfa312SAlgea Cao 				err);
79899bfa312SAlgea Cao 			return;
79999bfa312SAlgea Cao 		}
8002afea1f0SAlgea Cao 	}
80199bfa312SAlgea Cao 
80299bfa312SAlgea Cao 	/* vsi header */
80399bfa312SAlgea Cao 	val = (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
80499bfa312SAlgea Cao 	hdmi_writel(hdmi, val, PKT_VSI_CONTENTS0);
80599bfa312SAlgea Cao 
80699bfa312SAlgea Cao 	reg = PKT_VSI_CONTENTS1;
80799bfa312SAlgea Cao 	for (i = 3; i < err; i++) {
80899bfa312SAlgea Cao 		if (i % 4 == 3)
80999bfa312SAlgea Cao 			val = buffer[i];
81099bfa312SAlgea Cao 		if (i % 4 == 0)
81199bfa312SAlgea Cao 			val |= buffer[i] << 8;
81299bfa312SAlgea Cao 		if (i % 4 == 1)
81399bfa312SAlgea Cao 			val |= buffer[i] << 16;
81499bfa312SAlgea Cao 		if (i % 4 == 2)
81599bfa312SAlgea Cao 			val |= buffer[i] << 24;
81699bfa312SAlgea Cao 
81799bfa312SAlgea Cao 		if ((i % 4 == 2) || (i == (err - 1))) {
81899bfa312SAlgea Cao 			hdmi_writel(hdmi, val, reg);
81999bfa312SAlgea Cao 			reg += 4;
82099bfa312SAlgea Cao 		}
82199bfa312SAlgea Cao 	}
82299bfa312SAlgea Cao 
82399bfa312SAlgea Cao 	hdmi_writel(hdmi, 0, PKT_VSI_CONTENTS7);
824cd2307e7SAlgea Cao 
825cd2307e7SAlgea Cao 	hdmi_modb(hdmi, 0, PKTSCHED_VSI_FIELDRATE, PKTSCHED_PKT_CONFIG1);
82699bfa312SAlgea Cao 	hdmi_modb(hdmi, PKTSCHED_VSI_TX_EN, PKTSCHED_VSI_TX_EN,
82799bfa312SAlgea Cao 		  PKTSCHED_PKT_EN);
82899bfa312SAlgea Cao }
82999bfa312SAlgea Cao 
hdmi_config_CVTEM(struct dw_hdmi_qp * hdmi,struct dw_hdmi_link_config * link_cfg)83028671edaSAlgea Cao static void hdmi_config_CVTEM(struct dw_hdmi_qp *hdmi,
83128671edaSAlgea Cao 			      struct dw_hdmi_link_config *link_cfg)
83228671edaSAlgea Cao {
83328671edaSAlgea Cao 	u8 ds_type = 0;
83428671edaSAlgea Cao 	u8 sync = 1;
83528671edaSAlgea Cao 	u8 vfr = 1;
83628671edaSAlgea Cao 	u8 afr = 0;
83728671edaSAlgea Cao 	u8 new = 1;
83828671edaSAlgea Cao 	u8 end = 0;
83928671edaSAlgea Cao 	u8 data_set_length = 136;
84028671edaSAlgea Cao 	u8 hb1[6] = { 0x80, 0, 0, 0, 0, 0x40 };
84128671edaSAlgea Cao 	u8 *pps_body;
84228671edaSAlgea Cao 	u32 val, i, reg;
84328671edaSAlgea Cao 	struct drm_display_mode *mode = &hdmi->previous_mode;
84428671edaSAlgea Cao 	int hsync, hfront, hback;
84528671edaSAlgea Cao 
84628671edaSAlgea Cao 	hdmi_modb(hdmi, 0, PKTSCHED_EMP_CVTEM_TX_EN, PKTSCHED_PKT_EN);
84728671edaSAlgea Cao 
84828671edaSAlgea Cao 	if (!link_cfg->dsc_mode) {
84928671edaSAlgea Cao 		printf("don't use dsc mode\n");
85028671edaSAlgea Cao 		return;
85128671edaSAlgea Cao 	}
85228671edaSAlgea Cao 
85328671edaSAlgea Cao 	pps_body = link_cfg->pps_payload;
85428671edaSAlgea Cao 
85528671edaSAlgea Cao 	hsync = mode->hsync_end - mode->hsync_start;
85628671edaSAlgea Cao 	hback = mode->htotal - mode->hsync_end;
85728671edaSAlgea Cao 	hfront = mode->hsync_start - mode->hdisplay;
85828671edaSAlgea Cao 
85928671edaSAlgea Cao 	for (i = 0; i < 6; i++) {
86028671edaSAlgea Cao 		val = i << 16 | hb1[i] << 8;
86128671edaSAlgea Cao 		hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS0 + i * 0x20);
86228671edaSAlgea Cao 	}
86328671edaSAlgea Cao 
86428671edaSAlgea Cao 	val = new << 7 | end << 6 | ds_type << 4 | afr << 3 |
86528671edaSAlgea Cao 	      vfr << 2 | sync << 1;
86628671edaSAlgea Cao 	hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS1);
86728671edaSAlgea Cao 
86828671edaSAlgea Cao 	val = data_set_length << 16 | pps_body[0] << 24;
86928671edaSAlgea Cao 	hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS2);
87028671edaSAlgea Cao 
87128671edaSAlgea Cao 	reg = PKT0_EMP_CVTEM_CONTENTS3;
87228671edaSAlgea Cao 	for (i = 1; i < 125; i++) {
87328671edaSAlgea Cao 		if (reg == PKT1_EMP_CVTEM_CONTENTS0 ||
87428671edaSAlgea Cao 		    reg == PKT2_EMP_CVTEM_CONTENTS0 ||
87528671edaSAlgea Cao 		    reg == PKT3_EMP_CVTEM_CONTENTS0 ||
87628671edaSAlgea Cao 		    reg == PKT4_EMP_CVTEM_CONTENTS0 ||
87728671edaSAlgea Cao 		    reg == PKT5_EMP_CVTEM_CONTENTS0) {
87828671edaSAlgea Cao 			reg += 4;
87928671edaSAlgea Cao 			i--;
88028671edaSAlgea Cao 			continue;
88128671edaSAlgea Cao 		}
88228671edaSAlgea Cao 		if (i % 4 == 1)
88328671edaSAlgea Cao 			val = pps_body[i];
88428671edaSAlgea Cao 		if (i % 4 == 2)
88528671edaSAlgea Cao 			val |= pps_body[i] << 8;
88628671edaSAlgea Cao 		if (i % 4 == 3)
88728671edaSAlgea Cao 			val |= pps_body[i] << 16;
88828671edaSAlgea Cao 		if (!(i % 4)) {
88928671edaSAlgea Cao 			val |= pps_body[i] << 24;
89028671edaSAlgea Cao 			hdmi_writel(hdmi, val, reg);
89128671edaSAlgea Cao 			reg += 4;
89228671edaSAlgea Cao 		}
89328671edaSAlgea Cao 	}
89428671edaSAlgea Cao 
89528671edaSAlgea Cao 	val = (hfront & 0xff) << 24 | pps_body[127] << 16 |
89628671edaSAlgea Cao 	      pps_body[126] << 8 | pps_body[125];
89728671edaSAlgea Cao 	hdmi_writel(hdmi, val, PKT4_EMP_CVTEM_CONTENTS6);
89828671edaSAlgea Cao 
89928671edaSAlgea Cao 	val = (hback & 0xff) << 24 | ((hsync >> 8) & 0xff) << 16 |
90028671edaSAlgea Cao 	      (hsync & 0xff) << 8 | ((hfront >> 8) & 0xff);
90128671edaSAlgea Cao 	hdmi_writel(hdmi, val, PKT4_EMP_CVTEM_CONTENTS7);
90228671edaSAlgea Cao 
90328671edaSAlgea Cao 	val = link_cfg->hcactive << 8 | ((hback >> 8) & 0xff);
90428671edaSAlgea Cao 	hdmi_writel(hdmi, val, PKT5_EMP_CVTEM_CONTENTS1);
90528671edaSAlgea Cao 
90628671edaSAlgea Cao 	for (i = PKT5_EMP_CVTEM_CONTENTS2; i <= PKT5_EMP_CVTEM_CONTENTS7; i += 4)
90728671edaSAlgea Cao 		hdmi_writel(hdmi, 0, i);
90828671edaSAlgea Cao 
90928671edaSAlgea Cao 	hdmi_modb(hdmi, PKTSCHED_EMP_CVTEM_TX_EN, PKTSCHED_EMP_CVTEM_TX_EN,
91028671edaSAlgea Cao 		  PKTSCHED_PKT_EN);
91128671edaSAlgea Cao }
91228671edaSAlgea Cao 
hdmi_set_frl_mask(int frl_rate)91328671edaSAlgea Cao static int hdmi_set_frl_mask(int frl_rate)
91428671edaSAlgea Cao {
91528671edaSAlgea Cao 	switch (frl_rate) {
91628671edaSAlgea Cao 	case 48:
91728671edaSAlgea Cao 		return FRL_12GBPS_4LANE;
91828671edaSAlgea Cao 	case 40:
91928671edaSAlgea Cao 		return FRL_10GBPS_4LANE;
92028671edaSAlgea Cao 	case 32:
92128671edaSAlgea Cao 		return FRL_8GBPS_4LANE;
92228671edaSAlgea Cao 	case 24:
92328671edaSAlgea Cao 		return FRL_6GBPS_4LANE;
92428671edaSAlgea Cao 	case 18:
92528671edaSAlgea Cao 		return FRL_6GBPS_3LANE;
92628671edaSAlgea Cao 	case 9:
92728671edaSAlgea Cao 		return FRL_3GBPS_3LANE;
92828671edaSAlgea Cao 	}
92928671edaSAlgea Cao 
93028671edaSAlgea Cao 	return 0;
93128671edaSAlgea Cao }
93228671edaSAlgea Cao 
hdmi_start_flt(struct dw_hdmi_qp * hdmi,u8 rate)93328671edaSAlgea Cao static int hdmi_start_flt(struct dw_hdmi_qp *hdmi, u8 rate)
93428671edaSAlgea Cao {
93528671edaSAlgea Cao 	u8 val;
93628671edaSAlgea Cao 	u32 value;
93728671edaSAlgea Cao 	u8 ffe_lv = 0;
93828671edaSAlgea Cao 	int i = 0;
93928671edaSAlgea Cao 	bool ltsp = false;
94028671edaSAlgea Cao 
941d017606bSAlgea Cao 	hdmi_modb(hdmi, AVP_DATAPATH_VIDEO_SWDISABLE,
942d017606bSAlgea Cao 		  AVP_DATAPATH_VIDEO_SWDISABLE, GLOBAL_SWDISABLE);
943d017606bSAlgea Cao 
9448c597bcaSAlgea Cao 	hdmi_writel(hdmi, AVP_DATAPATH_SWINIT_P, GLOBAL_SWRESET_REQUEST);
9458c597bcaSAlgea Cao 
946d017606bSAlgea Cao 	/* clear flt flags */
947d017606bSAlgea Cao 	drm_scdc_writeb(&hdmi->adap, 0x10, 0xff);
948d017606bSAlgea Cao 
94928671edaSAlgea Cao 	/* FLT_READY & FFE_LEVELS read */
95028671edaSAlgea Cao 	for (i = 0; i < 20; i++) {
95128671edaSAlgea Cao 		drm_scdc_readb(&hdmi->adap, SCDC_STATUS_FLAGS_0, &val);
95228671edaSAlgea Cao 		if (val & BIT(6))
95328671edaSAlgea Cao 			break;
95428671edaSAlgea Cao 		mdelay(20);
95528671edaSAlgea Cao 	}
95628671edaSAlgea Cao 
95728671edaSAlgea Cao 	if (i == 20) {
958d017606bSAlgea Cao 		printf("sink flt isn't ready\n");
95928671edaSAlgea Cao 		return -EINVAL;
96028671edaSAlgea Cao 	}
96128671edaSAlgea Cao 
96228671edaSAlgea Cao 	/* max ffe level 3 */
96328671edaSAlgea Cao 	val = 0 << 4 | hdmi_set_frl_mask(rate);
96428671edaSAlgea Cao 	drm_scdc_writeb(&hdmi->adap, 0x31, val);
96528671edaSAlgea Cao 	/* select FRL_RATE & FFE_LEVELS */
96628671edaSAlgea Cao 	hdmi_writel(hdmi, ffe_lv, FLT_CONFIG0);
96728671edaSAlgea Cao 
968d017606bSAlgea Cao 	i = 500;
969d017606bSAlgea Cao 	while (i--) {
97028671edaSAlgea Cao 		mdelay(4);
97128671edaSAlgea Cao 		drm_scdc_readb(&hdmi->adap, 0x10, &val);
97228671edaSAlgea Cao 
97328671edaSAlgea Cao 		if (!(val & 0x30))
97428671edaSAlgea Cao 			continue;
97528671edaSAlgea Cao 
97628671edaSAlgea Cao 		if (val & BIT(5)) {
97728671edaSAlgea Cao 			u8 reg_val, ln0, ln1, ln2, ln3;
97828671edaSAlgea Cao 
97928671edaSAlgea Cao 			drm_scdc_readb(&hdmi->adap, 0x41, &reg_val);
98028671edaSAlgea Cao 			ln0 = reg_val & 0xf;
98128671edaSAlgea Cao 			ln1 = (reg_val >> 4) & 0xf;
98228671edaSAlgea Cao 
98328671edaSAlgea Cao 			drm_scdc_readb(&hdmi->adap, 0x42, &reg_val);
98428671edaSAlgea Cao 			ln2 = reg_val & 0xf;
98528671edaSAlgea Cao 			ln3 = (reg_val >> 4) & 0xf;
98628671edaSAlgea Cao 
98728671edaSAlgea Cao 			if (!ln0 && !ln1 && !ln2 && !ln3) {
988d017606bSAlgea Cao 				printf("goto ltsp\n");
98928671edaSAlgea Cao 				ltsp = true;
99028671edaSAlgea Cao 				hdmi_writel(hdmi, 0, FLT_CONFIG1);
99128671edaSAlgea Cao 			} else if ((ln0 == 0xf) | (ln1 == 0xf) | (ln2 == 0xf) | (ln3 == 0xf)) {
992d017606bSAlgea Cao 				printf("goto lts4\n");
99328671edaSAlgea Cao 				break;
99428671edaSAlgea Cao 			} else if ((ln0 == 0xe) | (ln1 == 0xe) | (ln2 == 0xe) | (ln3 == 0xe)) {
995d017606bSAlgea Cao 				printf("goto ffe\n");
99628671edaSAlgea Cao 				break;
99728671edaSAlgea Cao 			} else {
99828671edaSAlgea Cao 				value = (ln3 << 16) | (ln2 << 12) | (ln1 << 8) | (ln0 << 4) | 0xf;
99928671edaSAlgea Cao 				hdmi_writel(hdmi, value, FLT_CONFIG1);
100028671edaSAlgea Cao 			}
100128671edaSAlgea Cao 		}
100228671edaSAlgea Cao 
100328671edaSAlgea Cao 		drm_scdc_writeb(&hdmi->adap, 0x10, val);
100428671edaSAlgea Cao 
100528671edaSAlgea Cao 		if ((val & BIT(4)) && ltsp) {
1006d017606bSAlgea Cao 			hdmi_modb(hdmi, 0, AVP_DATAPATH_VIDEO_SWDISABLE, GLOBAL_SWDISABLE);
100728671edaSAlgea Cao 			printf("flt success\n");
100828671edaSAlgea Cao 			break;
100928671edaSAlgea Cao 		}
101028671edaSAlgea Cao 	}
101128671edaSAlgea Cao 
1012d017606bSAlgea Cao 	if (i < 0) {
1013d017606bSAlgea Cao 		printf("flt time out\n");
1014d017606bSAlgea Cao 		return -ETIMEDOUT;
1015d017606bSAlgea Cao 	}
1016d017606bSAlgea Cao 
101728671edaSAlgea Cao 	return 0;
101828671edaSAlgea Cao }
101928671edaSAlgea Cao 
102028671edaSAlgea Cao #define HDMI_MODE_FRL_MASK     BIT(30)
102128671edaSAlgea Cao 
hdmi_set_op_mode(struct dw_hdmi_qp * hdmi,struct dw_hdmi_link_config * link_cfg,struct display_state * state,struct rockchip_connector * conn)102228671edaSAlgea Cao static void hdmi_set_op_mode(struct dw_hdmi_qp *hdmi,
102328671edaSAlgea Cao 			     struct dw_hdmi_link_config *link_cfg,
10248c597bcaSAlgea Cao 			     struct display_state *state,
10258c597bcaSAlgea Cao 			     struct rockchip_connector *conn)
102628671edaSAlgea Cao {
102728671edaSAlgea Cao 	int frl_rate;
10288c597bcaSAlgea Cao 	int i, ret;
102928671edaSAlgea Cao 
103028671edaSAlgea Cao 	if (!link_cfg->frl_mode) {
103128671edaSAlgea Cao 		printf("dw hdmi qp use tmds mode\n");
103228671edaSAlgea Cao 		hdmi_modb(hdmi, 0, OPMODE_FRL, LINK_CONFIG0);
103328671edaSAlgea Cao 		hdmi_modb(hdmi, 0, OPMODE_FRL_4LANES, LINK_CONFIG0);
10348c597bcaSAlgea Cao 		hdmi->phy.ops->init(conn, hdmi->rk_hdmi, state);
10358c597bcaSAlgea Cao 		hdmi->phy.enabled = true;
103628671edaSAlgea Cao 		return;
103728671edaSAlgea Cao 	}
103828671edaSAlgea Cao 
103928671edaSAlgea Cao 	if (link_cfg->frl_lanes == 4)
104028671edaSAlgea Cao 		hdmi_modb(hdmi, OPMODE_FRL_4LANES, OPMODE_FRL_4LANES,
104128671edaSAlgea Cao 			  LINK_CONFIG0);
104228671edaSAlgea Cao 	else
104328671edaSAlgea Cao 		hdmi_modb(hdmi, 0, OPMODE_FRL_4LANES, LINK_CONFIG0);
104428671edaSAlgea Cao 
104528671edaSAlgea Cao 	hdmi_modb(hdmi, 1, OPMODE_FRL, LINK_CONFIG0);
104628671edaSAlgea Cao 
104728671edaSAlgea Cao 	frl_rate = link_cfg->frl_lanes * link_cfg->rate_per_lane;
10488c597bcaSAlgea Cao 	hdmi->phy.ops->init(conn, hdmi->rk_hdmi, state);
10498c597bcaSAlgea Cao 	hdmi->phy.enabled = true;
10508c597bcaSAlgea Cao 
10518c597bcaSAlgea Cao 	mdelay(200);
10528c597bcaSAlgea Cao 	ret = hdmi_start_flt(hdmi, frl_rate);
10538c597bcaSAlgea Cao 	if (ret) {
10548c597bcaSAlgea Cao 		hdmi_writel(hdmi, 0, FLT_CONFIG0);
10558c597bcaSAlgea Cao 		drm_scdc_writeb(&hdmi->adap, 0x31, 0);
10568c597bcaSAlgea Cao 		hdmi_modb(hdmi, 0, AVP_DATAPATH_VIDEO_SWDISABLE, GLOBAL_SWDISABLE);
10578c597bcaSAlgea Cao 		return;
10588c597bcaSAlgea Cao 	}
1059cebdc49bSAlgea Cao 
1060d017606bSAlgea Cao 	for (i = 0; i < 200; i++) {
1061cebdc49bSAlgea Cao 		hdmi_modb(hdmi, PKTSCHED_NULL_TX_EN, PKTSCHED_NULL_TX_EN, PKTSCHED_PKT_EN);
1062d017606bSAlgea Cao 		udelay(50);
1063cebdc49bSAlgea Cao 		hdmi_modb(hdmi, 0, PKTSCHED_NULL_TX_EN, PKTSCHED_PKT_EN);
1064d017606bSAlgea Cao 		udelay(50);
1065cebdc49bSAlgea Cao 	}
106628671edaSAlgea Cao }
106728671edaSAlgea Cao 
dw_hdmi_setup(struct dw_hdmi_qp * hdmi,struct rockchip_connector * conn,struct drm_display_mode * mode,struct display_state * state)106828671edaSAlgea Cao static int dw_hdmi_setup(struct dw_hdmi_qp *hdmi,
10690594ce39SZhang Yubing 			 struct rockchip_connector *conn,
107028671edaSAlgea Cao 			 struct drm_display_mode *mode,
107128671edaSAlgea Cao 			 struct display_state *state)
107228671edaSAlgea Cao {
107328671edaSAlgea Cao 	int ret;
107428671edaSAlgea Cao 	void *data = hdmi->plat_data->phy_data;
107528671edaSAlgea Cao 	struct dw_hdmi_link_config *link_cfg;
107628671edaSAlgea Cao 	struct drm_hdmi_info *hdmi_info = &hdmi->edid_data.display_info.hdmi;
107728671edaSAlgea Cao 	struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
107828671edaSAlgea Cao 	u8 bytes = 0;
107928671edaSAlgea Cao 
108028671edaSAlgea Cao 	if (!hdmi->vic)
108128671edaSAlgea Cao 		printf("Non-CEA mode used in HDMI\n");
108228671edaSAlgea Cao 	else
108328671edaSAlgea Cao 		printf("CEA mode used vic=%d\n", hdmi->vic);
108428671edaSAlgea Cao 
108592d234f7SAlgea Cao 	vmode->mpixelclock = mode->clock * 1000;
108628671edaSAlgea Cao 	vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock);
108728671edaSAlgea Cao 	if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
108828671edaSAlgea Cao 		vmode->mtmdsclock /= 2;
108928671edaSAlgea Cao 	printf("mtmdsclock:%d\n", vmode->mtmdsclock);
109028671edaSAlgea Cao 
109128671edaSAlgea Cao 	if (hdmi->plat_data->get_enc_out_encoding)
109228671edaSAlgea Cao 		hdmi->hdmi_data.enc_out_encoding =
109328671edaSAlgea Cao 			hdmi->plat_data->get_enc_out_encoding(data);
109428671edaSAlgea Cao 	else if (hdmi->vic == 6 || hdmi->vic == 7 ||
109528671edaSAlgea Cao 		 hdmi->vic == 21 || hdmi->vic == 22 ||
109628671edaSAlgea Cao 		 hdmi->vic == 2 || hdmi->vic == 3 ||
109728671edaSAlgea Cao 		 hdmi->vic == 17 || hdmi->vic == 18)
109828671edaSAlgea Cao 		hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601;
109928671edaSAlgea Cao 	else
110028671edaSAlgea Cao 		hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709;
110128671edaSAlgea Cao 
110228671edaSAlgea Cao 	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
110328671edaSAlgea Cao 		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
110428671edaSAlgea Cao 		hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 1;
110528671edaSAlgea Cao 	} else {
110628671edaSAlgea Cao 		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
110728671edaSAlgea Cao 		hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
110828671edaSAlgea Cao 	}
110928671edaSAlgea Cao 
111028671edaSAlgea Cao 	/* TOFIX: Get input encoding from plat data or fallback to none */
111128671edaSAlgea Cao 	if (hdmi->plat_data->get_enc_in_encoding)
111228671edaSAlgea Cao 		hdmi->hdmi_data.enc_in_encoding =
111328671edaSAlgea Cao 			hdmi->plat_data->get_enc_in_encoding(data);
111428671edaSAlgea Cao 	else if (hdmi->plat_data->input_bus_encoding)
111528671edaSAlgea Cao 		hdmi->hdmi_data.enc_in_encoding =
111628671edaSAlgea Cao 			hdmi->plat_data->input_bus_encoding;
111728671edaSAlgea Cao 	else
111828671edaSAlgea Cao 		hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT;
111928671edaSAlgea Cao 
112028671edaSAlgea Cao 	if (hdmi->plat_data->get_quant_range)
112128671edaSAlgea Cao 		hdmi->hdmi_data.quant_range =
112228671edaSAlgea Cao 			hdmi->plat_data->get_quant_range(data);
112328671edaSAlgea Cao 	else
112428671edaSAlgea Cao 		hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
112528671edaSAlgea Cao 
112628671edaSAlgea Cao 	/*
112728671edaSAlgea Cao 	 * According to the dw-hdmi specification 6.4.2
112828671edaSAlgea Cao 	 * vp_pr_cd[3:0]:
112928671edaSAlgea Cao 	 * 0000b: No pixel repetition (pixel sent only once)
113028671edaSAlgea Cao 	 * 0001b: Pixel sent two times (pixel repeated once)
113128671edaSAlgea Cao 	 */
113228671edaSAlgea Cao 	hdmi->hdmi_data.pix_repet_factor =
113328671edaSAlgea Cao 		(mode->flags & DRM_MODE_FLAG_DBLCLK) ? 1 : 0;
113428671edaSAlgea Cao 	hdmi->hdmi_data.video_mode.mdataenablepolarity = true;
113528671edaSAlgea Cao 
113628671edaSAlgea Cao 	/* HDMI Initialization Step B.2 */
11378c597bcaSAlgea Cao 	hdmi->phy.ops->set_pll(conn, hdmi->rk_hdmi, state);
113828671edaSAlgea Cao 
1139bc291652SAlgea Cao 	/* Mark yuv422 10bit */
1140bc291652SAlgea Cao 	if (hdmi->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_YUYV10_1X20)
1141bc291652SAlgea Cao 		hdmi_writel(hdmi, BIT(20), VIDEO_INTERFACE_CONFIG0);
1142463abfccSAlgea Cao 	dw_hdmi_qp_set_grf_cfg(hdmi->rk_hdmi);
114328671edaSAlgea Cao 	link_cfg = dw_hdmi_rockchip_get_link_cfg(hdmi->rk_hdmi);
114428671edaSAlgea Cao 
114528671edaSAlgea Cao 	/* not for DVI mode */
114628671edaSAlgea Cao 	if (hdmi->sink_is_hdmi) {
114728671edaSAlgea Cao 		printf("%s HDMI mode\n", __func__);
114828671edaSAlgea Cao 		hdmi_modb(hdmi, 0, OPMODE_DVI, LINK_CONFIG0);
114928671edaSAlgea Cao 		hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
11508c597bcaSAlgea Cao 		hdmi_modb(hdmi, KEEPOUT_REKEY_ALWAYS, KEEPOUT_REKEY_CFG, FRAME_COMPOSER_CONFIG9);
11518c597bcaSAlgea Cao 		hdmi_writel(hdmi, 0, FLT_CONFIG0);
11528c597bcaSAlgea Cao 		if (hdmi_info->scdc.supported)
11538c597bcaSAlgea Cao 			drm_scdc_writeb(&hdmi->adap, 0x31, 0);
115428671edaSAlgea Cao 		if (!link_cfg->frl_mode) {
115528671edaSAlgea Cao 			if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK) {
115628671edaSAlgea Cao 				drm_scdc_readb(&hdmi->adap, SCDC_SINK_VERSION, &bytes);
115728671edaSAlgea Cao 				drm_scdc_writeb(&hdmi->adap, SCDC_SOURCE_VERSION,
115828671edaSAlgea Cao 						min_t(u8, bytes, SCDC_MIN_SOURCE_VERSION));
115928671edaSAlgea Cao 				drm_scdc_set_high_tmds_clock_ratio(&hdmi->adap, 1);
116028671edaSAlgea Cao 				drm_scdc_set_scrambling(&hdmi->adap, 1);
116128671edaSAlgea Cao 				hdmi_writel(hdmi, 1, SCRAMB_CONFIG0);
11628c597bcaSAlgea Cao 				mdelay(100);
116328671edaSAlgea Cao 			} else {
116428671edaSAlgea Cao 				if (hdmi_info->scdc.supported) {
116528671edaSAlgea Cao 					drm_scdc_set_high_tmds_clock_ratio(&hdmi->adap, 0);
116628671edaSAlgea Cao 					drm_scdc_set_scrambling(&hdmi->adap, 0);
116728671edaSAlgea Cao 				}
116828671edaSAlgea Cao 				hdmi_writel(hdmi, 0, SCRAMB_CONFIG0);
116928671edaSAlgea Cao 			}
117028671edaSAlgea Cao 		}
117128671edaSAlgea Cao 		/* HDMI Initialization Step F - Configure AVI InfoFrame */
117228671edaSAlgea Cao 		hdmi_config_AVI(hdmi, mode);
117399bfa312SAlgea Cao 		hdmi_config_vendor_specific_infoframe(hdmi, mode);
117428671edaSAlgea Cao 		hdmi_config_CVTEM(hdmi, link_cfg);
11758c597bcaSAlgea Cao 		hdmi_set_op_mode(hdmi, link_cfg, state, conn);
1176a14cbdd6SAlgea Cao 		/* clear avmute */
11778c597bcaSAlgea Cao 		mdelay(50);
1178a14cbdd6SAlgea Cao 		hdmi_writel(hdmi, 2, PKTSCHED_PKT_CONTROL0);
11798c597bcaSAlgea Cao 		hdmi_modb(hdmi, PKTSCHED_GCP_TX_EN, PKTSCHED_GCP_TX_EN,
11808c597bcaSAlgea Cao 			  PKTSCHED_PKT_EN);
118128671edaSAlgea Cao 	} else {
118228671edaSAlgea Cao 		hdmi_modb(hdmi, OPMODE_DVI, OPMODE_DVI, LINK_CONFIG0);
11838c597bcaSAlgea Cao 		ret = hdmi->phy.ops->init(conn, hdmi->rk_hdmi, state);
11848c597bcaSAlgea Cao 		if (ret)
11858c597bcaSAlgea Cao 			return ret;
11868c597bcaSAlgea Cao 		hdmi->phy.enabled = true;
118728671edaSAlgea Cao 		printf("%s DVI mode\n", __func__);
118828671edaSAlgea Cao 	}
118928671edaSAlgea Cao 
1190c3c14736SAlgea Cao 	/* Mark uboot hdmi is enabled */
1191c3c14736SAlgea Cao 	hdmi_writel(hdmi, BIT(21), VIDEO_INTERFACE_CONFIG0);
1192c3c14736SAlgea Cao 
119328671edaSAlgea Cao 	return 0;
119428671edaSAlgea Cao }
119528671edaSAlgea Cao 
dw_hdmi_detect_hotplug(struct dw_hdmi_qp * hdmi,struct display_state * state)119628671edaSAlgea Cao int dw_hdmi_detect_hotplug(struct dw_hdmi_qp *hdmi,
119728671edaSAlgea Cao 			   struct display_state *state)
119828671edaSAlgea Cao {
119928671edaSAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
1200af006bcbSChen Shunqing 	struct rockchip_connector *conn = conn_state->connector;
120128671edaSAlgea Cao 	int ret;
120228671edaSAlgea Cao 
120328671edaSAlgea Cao 	ret = hdmi->phy.ops->read_hpd(hdmi->rk_hdmi);
1204af006bcbSChen Shunqing 	if (!ret) {
1205af006bcbSChen Shunqing 		if (conn->bridge)
1206af006bcbSChen Shunqing 			ret = rockchip_bridge_detect(conn->bridge);
1207af006bcbSChen Shunqing 	}
1208af006bcbSChen Shunqing 
1209a12a16bbSAlgea Cao 	if (ret || state->force_output) {
121028671edaSAlgea Cao 		if (!hdmi->id)
121128671edaSAlgea Cao 			conn_state->output_if |= VOP_OUTPUT_IF_HDMI0;
121228671edaSAlgea Cao 		else
121328671edaSAlgea Cao 			conn_state->output_if |= VOP_OUTPUT_IF_HDMI1;
121428671edaSAlgea Cao 	}
121528671edaSAlgea Cao 
121628671edaSAlgea Cao 	return ret;
121728671edaSAlgea Cao }
121828671edaSAlgea Cao 
rockchip_dw_hdmi_qp_init(struct rockchip_connector * conn,struct display_state * state)12190594ce39SZhang Yubing int rockchip_dw_hdmi_qp_init(struct rockchip_connector *conn, struct display_state *state)
122028671edaSAlgea Cao {
122128671edaSAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
12220594ce39SZhang Yubing 	const struct dw_hdmi_plat_data *pdata =
12230594ce39SZhang Yubing 		(const struct dw_hdmi_plat_data *)dev_get_driver_data(conn->dev);
12240594ce39SZhang Yubing 	void *rk_hdmi = dev_get_priv(conn->dev);
122528671edaSAlgea Cao 	struct dw_hdmi_qp *hdmi;
122628671edaSAlgea Cao 	struct drm_display_mode *mode_buf;
12270594ce39SZhang Yubing 	ofnode hdmi_node = conn->dev->node;
122828671edaSAlgea Cao 	struct device_node *ddc_node;
122928671edaSAlgea Cao 
123028671edaSAlgea Cao 	hdmi = malloc(sizeof(struct dw_hdmi_qp));
123128671edaSAlgea Cao 	if (!hdmi)
123228671edaSAlgea Cao 		return -ENOMEM;
123328671edaSAlgea Cao 
123428671edaSAlgea Cao 	memset(hdmi, 0, sizeof(struct dw_hdmi_qp));
123528671edaSAlgea Cao 	mode_buf = malloc(MODE_LEN * sizeof(struct drm_display_mode));
123628671edaSAlgea Cao 	if (!mode_buf)
123728671edaSAlgea Cao 		return -ENOMEM;
123828671edaSAlgea Cao 
123928671edaSAlgea Cao 	hdmi->rk_hdmi = rk_hdmi;
124028671edaSAlgea Cao 	hdmi->id = of_alias_get_id(ofnode_to_np(hdmi_node), "hdmi");
124128671edaSAlgea Cao 	if (hdmi->id < 0)
124228671edaSAlgea Cao 		hdmi->id = 0;
124328671edaSAlgea Cao 	conn_state->disp_info = rockchip_get_disp_info(conn_state->type, hdmi->id);
124428671edaSAlgea Cao 
124528671edaSAlgea Cao 	memset(mode_buf, 0, MODE_LEN * sizeof(struct drm_display_mode));
124628671edaSAlgea Cao 
12470594ce39SZhang Yubing 	hdmi->regs = dev_read_addr_ptr(conn->dev);
124828671edaSAlgea Cao 
124928671edaSAlgea Cao 	ddc_node = of_parse_phandle(ofnode_to_np(hdmi_node), "ddc-i2c-bus", 0);
125028671edaSAlgea Cao 	if (ddc_node) {
125128671edaSAlgea Cao 		uclass_get_device_by_ofnode(UCLASS_I2C, np_to_ofnode(ddc_node),
125228671edaSAlgea Cao 					    &hdmi->adap.i2c_bus);
125328671edaSAlgea Cao 		if (hdmi->adap.i2c_bus)
125428671edaSAlgea Cao 			hdmi->adap.ops = i2c_get_ops(hdmi->adap.i2c_bus);
125528671edaSAlgea Cao 	}
125628671edaSAlgea Cao 
125728671edaSAlgea Cao 	hdmi->i2c = malloc(sizeof(struct dw_hdmi_i2c));
125828671edaSAlgea Cao 	if (!hdmi->i2c)
125928671edaSAlgea Cao 		return -ENOMEM;
126028671edaSAlgea Cao 	hdmi->adap.ddc_xfer = dw_hdmi_i2c_xfer;
126128671edaSAlgea Cao 
126228671edaSAlgea Cao 	/*
126328671edaSAlgea Cao 	 * Read high and low time from device tree. If not available use
126428671edaSAlgea Cao 	 * the default timing scl clock rate is about 99.6KHz.
126528671edaSAlgea Cao 	 */
126628671edaSAlgea Cao 	hdmi->i2c->scl_high_ns =
126728671edaSAlgea Cao 		ofnode_read_s32_default(hdmi_node,
126828671edaSAlgea Cao 					"ddc-i2c-scl-high-time-ns", 4708);
126928671edaSAlgea Cao 	hdmi->i2c->scl_low_ns =
127028671edaSAlgea Cao 		ofnode_read_s32_default(hdmi_node,
127128671edaSAlgea Cao 					"ddc-i2c-scl-low-time-ns", 4916);
127228671edaSAlgea Cao 
127328671edaSAlgea Cao 	dw_hdmi_i2c_init(hdmi);
127428671edaSAlgea Cao 	conn_state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
127528671edaSAlgea Cao 
127628671edaSAlgea Cao 	hdmi->dev_type = pdata->dev_type;
127728671edaSAlgea Cao 	hdmi->plat_data = pdata;
127828671edaSAlgea Cao 	hdmi->edid_data.mode_buf = mode_buf;
127928671edaSAlgea Cao 
12800594ce39SZhang Yubing 	conn->data = hdmi;
128128671edaSAlgea Cao 
128228671edaSAlgea Cao 	dw_hdmi_detect_phy(hdmi);
128328671edaSAlgea Cao 	hdmi_writel(hdmi, 0, MAINUNIT_0_INT_MASK_N);
128428671edaSAlgea Cao 	hdmi_writel(hdmi, 0, MAINUNIT_1_INT_MASK_N);
128528671edaSAlgea Cao 	hdmi_writel(hdmi, 428571429, TIMER_BASE_CONFIG0);
128628671edaSAlgea Cao 
1287463abfccSAlgea Cao 	dw_hdmi_qp_io_path_init(hdmi->rk_hdmi);
128828671edaSAlgea Cao 
128928671edaSAlgea Cao 	return 0;
129028671edaSAlgea Cao }
129128671edaSAlgea Cao 
rockchip_dw_hdmi_qp_deinit(struct rockchip_connector * conn,struct display_state * state)12920594ce39SZhang Yubing void rockchip_dw_hdmi_qp_deinit(struct rockchip_connector *conn, struct display_state *state)
129328671edaSAlgea Cao {
12940594ce39SZhang Yubing 	struct dw_hdmi_qp *hdmi = conn->data;
129528671edaSAlgea Cao 
129628671edaSAlgea Cao 	if (hdmi->i2c)
129728671edaSAlgea Cao 		free(hdmi->i2c);
129828671edaSAlgea Cao 	if (hdmi->edid_data.mode_buf)
129928671edaSAlgea Cao 		free(hdmi->edid_data.mode_buf);
130028671edaSAlgea Cao 	if (hdmi)
130128671edaSAlgea Cao 		free(hdmi);
130228671edaSAlgea Cao }
130328671edaSAlgea Cao 
rockchip_dw_hdmi_qp_config_output(struct rockchip_connector * conn,struct display_state * state)1304af006bcbSChen Shunqing static void rockchip_dw_hdmi_qp_config_output(struct rockchip_connector *conn,
1305af006bcbSChen Shunqing 					      struct display_state *state)
1306af006bcbSChen Shunqing {
1307af006bcbSChen Shunqing 	struct connector_state *conn_state = &state->conn_state;
1308af006bcbSChen Shunqing 	struct drm_display_mode *mode = &conn_state->mode;
1309af006bcbSChen Shunqing 	struct dw_hdmi_qp *hdmi = conn->data;
1310af006bcbSChen Shunqing 	unsigned int bus_format;
1311af006bcbSChen Shunqing 	unsigned long enc_out_encoding;
1312af006bcbSChen Shunqing 	struct overscan *overscan = &conn_state->overscan;
1313af006bcbSChen Shunqing 
1314463abfccSAlgea Cao 	dw_hdmi_qp_select_output(&hdmi->edid_data, conn, &bus_format,
1315af006bcbSChen Shunqing 				 overscan, hdmi->dev_type,
1316af006bcbSChen Shunqing 				 hdmi->output_bus_format_rgb, hdmi->rk_hdmi,
1317af006bcbSChen Shunqing 				 state);
1318af006bcbSChen Shunqing 
1319af006bcbSChen Shunqing 	*mode = *hdmi->edid_data.preferred_mode;
1320af006bcbSChen Shunqing 	hdmi->vic = drm_match_cea_mode(mode);
1321af006bcbSChen Shunqing 
1322af006bcbSChen Shunqing 	printf("mode:%dx%d bus_format:0x%x\n", mode->hdisplay, mode->vdisplay, bus_format);
1323af006bcbSChen Shunqing 	conn_state->bus_format = bus_format;
1324af006bcbSChen Shunqing 	hdmi->hdmi_data.enc_in_bus_format = bus_format;
1325af006bcbSChen Shunqing 	hdmi->hdmi_data.enc_out_bus_format = bus_format;
1326af006bcbSChen Shunqing 
1327af006bcbSChen Shunqing 	switch (bus_format) {
1328af006bcbSChen Shunqing 	case MEDIA_BUS_FMT_YUYV10_1X20:
1329af006bcbSChen Shunqing 		conn_state->bus_format = MEDIA_BUS_FMT_YUYV10_1X20;
1330af006bcbSChen Shunqing 		hdmi->hdmi_data.enc_in_bus_format =
1331af006bcbSChen Shunqing 			MEDIA_BUS_FMT_YUYV10_1X20;
1332af006bcbSChen Shunqing 		conn_state->output_mode = ROCKCHIP_OUT_MODE_YUV422;
1333af006bcbSChen Shunqing 		break;
1334af006bcbSChen Shunqing 	case MEDIA_BUS_FMT_YUYV8_1X16:
1335af006bcbSChen Shunqing 		conn_state->bus_format = MEDIA_BUS_FMT_YUYV8_1X16;
1336af006bcbSChen Shunqing 		hdmi->hdmi_data.enc_in_bus_format =
1337af006bcbSChen Shunqing 			MEDIA_BUS_FMT_YUYV8_1X16;
1338af006bcbSChen Shunqing 		conn_state->output_mode = ROCKCHIP_OUT_MODE_YUV422;
1339af006bcbSChen Shunqing 		break;
1340af006bcbSChen Shunqing 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
1341af006bcbSChen Shunqing 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
1342af006bcbSChen Shunqing 		conn_state->output_mode = ROCKCHIP_OUT_MODE_YUV420;
1343af006bcbSChen Shunqing 		break;
1344af006bcbSChen Shunqing 	}
1345af006bcbSChen Shunqing 
1346af006bcbSChen Shunqing 	if (hdmi->vic == 6 || hdmi->vic == 7 || hdmi->vic == 21 ||
1347af006bcbSChen Shunqing 	    hdmi->vic == 22 || hdmi->vic == 2 || hdmi->vic == 3 ||
1348af006bcbSChen Shunqing 	    hdmi->vic == 17 || hdmi->vic == 18)
1349af006bcbSChen Shunqing 		enc_out_encoding = V4L2_YCBCR_ENC_601;
1350af006bcbSChen Shunqing 	else
1351af006bcbSChen Shunqing 		enc_out_encoding = V4L2_YCBCR_ENC_709;
1352af006bcbSChen Shunqing 
1353af006bcbSChen Shunqing 	if (enc_out_encoding == V4L2_YCBCR_ENC_BT2020)
1354df0a5c43SDamon Ding 		conn_state->color_encoding = DRM_COLOR_YCBCR_BT2020;
1355af006bcbSChen Shunqing 	else if (bus_format == MEDIA_BUS_FMT_RGB888_1X24 ||
1356af006bcbSChen Shunqing 		 bus_format == MEDIA_BUS_FMT_RGB101010_1X30)
1357df0a5c43SDamon Ding 		conn_state->color_encoding = DRM_COLOR_YCBCR_BT709;
1358af006bcbSChen Shunqing 	else if (enc_out_encoding == V4L2_YCBCR_ENC_709)
1359df0a5c43SDamon Ding 		conn_state->color_encoding = DRM_COLOR_YCBCR_BT709;
1360af006bcbSChen Shunqing 	else
1361df0a5c43SDamon Ding 		conn_state->color_encoding = DRM_COLOR_YCBCR_BT601;
1362df0a5c43SDamon Ding 
1363df0a5c43SDamon Ding 	if (bus_format == MEDIA_BUS_FMT_RGB888_1X24 ||
1364df0a5c43SDamon Ding 	    bus_format == MEDIA_BUS_FMT_RGB101010_1X30)
1365df0a5c43SDamon Ding 		conn_state->color_range = hdmi->hdmi_data.quant_range ==
1366df0a5c43SDamon Ding 					  HDMI_QUANTIZATION_RANGE_LIMITED ?
1367df0a5c43SDamon Ding 					  DRM_COLOR_YCBCR_LIMITED_RANGE :
1368df0a5c43SDamon Ding 					  DRM_COLOR_YCBCR_FULL_RANGE;
1369df0a5c43SDamon Ding 	else
1370df0a5c43SDamon Ding 		conn_state->color_range = hdmi->hdmi_data.quant_range ==
1371df0a5c43SDamon Ding 					  HDMI_QUANTIZATION_RANGE_FULL ?
1372df0a5c43SDamon Ding 					  DRM_COLOR_YCBCR_FULL_RANGE :
1373df0a5c43SDamon Ding 					  DRM_COLOR_YCBCR_LIMITED_RANGE;
1374af006bcbSChen Shunqing }
1375af006bcbSChen Shunqing 
rockchip_dw_hdmi_qp_prepare(struct rockchip_connector * conn,struct display_state * state)13760594ce39SZhang Yubing int rockchip_dw_hdmi_qp_prepare(struct rockchip_connector *conn, struct display_state *state)
137728671edaSAlgea Cao {
1378af006bcbSChen Shunqing 	struct connector_state *conn_state = &state->conn_state;
1379af006bcbSChen Shunqing 	struct drm_display_mode *mode = &conn_state->mode;
1380af006bcbSChen Shunqing 	struct dw_hdmi_qp *hdmi = conn->data;
1381af006bcbSChen Shunqing 
1382af006bcbSChen Shunqing 	if (!hdmi->edid_data.preferred_mode && conn->bridge) {
1383af006bcbSChen Shunqing 		drm_add_hdmi_modes(&hdmi->edid_data, mode);
1384af006bcbSChen Shunqing 		drm_mode_sort(&hdmi->edid_data);
1385af006bcbSChen Shunqing 		hdmi->sink_is_hdmi = true;
1386af006bcbSChen Shunqing 		hdmi->sink_has_audio = true;
1387af006bcbSChen Shunqing 		rockchip_dw_hdmi_qp_config_output(conn, state);
1388af006bcbSChen Shunqing 	}
1389af006bcbSChen Shunqing 
139028671edaSAlgea Cao 	return 0;
139128671edaSAlgea Cao }
139228671edaSAlgea Cao 
rockchip_dw_hdmi_qp_check(struct rockchip_connector * conn,struct display_state * state)13931d642d95SAlgea Cao int rockchip_dw_hdmi_qp_check(struct rockchip_connector *conn, struct display_state *state)
13941d642d95SAlgea Cao {
13951d642d95SAlgea Cao 	struct crtc_state *cstate = &state->crtc_state;
13961d642d95SAlgea Cao 	struct rockchip_crtc *crtc = cstate->crtc;
13971d642d95SAlgea Cao 	struct dw_hdmi_qp *hdmi = conn->data;
13981d642d95SAlgea Cao 
13991d642d95SAlgea Cao 	/* clear hdmi uboot logo on flag */
14001d642d95SAlgea Cao 	if (crtc->splice_mode && cstate->crtc_id == 1)
14011d642d95SAlgea Cao 		hdmi_writel(hdmi, 0, I2CM_INTERFACE_CONTROL0);
14021d642d95SAlgea Cao 
14031d642d95SAlgea Cao 	return 0;
14041d642d95SAlgea Cao }
14051d642d95SAlgea Cao 
dw_hdmi_disable(struct rockchip_connector * conn,struct dw_hdmi_qp * hdmi,struct display_state * state)14060594ce39SZhang Yubing static void dw_hdmi_disable(struct rockchip_connector *conn, struct dw_hdmi_qp *hdmi,
14070594ce39SZhang Yubing 			    struct display_state *state)
140828671edaSAlgea Cao {
140928671edaSAlgea Cao 	if (hdmi->phy.enabled) {
14100594ce39SZhang Yubing 		hdmi->phy.ops->disable(conn, hdmi->rk_hdmi, state);
141128671edaSAlgea Cao 		hdmi->phy.enabled = false;
141228671edaSAlgea Cao 	}
141328671edaSAlgea Cao }
141428671edaSAlgea Cao 
rockchip_dw_hdmi_qp_enable(struct rockchip_connector * conn,struct display_state * state)14150594ce39SZhang Yubing int rockchip_dw_hdmi_qp_enable(struct rockchip_connector *conn, struct display_state *state)
141628671edaSAlgea Cao {
141728671edaSAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
141828671edaSAlgea Cao 	struct drm_display_mode *mode = &conn_state->mode;
14190594ce39SZhang Yubing 	struct dw_hdmi_qp *hdmi = conn->data;
142028671edaSAlgea Cao 
142128671edaSAlgea Cao 	if (!hdmi)
142228671edaSAlgea Cao 		return -EFAULT;
142328671edaSAlgea Cao 
142428671edaSAlgea Cao 	/* Store the display mode for plugin/DKMS poweron events */
142528671edaSAlgea Cao 	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
142628671edaSAlgea Cao 
14270594ce39SZhang Yubing 	dw_hdmi_setup(hdmi, conn, mode, state);
142828671edaSAlgea Cao 
142928671edaSAlgea Cao 	return 0;
143028671edaSAlgea Cao }
143128671edaSAlgea Cao 
rockchip_dw_hdmi_qp_disable(struct rockchip_connector * conn,struct display_state * state)14320594ce39SZhang Yubing int rockchip_dw_hdmi_qp_disable(struct rockchip_connector *conn, struct display_state *state)
143328671edaSAlgea Cao {
14340594ce39SZhang Yubing 	struct dw_hdmi_qp *hdmi = conn->data;
143528671edaSAlgea Cao 
14360594ce39SZhang Yubing 	dw_hdmi_disable(conn, hdmi, state);
143728671edaSAlgea Cao 	return 0;
143828671edaSAlgea Cao }
143928671edaSAlgea Cao 
rockchip_dw_hdmi_qp_mode_valid(struct dw_hdmi_qp * hdmi)144077c2997fSAlgea Cao static void rockchip_dw_hdmi_qp_mode_valid(struct dw_hdmi_qp *hdmi)
144177c2997fSAlgea Cao {
144277c2997fSAlgea Cao 	struct hdmi_edid_data *edid_data = &hdmi->edid_data;
144377c2997fSAlgea Cao 	int i;
1444200f72c9SAlgea Cao 	bool enable_gpio = dw_hdmi_qp_check_enable_gpio(hdmi->rk_hdmi);
144577c2997fSAlgea Cao 
144677c2997fSAlgea Cao 	for (i = 0; i < edid_data->modes; i++) {
144777c2997fSAlgea Cao 		if (edid_data->mode_buf[i].invalid)
144877c2997fSAlgea Cao 			continue;
1449200f72c9SAlgea Cao 
145077c2997fSAlgea Cao 		if (edid_data->mode_buf[i].clock <= 25000)
145177c2997fSAlgea Cao 			edid_data->mode_buf[i].invalid = true;
1452200f72c9SAlgea Cao 
1453200f72c9SAlgea Cao 		if (edid_data->mode_buf[i].clock > 600000 && !enable_gpio)
1454200f72c9SAlgea Cao 			edid_data->mode_buf[i].invalid = true;
145577c2997fSAlgea Cao 	}
145677c2997fSAlgea Cao }
145777c2997fSAlgea Cao 
_rockchip_dw_hdmi_qp_get_timing(struct rockchip_connector * conn,struct display_state * state)145872209a0bSZhang Yubing static int _rockchip_dw_hdmi_qp_get_timing(struct rockchip_connector *conn,
1459*9c170041SAlgea Cao 					   struct display_state *state)
146028671edaSAlgea Cao {
146172209a0bSZhang Yubing 	int i;
146228671edaSAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
14630594ce39SZhang Yubing 	struct dw_hdmi_qp *hdmi = conn->data;
146428671edaSAlgea Cao 	struct edid *edid = (struct edid *)conn_state->edid;
146528671edaSAlgea Cao 	const u8 def_modes_vic[6] = {4, 16, 2, 17, 31, 19};
1466*9c170041SAlgea Cao 	int ret = 0;
146728671edaSAlgea Cao 
146828671edaSAlgea Cao 	if (!hdmi)
146928671edaSAlgea Cao 		return -EFAULT;
147028671edaSAlgea Cao 
1471*9c170041SAlgea Cao 	if (edid) {
147228671edaSAlgea Cao 		hdmi->sink_is_hdmi =
147328671edaSAlgea Cao 			drm_detect_hdmi_monitor(edid);
147428671edaSAlgea Cao 		hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
1475*9c170041SAlgea Cao 		ret = drm_add_edid_modes(&hdmi->edid_data, conn_state->edid);
147628671edaSAlgea Cao 	}
1477*9c170041SAlgea Cao 	if (ret <= 0) {
147828671edaSAlgea Cao 		hdmi->sink_is_hdmi = true;
147928671edaSAlgea Cao 		hdmi->sink_has_audio = true;
148028671edaSAlgea Cao 		do_cea_modes(&hdmi->edid_data, def_modes_vic,
148128671edaSAlgea Cao 			     sizeof(def_modes_vic));
148228671edaSAlgea Cao 		hdmi->edid_data.preferred_mode = &hdmi->edid_data.mode_buf[0];
148328671edaSAlgea Cao 		printf("failed to get edid\n");
148428671edaSAlgea Cao 	}
148528671edaSAlgea Cao 	drm_rk_filter_whitelist(&hdmi->edid_data);
148677c2997fSAlgea Cao 	rockchip_dw_hdmi_qp_mode_valid(hdmi);
148728671edaSAlgea Cao 	drm_mode_max_resolution_filter(&hdmi->edid_data,
148828671edaSAlgea Cao 				       &state->crtc_state.max_output);
148928671edaSAlgea Cao 	if (!drm_mode_prune_invalid(&hdmi->edid_data)) {
149028671edaSAlgea Cao 		printf("can't find valid hdmi mode\n");
149128671edaSAlgea Cao 		return -EINVAL;
149228671edaSAlgea Cao 	}
149328671edaSAlgea Cao 
149428671edaSAlgea Cao 	for (i = 0; i < hdmi->edid_data.modes; i++)
149528671edaSAlgea Cao 		hdmi->edid_data.mode_buf[i].vrefresh =
149628671edaSAlgea Cao 			drm_mode_vrefresh(&hdmi->edid_data.mode_buf[i]);
149728671edaSAlgea Cao 
149828671edaSAlgea Cao 	drm_mode_sort(&hdmi->edid_data);
149928671edaSAlgea Cao 
1500af006bcbSChen Shunqing 	rockchip_dw_hdmi_qp_config_output(conn, state);
150128671edaSAlgea Cao 
150228671edaSAlgea Cao 	return 0;
150328671edaSAlgea Cao }
150428671edaSAlgea Cao 
rockchip_dw_hdmi_qp_get_timing(struct rockchip_connector * conn,struct display_state * state)150572209a0bSZhang Yubing int rockchip_dw_hdmi_qp_get_timing(struct rockchip_connector *conn, struct display_state *state)
150672209a0bSZhang Yubing {
150772209a0bSZhang Yubing 	struct connector_state *conn_state = &state->conn_state;
150872209a0bSZhang Yubing 	struct dw_hdmi_qp *hdmi = conn->data;
150972209a0bSZhang Yubing 
1510*9c170041SAlgea Cao 	conn_state->edid = drm_do_get_edid(&hdmi->adap);
151172209a0bSZhang Yubing 
151272209a0bSZhang Yubing 	if (conn_state->secondary)
1513*9c170041SAlgea Cao 		_rockchip_dw_hdmi_qp_get_timing(conn_state->secondary, state);
151472209a0bSZhang Yubing 
1515*9c170041SAlgea Cao 	return _rockchip_dw_hdmi_qp_get_timing(conn, state);
151672209a0bSZhang Yubing }
151772209a0bSZhang Yubing 
151872209a0bSZhang Yubing 
rockchip_dw_hdmi_qp_detect(struct rockchip_connector * conn,struct display_state * state)15190594ce39SZhang Yubing int rockchip_dw_hdmi_qp_detect(struct rockchip_connector *conn, struct display_state *state)
152028671edaSAlgea Cao {
152128671edaSAlgea Cao 	int ret;
15220594ce39SZhang Yubing 	struct dw_hdmi_qp *hdmi = conn->data;
152328671edaSAlgea Cao 
152428671edaSAlgea Cao 	if (!hdmi)
152528671edaSAlgea Cao 		return -EFAULT;
152628671edaSAlgea Cao 
152728671edaSAlgea Cao 	ret = dw_hdmi_detect_hotplug(hdmi, state);
152828671edaSAlgea Cao 
152928671edaSAlgea Cao 	return ret;
153028671edaSAlgea Cao }
153128671edaSAlgea Cao 
rockchip_dw_hdmi_qp_get_edid(struct rockchip_connector * conn,struct display_state * state)15320594ce39SZhang Yubing int rockchip_dw_hdmi_qp_get_edid(struct rockchip_connector *conn, struct display_state *state)
153328671edaSAlgea Cao {
1534*9c170041SAlgea Cao 	int ret = 0;
153528671edaSAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
15360594ce39SZhang Yubing 	struct dw_hdmi_qp *hdmi = conn->data;
153728671edaSAlgea Cao 
1538*9c170041SAlgea Cao 	conn_state->edid = drm_do_get_edid(&hdmi->adap);
1539*9c170041SAlgea Cao 	if (!conn_state->edid)
1540*9c170041SAlgea Cao 		ret = -EINVAL;
154128671edaSAlgea Cao 
154228671edaSAlgea Cao 	return ret;
154328671edaSAlgea Cao }
1544