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, ®_val);
98028671edaSAlgea Cao ln0 = reg_val & 0xf;
98128671edaSAlgea Cao ln1 = (reg_val >> 4) & 0xf;
98228671edaSAlgea Cao
98328671edaSAlgea Cao drm_scdc_readb(&hdmi->adap, 0x42, ®_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