175450f0dSDamon Ding // SPDX-License-Identifier: GPL-2.0+
275450f0dSDamon Ding /*
375450f0dSDamon Ding * (C) Copyright 2024 Rockchip Electronics Co., Ltd
475450f0dSDamon Ding */
575450f0dSDamon Ding
675450f0dSDamon Ding #include <common.h>
775450f0dSDamon Ding #include <dm.h>
875450f0dSDamon Ding #include <errno.h>
975450f0dSDamon Ding #include <i2c.h>
1075450f0dSDamon Ding #include <video_bridge.h>
1175450f0dSDamon Ding #include <linux/delay.h>
1275450f0dSDamon Ding #include <linux/iopoll.h>
1375450f0dSDamon Ding #include <linux/media-bus-format.h>
1475450f0dSDamon Ding #include <linux/hdmi.h>
1575450f0dSDamon Ding
1675450f0dSDamon Ding #include "rockchip_bridge.h"
1775450f0dSDamon Ding #include "rockchip_display.h"
1875450f0dSDamon Ding #include "rockchip_panel.h"
1975450f0dSDamon Ding
2075450f0dSDamon Ding #define SII902X_TPI_VIDEO_DATA 0x0
2175450f0dSDamon Ding
2275450f0dSDamon Ding #define SII902X_TPI_PIXEL_REPETITION 0x8
2375450f0dSDamon Ding #define SII902X_TPI_AVI_PIXEL_REP_BUS_24BIT BIT(5)
2475450f0dSDamon Ding #define SII902X_TPI_AVI_PIXEL_REP_RISING_EDGE BIT(4)
2575450f0dSDamon Ding #define SII902X_TPI_AVI_PIXEL_REP_4X 3
2675450f0dSDamon Ding #define SII902X_TPI_AVI_PIXEL_REP_2X 1
2775450f0dSDamon Ding #define SII902X_TPI_AVI_PIXEL_REP_NONE 0
28404ebce3SDamon Ding #define SII902X_TPI_CLK_RATIO_MASK GENMASK(7, 6)
2975450f0dSDamon Ding #define SII902X_TPI_CLK_RATIO_HALF (0 << 6)
3075450f0dSDamon Ding #define SII902X_TPI_CLK_RATIO_1X (1 << 6)
3175450f0dSDamon Ding #define SII902X_TPI_CLK_RATIO_2X (2 << 6)
3275450f0dSDamon Ding #define SII902X_TPI_CLK_RATIO_4X (3 << 6)
3375450f0dSDamon Ding
3475450f0dSDamon Ding #define SII902X_TPI_AVI_IN_FORMAT 0x9
3575450f0dSDamon Ding #define SII902X_TPI_AVI_INPUT_BITMODE_12BIT BIT(7)
3675450f0dSDamon Ding #define SII902X_TPI_AVI_INPUT_DITHER BIT(6)
3775450f0dSDamon Ding #define SII902X_TPI_AVI_INPUT_RANGE_LIMITED (2 << 2)
3875450f0dSDamon Ding #define SII902X_TPI_AVI_INPUT_RANGE_FULL (1 << 2)
3975450f0dSDamon Ding #define SII902X_TPI_AVI_INPUT_RANGE_AUTO (0 << 2)
4075450f0dSDamon Ding #define SII902X_TPI_AVI_INPUT_COLORSPACE_BLACK (3 << 0)
4175450f0dSDamon Ding #define SII902X_TPI_AVI_INPUT_COLORSPACE_YUV422 (2 << 0)
4275450f0dSDamon Ding #define SII902X_TPI_AVI_INPUT_COLORSPACE_YUV444 (1 << 0)
4375450f0dSDamon Ding #define SII902X_TPI_AVI_INPUT_COLORSPACE_RGB (0 << 0)
4475450f0dSDamon Ding
4575450f0dSDamon Ding #define SII902X_TPI_AVI_INFOFRAME 0x0c
4675450f0dSDamon Ding
4775450f0dSDamon Ding #define SII902X_SYS_CTRL_DATA 0x1a
4875450f0dSDamon Ding #define SII902X_SYS_CTRL_PWR_DWN BIT(4)
4975450f0dSDamon Ding #define SII902X_SYS_CTRL_AV_MUTE BIT(3)
5075450f0dSDamon Ding #define SII902X_SYS_CTRL_DDC_BUS_REQ BIT(2)
5175450f0dSDamon Ding #define SII902X_SYS_CTRL_DDC_BUS_GRTD BIT(1)
5275450f0dSDamon Ding #define SII902X_SYS_CTRL_OUTPUT_MODE BIT(0)
5375450f0dSDamon Ding #define SII902X_SYS_CTRL_OUTPUT_HDMI 1
5475450f0dSDamon Ding #define SII902X_SYS_CTRL_OUTPUT_DVI 0
5575450f0dSDamon Ding
5675450f0dSDamon Ding #define SII902X_REG_CHIPID(n) (0x1b + (n))
5775450f0dSDamon Ding
5875450f0dSDamon Ding #define SII902X_PWR_STATE_CTRL 0x1e
5975450f0dSDamon Ding #define SII902X_AVI_POWER_STATE_MSK GENMASK(1, 0)
6075450f0dSDamon Ding #define SII902X_AVI_POWER_STATE_D(l) ((l) & SII902X_AVI_POWER_STATE_MSK)
6175450f0dSDamon Ding
6275450f0dSDamon Ding /* Audio */
6375450f0dSDamon Ding #define SII902X_TPI_I2S_ENABLE_MAPPING_REG 0x1f
6475450f0dSDamon Ding #define SII902X_TPI_I2S_CONFIG_FIFO0 (0 << 0)
6575450f0dSDamon Ding #define SII902X_TPI_I2S_CONFIG_FIFO1 (1 << 0)
6675450f0dSDamon Ding #define SII902X_TPI_I2S_CONFIG_FIFO2 (2 << 0)
6775450f0dSDamon Ding #define SII902X_TPI_I2S_CONFIG_FIFO3 (3 << 0)
6875450f0dSDamon Ding #define SII902X_TPI_I2S_LEFT_RIGHT_SWAP (1 << 2)
6975450f0dSDamon Ding #define SII902X_TPI_I2S_AUTO_DOWNSAMPLE (1 << 3)
7075450f0dSDamon Ding #define SII902X_TPI_I2S_SELECT_SD0 (0 << 4)
7175450f0dSDamon Ding #define SII902X_TPI_I2S_SELECT_SD1 (1 << 4)
7275450f0dSDamon Ding #define SII902X_TPI_I2S_SELECT_SD2 (2 << 4)
7375450f0dSDamon Ding #define SII902X_TPI_I2S_SELECT_SD3 (3 << 4)
7475450f0dSDamon Ding #define SII902X_TPI_I2S_FIFO_ENABLE (1 << 7)
7575450f0dSDamon Ding
7675450f0dSDamon Ding #define SII902X_TPI_I2S_INPUT_CONFIG_REG 0x20
7775450f0dSDamon Ding #define SII902X_TPI_I2S_FIRST_BIT_SHIFT_YES (0 << 0)
7875450f0dSDamon Ding #define SII902X_TPI_I2S_FIRST_BIT_SHIFT_NO (1 << 0)
7975450f0dSDamon Ding #define SII902X_TPI_I2S_SD_DIRECTION_MSB_FIRST (0 << 1)
8075450f0dSDamon Ding #define SII902X_TPI_I2S_SD_DIRECTION_LSB_FIRST (1 << 1)
8175450f0dSDamon Ding #define SII902X_TPI_I2S_SD_JUSTIFY_LEFT (0 << 2)
8275450f0dSDamon Ding #define SII902X_TPI_I2S_SD_JUSTIFY_RIGHT (1 << 2)
8375450f0dSDamon Ding #define SII902X_TPI_I2S_WS_POLARITY_LOW (0 << 3)
8475450f0dSDamon Ding #define SII902X_TPI_I2S_WS_POLARITY_HIGH (1 << 3)
8575450f0dSDamon Ding #define SII902X_TPI_I2S_MCLK_MULTIPLIER_128 (0 << 4)
8675450f0dSDamon Ding #define SII902X_TPI_I2S_MCLK_MULTIPLIER_256 (1 << 4)
8775450f0dSDamon Ding #define SII902X_TPI_I2S_MCLK_MULTIPLIER_384 (2 << 4)
8875450f0dSDamon Ding #define SII902X_TPI_I2S_MCLK_MULTIPLIER_512 (3 << 4)
8975450f0dSDamon Ding #define SII902X_TPI_I2S_MCLK_MULTIPLIER_768 (4 << 4)
9075450f0dSDamon Ding #define SII902X_TPI_I2S_MCLK_MULTIPLIER_1024 (5 << 4)
9175450f0dSDamon Ding #define SII902X_TPI_I2S_MCLK_MULTIPLIER_1152 (6 << 4)
9275450f0dSDamon Ding #define SII902X_TPI_I2S_MCLK_MULTIPLIER_192 (7 << 4)
9375450f0dSDamon Ding #define SII902X_TPI_I2S_SCK_EDGE_FALLING (0 << 7)
9475450f0dSDamon Ding #define SII902X_TPI_I2S_SCK_EDGE_RISING (1 << 7)
9575450f0dSDamon Ding
9675450f0dSDamon Ding #define SII902X_TPI_I2S_STRM_HDR_BASE 0x21
9775450f0dSDamon Ding #define SII902X_TPI_I2S_STRM_HDR_SIZE 5
9875450f0dSDamon Ding
9975450f0dSDamon Ding #define SII902X_TPI_AUDIO_CONFIG_BYTE2_REG 0x26
10075450f0dSDamon Ding #define SII902X_TPI_AUDIO_CODING_STREAM_HEADER (0 << 0)
10175450f0dSDamon Ding #define SII902X_TPI_AUDIO_CODING_PCM (1 << 0)
10275450f0dSDamon Ding #define SII902X_TPI_AUDIO_CODING_AC3 (2 << 0)
10375450f0dSDamon Ding #define SII902X_TPI_AUDIO_CODING_MPEG1 (3 << 0)
10475450f0dSDamon Ding #define SII902X_TPI_AUDIO_CODING_MP3 (4 << 0)
10575450f0dSDamon Ding #define SII902X_TPI_AUDIO_CODING_MPEG2 (5 << 0)
10675450f0dSDamon Ding #define SII902X_TPI_AUDIO_CODING_AAC (6 << 0)
10775450f0dSDamon Ding #define SII902X_TPI_AUDIO_CODING_DTS (7 << 0)
10875450f0dSDamon Ding #define SII902X_TPI_AUDIO_CODING_ATRAC (8 << 0)
10975450f0dSDamon Ding #define SII902X_TPI_AUDIO_MUTE_DISABLE (0 << 4)
11075450f0dSDamon Ding #define SII902X_TPI_AUDIO_MUTE_ENABLE (1 << 4)
11175450f0dSDamon Ding #define SII902X_TPI_AUDIO_LAYOUT_2_CHANNELS (0 << 5)
11275450f0dSDamon Ding #define SII902X_TPI_AUDIO_LAYOUT_8_CHANNELS (1 << 5)
11375450f0dSDamon Ding #define SII902X_TPI_AUDIO_INTERFACE_DISABLE (0 << 6)
11475450f0dSDamon Ding #define SII902X_TPI_AUDIO_INTERFACE_SPDIF (1 << 6)
11575450f0dSDamon Ding #define SII902X_TPI_AUDIO_INTERFACE_I2S (2 << 6)
11675450f0dSDamon Ding
11775450f0dSDamon Ding #define SII902X_TPI_AUDIO_CONFIG_BYTE3_REG 0x27
11875450f0dSDamon Ding #define SII902X_TPI_AUDIO_FREQ_STREAM (0 << 3)
11975450f0dSDamon Ding #define SII902X_TPI_AUDIO_FREQ_32KHZ (1 << 3)
12075450f0dSDamon Ding #define SII902X_TPI_AUDIO_FREQ_44KHZ (2 << 3)
12175450f0dSDamon Ding #define SII902X_TPI_AUDIO_FREQ_48KHZ (3 << 3)
12275450f0dSDamon Ding #define SII902X_TPI_AUDIO_FREQ_88KHZ (4 << 3)
12375450f0dSDamon Ding #define SII902X_TPI_AUDIO_FREQ_96KHZ (5 << 3)
12475450f0dSDamon Ding #define SII902X_TPI_AUDIO_FREQ_176KHZ (6 << 3)
12575450f0dSDamon Ding #define SII902X_TPI_AUDIO_FREQ_192KHZ (7 << 3)
12675450f0dSDamon Ding #define SII902X_TPI_AUDIO_SAMPLE_SIZE_STREAM (0 << 6)
12775450f0dSDamon Ding #define SII902X_TPI_AUDIO_SAMPLE_SIZE_16 (1 << 6)
12875450f0dSDamon Ding #define SII902X_TPI_AUDIO_SAMPLE_SIZE_20 (2 << 6)
12975450f0dSDamon Ding #define SII902X_TPI_AUDIO_SAMPLE_SIZE_24 (3 << 6)
13075450f0dSDamon Ding
13175450f0dSDamon Ding #define SII902X_TPI_AUDIO_CONFIG_BYTE4_REG 0x28
13275450f0dSDamon Ding
13375450f0dSDamon Ding #define SII902X_INT_ENABLE 0x3c
13475450f0dSDamon Ding #define SII902X_INT_STATUS 0x3d
13575450f0dSDamon Ding #define SII902X_HOTPLUG_EVENT BIT(0)
13675450f0dSDamon Ding #define SII902X_PLUGGED_STATUS BIT(2)
13775450f0dSDamon Ding
13875450f0dSDamon Ding #define SII902X_TPI_SYNC_GEN_CTRL 0x60
13975450f0dSDamon Ding #define SII902X_TPI_SYNC_POLAR_DETECT 0x61
14075450f0dSDamon Ding #define SII902X_TPI_HBIT_TO_HSYNC 0x62
14175450f0dSDamon Ding #define SII902X_EMBEDDED_SYNC_EXTRACTION_REG 0x63
14275450f0dSDamon Ding #define SII902X_EMBEDDED_SYNC_EXTRACTION BIT(6)
14375450f0dSDamon Ding
14475450f0dSDamon Ding #define SII902X_REG_TPI_RQB 0xc7
14575450f0dSDamon Ding
14675450f0dSDamon Ding /* Indirect internal register access */
14775450f0dSDamon Ding #define SII902X_IND_SET_PAGE 0xbc
14875450f0dSDamon Ding #define SII902X_IND_OFFSET 0xbd
14975450f0dSDamon Ding #define SII902X_IND_VALUE 0xbe
15075450f0dSDamon Ding
15175450f0dSDamon Ding #define SII902X_TPI_MISC_INFOFRAME_BASE 0xbf
15275450f0dSDamon Ding #define SII902X_TPI_MISC_INFOFRAME_END 0xde
15375450f0dSDamon Ding #define SII902X_TPI_MISC_INFOFRAME_SIZE \
15475450f0dSDamon Ding (SII902X_TPI_MISC_INFOFRAME_END - SII902X_TPI_MISC_INFOFRAME_BASE)
15575450f0dSDamon Ding
15675450f0dSDamon Ding #define SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS 500
15775450f0dSDamon Ding
15875450f0dSDamon Ding #define SII902X_AUDIO_PORT_INDEX 3
15975450f0dSDamon Ding
16075450f0dSDamon Ding struct sii902x {
16175450f0dSDamon Ding struct udevice *dev;
16275450f0dSDamon Ding struct udevice *power_supply;
16375450f0dSDamon Ding struct gpio_desc enable_gpio;
16475450f0dSDamon Ding struct gpio_desc reset_gpio;
16575450f0dSDamon Ding struct rockchip_bridge *bridge;
16675450f0dSDamon Ding struct drm_display_mode mode;
16775450f0dSDamon Ding struct ddc_adapter adap;
16875450f0dSDamon Ding struct hdmi_edid_data edid_data;
16975450f0dSDamon Ding int bus_format;
17075450f0dSDamon Ding };
17175450f0dSDamon Ding
17275450f0dSDamon Ding enum sii902x_bus_format {
17375450f0dSDamon Ding FORMAT_RGB_INPUT,
17475450f0dSDamon Ding FORMAT_YCBCR422_INPUT,
17575450f0dSDamon Ding FORMAT_YCBCR444_INPUT,
17675450f0dSDamon Ding };
17775450f0dSDamon Ding
bridge_to_sii902x(struct rockchip_bridge * bridge)17875450f0dSDamon Ding static inline struct sii902x *bridge_to_sii902x(struct rockchip_bridge *bridge)
17975450f0dSDamon Ding {
18075450f0dSDamon Ding return container_of(&bridge, struct sii902x, bridge);
18175450f0dSDamon Ding }
18275450f0dSDamon Ding
sii902x_reg_update_bits(struct sii902x * sii902x,u8 reg,u8 mask,u8 val)18375450f0dSDamon Ding static int sii902x_reg_update_bits(struct sii902x *sii902x, u8 reg, u8 mask, u8 val)
18475450f0dSDamon Ding {
18575450f0dSDamon Ding u8 status;
18675450f0dSDamon Ding
18775450f0dSDamon Ding status = dm_i2c_reg_read(sii902x->dev, reg);
18875450f0dSDamon Ding status &= ~mask;
18975450f0dSDamon Ding status |= val & mask;
19075450f0dSDamon Ding
19175450f0dSDamon Ding return dm_i2c_reg_write(sii902x->dev, reg, status);
19275450f0dSDamon Ding }
19375450f0dSDamon Ding
sii902x_bridge_detect(struct rockchip_bridge * bridge)19475450f0dSDamon Ding static bool sii902x_bridge_detect(struct rockchip_bridge *bridge)
19575450f0dSDamon Ding {
19675450f0dSDamon Ding struct udevice *dev = bridge->dev;
19775450f0dSDamon Ding u8 status;
19875450f0dSDamon Ding
19975450f0dSDamon Ding status = dm_i2c_reg_read(dev, SII902X_INT_STATUS);
20075450f0dSDamon Ding
20175450f0dSDamon Ding return (status & SII902X_PLUGGED_STATUS) ? true : false;
20275450f0dSDamon Ding }
20375450f0dSDamon Ding
sii902x_check_embedded_format(uint32_t bus_format)20475450f0dSDamon Ding static bool sii902x_check_embedded_format(uint32_t bus_format)
20575450f0dSDamon Ding {
20675450f0dSDamon Ding switch (bus_format) {
20775450f0dSDamon Ding case MEDIA_BUS_FMT_YUYV8_2X8:
20875450f0dSDamon Ding case MEDIA_BUS_FMT_YVYU8_2X8:
20975450f0dSDamon Ding case MEDIA_BUS_FMT_UYVY8_2X8:
21075450f0dSDamon Ding case MEDIA_BUS_FMT_VYUY8_2X8:
21175450f0dSDamon Ding case MEDIA_BUS_FMT_YUYV8_1X16:
21275450f0dSDamon Ding case MEDIA_BUS_FMT_YVYU8_1X16:
21375450f0dSDamon Ding case MEDIA_BUS_FMT_UYVY8_1X16:
21475450f0dSDamon Ding case MEDIA_BUS_FMT_VYUY8_1X16:
21575450f0dSDamon Ding return true;
21675450f0dSDamon Ding default:
21775450f0dSDamon Ding return false;
21875450f0dSDamon Ding }
21975450f0dSDamon Ding }
22075450f0dSDamon Ding
sii902x_set_embedded_sync(struct sii902x * sii902x)22175450f0dSDamon Ding static void sii902x_set_embedded_sync(struct sii902x *sii902x)
22275450f0dSDamon Ding {
22375450f0dSDamon Ding struct udevice *dev = sii902x->dev;
22475450f0dSDamon Ding unsigned char data[8];
22575450f0dSDamon Ding struct videomode vm;
22675450f0dSDamon Ding
22775450f0dSDamon Ding if (!sii902x_check_embedded_format(sii902x->bus_format))
22875450f0dSDamon Ding return;
22975450f0dSDamon Ding
23075450f0dSDamon Ding switch (sii902x->bus_format) {
23175450f0dSDamon Ding case MEDIA_BUS_FMT_YUYV8_2X8:
23275450f0dSDamon Ding case MEDIA_BUS_FMT_YVYU8_2X8:
23375450f0dSDamon Ding case MEDIA_BUS_FMT_UYVY8_2X8:
23475450f0dSDamon Ding case MEDIA_BUS_FMT_VYUY8_2X8:
23575450f0dSDamon Ding sii902x_reg_update_bits(sii902x, SII902X_TPI_SYNC_GEN_CTRL, 0x20, 0x20);
23675450f0dSDamon Ding break;
23775450f0dSDamon Ding default:
23875450f0dSDamon Ding break;
23975450f0dSDamon Ding }
24075450f0dSDamon Ding
24175450f0dSDamon Ding sii902x_reg_update_bits(sii902x, SII902X_TPI_SYNC_GEN_CTRL, 0x80, 0x00);
24275450f0dSDamon Ding dm_i2c_reg_write(dev, SII902X_EMBEDDED_SYNC_EXTRACTION_REG, 0x00);
24375450f0dSDamon Ding sii902x_reg_update_bits(sii902x, SII902X_TPI_SYNC_GEN_CTRL, 0x80, 0x80);
24475450f0dSDamon Ding
24575450f0dSDamon Ding drm_display_mode_to_videomode(&sii902x->mode, &vm);
24675450f0dSDamon Ding data[0] = vm.hfront_porch & 0xff;
24775450f0dSDamon Ding data[1] = (vm.hfront_porch >> 8) & 0x03;
24875450f0dSDamon Ding if (sii902x->mode.flags & DRM_MODE_FLAG_INTERLACE) {
24975450f0dSDamon Ding data[2] = (sii902x->mode.vtotal >> 1) & 0xff;
25075450f0dSDamon Ding data[3] = ((sii902x->mode.vtotal >> 1) >> 8) & 0x1f;
25175450f0dSDamon Ding } else {
25275450f0dSDamon Ding data[2] = 0;
25375450f0dSDamon Ding data[3] = 0;
25475450f0dSDamon Ding }
25575450f0dSDamon Ding data[4] = vm.hsync_len & 0xff;
25675450f0dSDamon Ding data[5] = (vm.hsync_len >> 8) & 0x03;
25775450f0dSDamon Ding data[6] = vm.vfront_porch;
25875450f0dSDamon Ding data[7] = vm.vsync_len;
25975450f0dSDamon Ding dm_i2c_write(dev, SII902X_TPI_HBIT_TO_HSYNC, data, 8);
26075450f0dSDamon Ding
26175450f0dSDamon Ding sii902x_reg_update_bits(sii902x, SII902X_TPI_SYNC_GEN_CTRL, 0x80, 0x80);
26275450f0dSDamon Ding sii902x_reg_update_bits(sii902x, SII902X_EMBEDDED_SYNC_EXTRACTION_REG, 0x40, 0x40);
26375450f0dSDamon Ding
26475450f0dSDamon Ding sii902x_reg_update_bits(sii902x, SII902X_EMBEDDED_SYNC_EXTRACTION_REG,
26575450f0dSDamon Ding SII902X_EMBEDDED_SYNC_EXTRACTION,
26675450f0dSDamon Ding SII902X_EMBEDDED_SYNC_EXTRACTION);
26775450f0dSDamon Ding }
26875450f0dSDamon Ding
sii902x_set_format(struct sii902x * sii902x)26975450f0dSDamon Ding static void sii902x_set_format(struct sii902x *sii902x)
27075450f0dSDamon Ding {
27175450f0dSDamon Ding struct udevice *dev = sii902x->dev;
27275450f0dSDamon Ding u8 val;
27375450f0dSDamon Ding
27475450f0dSDamon Ding switch (sii902x->bus_format) {
27575450f0dSDamon Ding case MEDIA_BUS_FMT_YUYV8_1X16:
27675450f0dSDamon Ding case MEDIA_BUS_FMT_YVYU8_1X16:
27775450f0dSDamon Ding case MEDIA_BUS_FMT_UYVY8_1X16:
27875450f0dSDamon Ding case MEDIA_BUS_FMT_VYUY8_1X16:
27975450f0dSDamon Ding case MEDIA_BUS_FMT_YUYV8_2X8:
28075450f0dSDamon Ding case MEDIA_BUS_FMT_YVYU8_2X8:
28175450f0dSDamon Ding case MEDIA_BUS_FMT_UYVY8_2X8:
28275450f0dSDamon Ding case MEDIA_BUS_FMT_VYUY8_2X8:
28375450f0dSDamon Ding val = SII902X_TPI_AVI_INPUT_COLORSPACE_YUV422;
28475450f0dSDamon Ding break;
28575450f0dSDamon Ding case MEDIA_BUS_FMT_YUV8_1X24:
28675450f0dSDamon Ding case MEDIA_BUS_FMT_VUY8_1X24:
28775450f0dSDamon Ding val = SII902X_TPI_AVI_INPUT_COLORSPACE_YUV444;
28875450f0dSDamon Ding break;
28975450f0dSDamon Ding case MEDIA_BUS_FMT_RGB888_1X24:
29075450f0dSDamon Ding default:
29175450f0dSDamon Ding val = SII902X_TPI_AVI_INPUT_COLORSPACE_RGB;
29275450f0dSDamon Ding break;
29375450f0dSDamon Ding }
29475450f0dSDamon Ding
29575450f0dSDamon Ding val |= SII902X_TPI_AVI_INPUT_RANGE_AUTO;
29675450f0dSDamon Ding val &= ~(SII902X_TPI_AVI_INPUT_DITHER |
29775450f0dSDamon Ding SII902X_TPI_AVI_INPUT_BITMODE_12BIT);
29875450f0dSDamon Ding dm_i2c_reg_write(dev, SII902X_TPI_AVI_IN_FORMAT, val);
29975450f0dSDamon Ding
30075450f0dSDamon Ding sii902x_set_embedded_sync(sii902x);
30175450f0dSDamon Ding }
30275450f0dSDamon Ding
sii902x_set_connector_display_info(struct sii902x * sii902x,struct connector_state * conn_state)30375450f0dSDamon Ding static void sii902x_set_connector_display_info(struct sii902x *sii902x,
30475450f0dSDamon Ding struct connector_state *conn_state)
30575450f0dSDamon Ding {
30675450f0dSDamon Ding conn_state->bus_format = sii902x->bus_format;
30775450f0dSDamon Ding switch (conn_state->bus_format) {
30875450f0dSDamon Ding case MEDIA_BUS_FMT_YUYV8_2X8:
30975450f0dSDamon Ding case MEDIA_BUS_FMT_YVYU8_2X8:
31075450f0dSDamon Ding case MEDIA_BUS_FMT_UYVY8_2X8:
31175450f0dSDamon Ding case MEDIA_BUS_FMT_VYUY8_2X8:
31275450f0dSDamon Ding conn_state->output_mode = ROCKCHIP_OUT_MODE_BT656;
31375450f0dSDamon Ding conn_state->output_if = VOP_OUTPUT_IF_BT656;
31475450f0dSDamon Ding conn_state->color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
31575450f0dSDamon Ding conn_state->color_encoding = DRM_COLOR_YCBCR_BT601;
31675450f0dSDamon Ding break;
31775450f0dSDamon Ding case MEDIA_BUS_FMT_YUYV8_1X16:
31875450f0dSDamon Ding case MEDIA_BUS_FMT_YVYU8_1X16:
31975450f0dSDamon Ding case MEDIA_BUS_FMT_UYVY8_1X16:
32075450f0dSDamon Ding case MEDIA_BUS_FMT_VYUY8_1X16:
32175450f0dSDamon Ding conn_state->output_mode = ROCKCHIP_OUT_MODE_BT1120;
32275450f0dSDamon Ding conn_state->output_if = VOP_OUTPUT_IF_BT1120;
32375450f0dSDamon Ding conn_state->color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
32475450f0dSDamon Ding conn_state->color_encoding = DRM_COLOR_YCBCR_BT709;
32575450f0dSDamon Ding break;
32675450f0dSDamon Ding case MEDIA_BUS_FMT_RGB888_1X24:
32775450f0dSDamon Ding default:
32875450f0dSDamon Ding conn_state->output_mode = ROCKCHIP_OUT_MODE_P888;
32975450f0dSDamon Ding conn_state->output_if = VOP_OUTPUT_IF_RGB;
33075450f0dSDamon Ding conn_state->color_range = DRM_COLOR_YCBCR_FULL_RANGE;
33175450f0dSDamon Ding conn_state->color_encoding = DRM_COLOR_YCBCR_BT709;
33275450f0dSDamon Ding break;
33375450f0dSDamon Ding }
33475450f0dSDamon Ding }
33575450f0dSDamon Ding
sii902x_bridge_mode_set(struct rockchip_bridge * bridge,const struct drm_display_mode * mode)33675450f0dSDamon Ding static void sii902x_bridge_mode_set(struct rockchip_bridge *bridge,
33775450f0dSDamon Ding const struct drm_display_mode *mode)
33875450f0dSDamon Ding {
33975450f0dSDamon Ding struct sii902x *sii902x = dev_get_priv(bridge->dev);
34075450f0dSDamon Ding struct connector_state *conn_state = &bridge->state->conn_state;
34175450f0dSDamon Ding struct edid *edid = (struct edid *)conn_state->edid;
34275450f0dSDamon Ding struct udevice *dev = sii902x->dev;
34375450f0dSDamon Ding struct hdmi_avi_infoframe frame;
34475450f0dSDamon Ding u8 buf[HDMI_INFOFRAME_SIZE(AVI)];
34575450f0dSDamon Ding u8 output_mode;
34675450f0dSDamon Ding u16 pixel_clock_10kHz = mode->clock / 10;
347404ebce3SDamon Ding u8 ratio;
34875450f0dSDamon Ding int ret, vrefresh;
34975450f0dSDamon Ding
35075450f0dSDamon Ding if (drm_detect_hdmi_monitor(edid))
35175450f0dSDamon Ding output_mode = SII902X_SYS_CTRL_OUTPUT_HDMI;
35275450f0dSDamon Ding else
35375450f0dSDamon Ding output_mode = SII902X_SYS_CTRL_OUTPUT_DVI;
35475450f0dSDamon Ding
35575450f0dSDamon Ding sii902x_reg_update_bits(sii902x, SII902X_SYS_CTRL_DATA,
35675450f0dSDamon Ding SII902X_SYS_CTRL_OUTPUT_MODE,
35775450f0dSDamon Ding output_mode);
35875450f0dSDamon Ding
35975450f0dSDamon Ding drm_mode_copy(&sii902x->mode, mode);
36075450f0dSDamon Ding vrefresh = drm_mode_vrefresh(mode) * 100;
36175450f0dSDamon Ding buf[0] = pixel_clock_10kHz & 0xff;
36275450f0dSDamon Ding buf[1] = pixel_clock_10kHz >> 8;
36375450f0dSDamon Ding buf[2] = vrefresh & 0xff;
36475450f0dSDamon Ding buf[3] = vrefresh >> 8;
36575450f0dSDamon Ding buf[4] = mode->crtc_htotal;
36675450f0dSDamon Ding buf[5] = mode->crtc_htotal >> 8;
36775450f0dSDamon Ding buf[6] = mode->crtc_vtotal;
36875450f0dSDamon Ding buf[7] = mode->crtc_vtotal >> 8;
369404ebce3SDamon Ding buf[8] = SII902X_TPI_AVI_PIXEL_REP_NONE | SII902X_TPI_AVI_PIXEL_REP_BUS_24BIT;
37075450f0dSDamon Ding switch (sii902x->bus_format) {
37175450f0dSDamon Ding case MEDIA_BUS_FMT_YUYV8_1X16:
37275450f0dSDamon Ding case MEDIA_BUS_FMT_YVYU8_1X16:
37375450f0dSDamon Ding case MEDIA_BUS_FMT_UYVY8_1X16:
37475450f0dSDamon Ding case MEDIA_BUS_FMT_VYUY8_1X16:
37575450f0dSDamon Ding case MEDIA_BUS_FMT_YUYV8_2X8:
37675450f0dSDamon Ding case MEDIA_BUS_FMT_YVYU8_2X8:
37775450f0dSDamon Ding case MEDIA_BUS_FMT_UYVY8_2X8:
37875450f0dSDamon Ding case MEDIA_BUS_FMT_VYUY8_2X8:
37975450f0dSDamon Ding buf[8] |= SII902X_TPI_AVI_PIXEL_REP_RISING_EDGE;
38075450f0dSDamon Ding break;
38175450f0dSDamon Ding default:
38275450f0dSDamon Ding break;
38375450f0dSDamon Ding }
38475450f0dSDamon Ding
38575450f0dSDamon Ding buf[9] = SII902X_TPI_AVI_INPUT_RANGE_AUTO;
38675450f0dSDamon Ding switch (sii902x->bus_format) {
38775450f0dSDamon Ding case MEDIA_BUS_FMT_YUYV8_1X16:
38875450f0dSDamon Ding case MEDIA_BUS_FMT_YVYU8_1X16:
38975450f0dSDamon Ding case MEDIA_BUS_FMT_UYVY8_1X16:
39075450f0dSDamon Ding case MEDIA_BUS_FMT_VYUY8_1X16:
39175450f0dSDamon Ding case MEDIA_BUS_FMT_YUYV8_2X8:
39275450f0dSDamon Ding case MEDIA_BUS_FMT_YVYU8_2X8:
39375450f0dSDamon Ding case MEDIA_BUS_FMT_UYVY8_2X8:
39475450f0dSDamon Ding case MEDIA_BUS_FMT_VYUY8_2X8:
39575450f0dSDamon Ding buf[9] |= SII902X_TPI_AVI_INPUT_COLORSPACE_YUV422;
39675450f0dSDamon Ding break;
39775450f0dSDamon Ding case MEDIA_BUS_FMT_YUV8_1X24:
39875450f0dSDamon Ding case MEDIA_BUS_FMT_VUY8_1X24:
39975450f0dSDamon Ding buf[9] |= SII902X_TPI_AVI_INPUT_COLORSPACE_YUV444;
40075450f0dSDamon Ding break;
40175450f0dSDamon Ding case MEDIA_BUS_FMT_RGB888_1X24:
40275450f0dSDamon Ding default:
40375450f0dSDamon Ding buf[9] |= SII902X_TPI_AVI_INPUT_COLORSPACE_RGB;
40475450f0dSDamon Ding break;
40575450f0dSDamon Ding }
40675450f0dSDamon Ding
40775450f0dSDamon Ding ret = dm_i2c_write(dev, SII902X_TPI_VIDEO_DATA, buf, 10);
40875450f0dSDamon Ding if (ret) {
40975450f0dSDamon Ding dev_err(dev, "failed to set video data\n");
41075450f0dSDamon Ding return;
41175450f0dSDamon Ding }
41275450f0dSDamon Ding
413*fb331418SChaoyi Chen if (sii902x->mode.flags & DRM_MODE_FLAG_DBLCLK)
414404ebce3SDamon Ding ratio = SII902X_TPI_CLK_RATIO_2X;
415404ebce3SDamon Ding else
416404ebce3SDamon Ding ratio = SII902X_TPI_CLK_RATIO_1X;
417404ebce3SDamon Ding sii902x_reg_update_bits(sii902x, SII902X_TPI_PIXEL_REPETITION,
418404ebce3SDamon Ding SII902X_TPI_CLK_RATIO_MASK, ratio);
419404ebce3SDamon Ding
42075450f0dSDamon Ding ret = drm_hdmi_avi_infoframe_from_display_mode(&frame, &sii902x->mode, false);
42175450f0dSDamon Ding if (ret < 0) {
42275450f0dSDamon Ding dev_err(dev, "couldn't fill AVI infoframe\n");
42375450f0dSDamon Ding return;
42475450f0dSDamon Ding }
42575450f0dSDamon Ding
42675450f0dSDamon Ding ret = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf));
42775450f0dSDamon Ding if (ret < 0) {
42875450f0dSDamon Ding dev_err(dev, "failed to pack AVI infoframe: %d\n", ret);
42975450f0dSDamon Ding return;
43075450f0dSDamon Ding }
43175450f0dSDamon Ding
43275450f0dSDamon Ding /* Do not send the infoframe header, but keep the CRC field. */
43375450f0dSDamon Ding dm_i2c_write(dev, SII902X_TPI_AVI_INFOFRAME,
43475450f0dSDamon Ding buf + HDMI_INFOFRAME_HEADER_SIZE - 1,
43575450f0dSDamon Ding HDMI_AVI_INFOFRAME_SIZE + 1);
43675450f0dSDamon Ding sii902x_set_format(sii902x);
43775450f0dSDamon Ding sii902x_set_connector_display_info(sii902x, conn_state);
43875450f0dSDamon Ding }
43975450f0dSDamon Ding
sii902x_bridge_enable(struct rockchip_bridge * bridge)44075450f0dSDamon Ding static void sii902x_bridge_enable(struct rockchip_bridge *bridge)
44175450f0dSDamon Ding {
44275450f0dSDamon Ding struct sii902x *sii902x = dev_get_priv(bridge->dev);
44375450f0dSDamon Ding
44475450f0dSDamon Ding sii902x_reg_update_bits(sii902x, SII902X_PWR_STATE_CTRL,
44575450f0dSDamon Ding SII902X_AVI_POWER_STATE_MSK,
44675450f0dSDamon Ding SII902X_AVI_POWER_STATE_D(0));
44775450f0dSDamon Ding sii902x_reg_update_bits(sii902x, SII902X_SYS_CTRL_DATA, SII902X_SYS_CTRL_PWR_DWN, 0);
44875450f0dSDamon Ding }
44975450f0dSDamon Ding
sii902x_bridge_disable(struct rockchip_bridge * bridge)45075450f0dSDamon Ding static void sii902x_bridge_disable(struct rockchip_bridge *bridge)
45175450f0dSDamon Ding {
45275450f0dSDamon Ding struct sii902x *sii902x = dev_get_priv(bridge->dev);
45375450f0dSDamon Ding
45475450f0dSDamon Ding sii902x_reg_update_bits(sii902x, SII902X_SYS_CTRL_DATA,
45575450f0dSDamon Ding SII902X_SYS_CTRL_PWR_DWN,
45675450f0dSDamon Ding SII902X_SYS_CTRL_PWR_DWN);
45775450f0dSDamon Ding }
45875450f0dSDamon Ding
45975450f0dSDamon Ding static const struct rockchip_bridge_funcs sii902x_bridge_funcs = {
46075450f0dSDamon Ding .detect = sii902x_bridge_detect,
46175450f0dSDamon Ding .mode_set = sii902x_bridge_mode_set,
46275450f0dSDamon Ding .enable = sii902x_bridge_enable,
46375450f0dSDamon Ding .post_disable = sii902x_bridge_disable,
46475450f0dSDamon Ding };
46575450f0dSDamon Ding
sii902x_reset(struct sii902x * sii902x)46675450f0dSDamon Ding static void sii902x_reset(struct sii902x *sii902x)
46775450f0dSDamon Ding {
46875450f0dSDamon Ding if (!dm_gpio_is_valid(&sii902x->reset_gpio))
46975450f0dSDamon Ding return;
47075450f0dSDamon Ding
47175450f0dSDamon Ding dm_gpio_set_value(&sii902x->reset_gpio, 1);
47275450f0dSDamon Ding
47375450f0dSDamon Ding /* The datasheet says treset-min = 100us. Make it 150us to be sure. */
47475450f0dSDamon Ding udelay(500);
47575450f0dSDamon Ding
47675450f0dSDamon Ding dm_gpio_set_value(&sii902x->reset_gpio, 0);
47775450f0dSDamon Ding }
47875450f0dSDamon Ding
47975450f0dSDamon Ding #define SII902X_READ_REG(v) dm_i2c_reg_read(sii902x->dev, v)
48075450f0dSDamon Ding
48175450f0dSDamon Ding /*
48275450f0dSDamon Ding * The purpose of sii902x_i2c_bypass_select is to enable the pass through
48375450f0dSDamon Ding * mode of the HDMI transmitter. Do not use regmap from within this function,
48475450f0dSDamon Ding * only use sii902x_*_unlocked functions to read/modify/write registers.
48575450f0dSDamon Ding * We are holding the parent adapter lock here, keep this in mind before
48675450f0dSDamon Ding * adding more i2c transactions.
48775450f0dSDamon Ding *
48875450f0dSDamon Ding * Also, since SII902X_SYS_CTRL_DATA is used with regmap_update_bits elsewhere
48975450f0dSDamon Ding * in this driver, we need to make sure that we only touch 0x1A[2:1] from
49075450f0dSDamon Ding * within sii902x_i2c_bypass_select and sii902x_i2c_bypass_deselect, and that
49175450f0dSDamon Ding * we leave the remaining bits as we have found them.
49275450f0dSDamon Ding */
sii902x_i2c_bypass_select(struct sii902x * sii902x)49375450f0dSDamon Ding static int sii902x_i2c_bypass_select(struct sii902x *sii902x)
49475450f0dSDamon Ding {
49575450f0dSDamon Ding struct udevice *dev = sii902x->dev;
49675450f0dSDamon Ding u8 status;
49775450f0dSDamon Ding int ret;
49875450f0dSDamon Ding
49975450f0dSDamon Ding sii902x_reg_update_bits(sii902x, SII902X_SYS_CTRL_DATA,
50075450f0dSDamon Ding SII902X_SYS_CTRL_DDC_BUS_REQ,
50175450f0dSDamon Ding SII902X_SYS_CTRL_DDC_BUS_REQ);
50275450f0dSDamon Ding
50375450f0dSDamon Ding ret = readx_poll_timeout(SII902X_READ_REG, SII902X_SYS_CTRL_DATA, status,
50475450f0dSDamon Ding status & SII902X_SYS_CTRL_DDC_BUS_GRTD,
50575450f0dSDamon Ding SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS * 1000);
50675450f0dSDamon Ding if (ret) {
50775450f0dSDamon Ding dev_err(dev, "Failed to acquire the i2c bus\n");
50875450f0dSDamon Ding return -ETIMEDOUT;
50975450f0dSDamon Ding }
51075450f0dSDamon Ding
51175450f0dSDamon Ding dm_i2c_reg_write(dev, SII902X_SYS_CTRL_DATA, status);
51275450f0dSDamon Ding
51375450f0dSDamon Ding return 0;
51475450f0dSDamon Ding }
51575450f0dSDamon Ding
51675450f0dSDamon Ding /*
51775450f0dSDamon Ding * The purpose of sii902x_i2c_bypass_deselect is to disable the pass through
51875450f0dSDamon Ding * mode of the HDMI transmitter. Do not use regmap from within this function,
51975450f0dSDamon Ding * only use sii902x_*_unlocked functions to read/modify/write registers.
52075450f0dSDamon Ding * We are holding the parent adapter lock here, keep this in mind before
52175450f0dSDamon Ding * adding more i2c transactions.
52275450f0dSDamon Ding *
52375450f0dSDamon Ding * Also, since SII902X_SYS_CTRL_DATA is used with regmap_update_bits elsewhere
52475450f0dSDamon Ding * in this driver, we need to make sure that we only touch 0x1A[2:1] from
52575450f0dSDamon Ding * within sii902x_i2c_bypass_select and sii902x_i2c_bypass_deselect, and that
52675450f0dSDamon Ding * we leave the remaining bits as we have found them.
52775450f0dSDamon Ding */
sii902x_i2c_bypass_deselect(struct sii902x * sii902x)52875450f0dSDamon Ding static int sii902x_i2c_bypass_deselect(struct sii902x *sii902x)
52975450f0dSDamon Ding {
53075450f0dSDamon Ding struct udevice *dev = sii902x->dev;
53175450f0dSDamon Ding unsigned int retries;
53275450f0dSDamon Ding u8 status;
53375450f0dSDamon Ding int ret;
53475450f0dSDamon Ding
53575450f0dSDamon Ding /*
53675450f0dSDamon Ding * When the HDMI transmitter is in pass through mode, we need an
53775450f0dSDamon Ding * (undocumented) additional delay between STOP and START conditions
53875450f0dSDamon Ding * to guarantee the bus won't get stuck.
53975450f0dSDamon Ding */
54075450f0dSDamon Ding udelay(30);
54175450f0dSDamon Ding
54275450f0dSDamon Ding /*
54375450f0dSDamon Ding * Sometimes the I2C bus can stall after failure to use the
54475450f0dSDamon Ding * EDID channel. Retry a few times to see if things clear
54575450f0dSDamon Ding * up, else continue anyway.
54675450f0dSDamon Ding */
54775450f0dSDamon Ding retries = 5;
54875450f0dSDamon Ding do {
54975450f0dSDamon Ding status = dm_i2c_reg_read(dev, SII902X_SYS_CTRL_DATA);
55075450f0dSDamon Ding retries--;
55175450f0dSDamon Ding } while (status < 0 && retries);
55275450f0dSDamon Ding if (status < 0) {
55375450f0dSDamon Ding dev_err(dev, "failed to read status (%d)\n", status);
55475450f0dSDamon Ding return status;
55575450f0dSDamon Ding }
55675450f0dSDamon Ding
55775450f0dSDamon Ding ret = sii902x_reg_update_bits(sii902x, SII902X_SYS_CTRL_DATA,
55875450f0dSDamon Ding SII902X_SYS_CTRL_DDC_BUS_REQ |
55975450f0dSDamon Ding SII902X_SYS_CTRL_DDC_BUS_GRTD, 0);
56075450f0dSDamon Ding if (ret)
56175450f0dSDamon Ding return ret;
56275450f0dSDamon Ding
56375450f0dSDamon Ding ret = readx_poll_timeout(SII902X_READ_REG, SII902X_SYS_CTRL_DATA, status,
56475450f0dSDamon Ding !(status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
56575450f0dSDamon Ding SII902X_SYS_CTRL_DDC_BUS_GRTD)),
56675450f0dSDamon Ding SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS * 1000);
56775450f0dSDamon Ding if (ret) {
56875450f0dSDamon Ding dev_err(dev, "failed to release the i2c bus\n");
56975450f0dSDamon Ding return -ETIMEDOUT;
57075450f0dSDamon Ding }
57175450f0dSDamon Ding
57275450f0dSDamon Ding return 0;
57375450f0dSDamon Ding }
57475450f0dSDamon Ding
sii902x_i2c_xfer(struct ddc_adapter * adap,struct i2c_msg * msgs,int num)57575450f0dSDamon Ding static int sii902x_i2c_xfer(struct ddc_adapter *adap, struct i2c_msg *msgs, int num)
57675450f0dSDamon Ding {
57775450f0dSDamon Ding struct sii902x *sii902x = container_of(adap, struct sii902x, adap);
57875450f0dSDamon Ding struct udevice *dev = sii902x->dev;
57975450f0dSDamon Ding u8 addr = msgs[0].addr;
58075450f0dSDamon Ding int i;
58175450f0dSDamon Ding int ret;
58275450f0dSDamon Ding
58375450f0dSDamon Ding ret = sii902x_i2c_bypass_select(sii902x);
58475450f0dSDamon Ding if (ret)
58575450f0dSDamon Ding return -EINVAL;
58675450f0dSDamon Ding
58775450f0dSDamon Ding debug("i2c xfer: num: %d, addr: %#x\n", num, addr);
58875450f0dSDamon Ding
58975450f0dSDamon Ding for (i = 0; i < num; i++) {
59075450f0dSDamon Ding debug("xfer: num: %d/%d, len: %d, flags: %#x\n",
59175450f0dSDamon Ding i + 1, num, msgs[i].len, msgs[i].flags);
59275450f0dSDamon Ding
59375450f0dSDamon Ding ret = dm_i2c_xfer(dev, msgs, num);
59475450f0dSDamon Ding if (ret < 0)
59575450f0dSDamon Ding break;
59675450f0dSDamon Ding }
59775450f0dSDamon Ding
59875450f0dSDamon Ding ret = sii902x_i2c_bypass_deselect(sii902x);
59975450f0dSDamon Ding if (ret)
60075450f0dSDamon Ding return -EINVAL;
60175450f0dSDamon Ding
60275450f0dSDamon Ding if (!ret)
60375450f0dSDamon Ding ret = num;
60475450f0dSDamon Ding
60575450f0dSDamon Ding return ret;
60675450f0dSDamon Ding }
60775450f0dSDamon Ding
sii902x_init(struct sii902x * sii902x)60875450f0dSDamon Ding static int sii902x_init(struct sii902x *sii902x)
60975450f0dSDamon Ding {
61075450f0dSDamon Ding struct udevice *dev = sii902x->dev;
61175450f0dSDamon Ding u8 chipid[4];
61275450f0dSDamon Ding u8 status;
61375450f0dSDamon Ding int ret;
61475450f0dSDamon Ding
61575450f0dSDamon Ding sii902x_reset(sii902x);
61675450f0dSDamon Ding
61775450f0dSDamon Ding ret = dm_i2c_reg_write(dev, SII902X_REG_TPI_RQB, 0x0);
61875450f0dSDamon Ding if (ret) {
61975450f0dSDamon Ding dev_err(dev, "enable TPI mode failed %d\n", ret);
62075450f0dSDamon Ding return ret;
62175450f0dSDamon Ding }
62275450f0dSDamon Ding
62375450f0dSDamon Ding ret = dm_i2c_read(dev, SII902X_REG_CHIPID(0), chipid, 4);
62475450f0dSDamon Ding if (ret) {
62575450f0dSDamon Ding dev_err(dev, "regmap_read failed %d\n", ret);
62675450f0dSDamon Ding return ret;
62775450f0dSDamon Ding }
62875450f0dSDamon Ding
62975450f0dSDamon Ding if (chipid[0] != 0xb0) {
63075450f0dSDamon Ding dev_err(dev, "Invalid chipid: %02x (expecting 0xb0)\n",
63175450f0dSDamon Ding chipid[0]);
63275450f0dSDamon Ding return -EINVAL;
63375450f0dSDamon Ding }
63475450f0dSDamon Ding
63575450f0dSDamon Ding /* Clear all pending interrupts */
63675450f0dSDamon Ding status = dm_i2c_reg_read(dev, SII902X_INT_STATUS);
63775450f0dSDamon Ding dm_i2c_reg_write(dev, SII902X_INT_STATUS, status);
63875450f0dSDamon Ding
63975450f0dSDamon Ding sii902x->adap.i2c_bus = dev->parent;
64075450f0dSDamon Ding sii902x->adap.ddc_xfer = sii902x_i2c_xfer;
64175450f0dSDamon Ding
64275450f0dSDamon Ding sii902x->edid_data.mode_buf = malloc(MODE_LEN * sizeof(struct drm_display_mode));
64375450f0dSDamon Ding if (!sii902x->edid_data.mode_buf) {
64475450f0dSDamon Ding dev_err(dev, "failed to malloc mode buffer for edid\n");
64575450f0dSDamon Ding return -ENOMEM;
64675450f0dSDamon Ding }
64775450f0dSDamon Ding
64875450f0dSDamon Ding return 0;
64975450f0dSDamon Ding }
65075450f0dSDamon Ding
sii902x_probe(struct udevice * dev)65175450f0dSDamon Ding static int sii902x_probe(struct udevice *dev)
65275450f0dSDamon Ding {
65375450f0dSDamon Ding struct sii902x *sii902x = dev_get_priv(dev);
65475450f0dSDamon Ding struct rockchip_bridge *bridge = (struct rockchip_bridge *)dev_get_driver_data(dev);
65575450f0dSDamon Ding int val;
65675450f0dSDamon Ding int ret;
65775450f0dSDamon Ding
65875450f0dSDamon Ding sii902x->dev = dev;
65975450f0dSDamon Ding sii902x->bridge = bridge;
66075450f0dSDamon Ding bridge->dev = dev;
66175450f0dSDamon Ding
66275450f0dSDamon Ding ret = gpio_request_by_name(dev, "reset-gpio", 0,
66375450f0dSDamon Ding &sii902x->reset_gpio, GPIOD_IS_OUT);
66475450f0dSDamon Ding if (ret && ret != -ENOENT) {
66575450f0dSDamon Ding dev_err(dev, "Cannot get reset GPIO: %d\n", ret);
66675450f0dSDamon Ding return ret;
66775450f0dSDamon Ding }
66875450f0dSDamon Ding
66975450f0dSDamon Ding ret = gpio_request_by_name(dev, "enable-gpio", 0,
67075450f0dSDamon Ding &sii902x->enable_gpio, GPIOD_IS_OUT);
67175450f0dSDamon Ding if (ret && ret != -ENOENT) {
67275450f0dSDamon Ding dev_err(dev, "Cannot get enable GPIO: %d\n", ret);
67375450f0dSDamon Ding return ret;
67475450f0dSDamon Ding } else {
67575450f0dSDamon Ding dm_gpio_set_value(&sii902x->enable_gpio, 1);
67675450f0dSDamon Ding udelay(2000);
67775450f0dSDamon Ding }
67875450f0dSDamon Ding
67975450f0dSDamon Ding val = ofnode_read_u32_default(dev->node, "bus-format", -1);
68075450f0dSDamon Ding if (val < 0) {
68175450f0dSDamon Ding sii902x->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
68275450f0dSDamon Ding } else {
68375450f0dSDamon Ding switch (val) {
68475450f0dSDamon Ding case FORMAT_RGB_INPUT:
68575450f0dSDamon Ding sii902x->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
68675450f0dSDamon Ding break;
68775450f0dSDamon Ding case FORMAT_YCBCR422_INPUT:
68875450f0dSDamon Ding sii902x->bus_format = MEDIA_BUS_FMT_YUYV8_1X16;
68975450f0dSDamon Ding break;
69075450f0dSDamon Ding case FORMAT_YCBCR444_INPUT:
69175450f0dSDamon Ding sii902x->bus_format = MEDIA_BUS_FMT_YUV8_1X24;
69275450f0dSDamon Ding break;
69375450f0dSDamon Ding default:
69475450f0dSDamon Ding sii902x->bus_format = val;
69575450f0dSDamon Ding break;
69675450f0dSDamon Ding }
69775450f0dSDamon Ding }
698e846b8c2SDamon Ding bridge->bus_format = sii902x->bus_format;
69975450f0dSDamon Ding
70075450f0dSDamon Ding ret = sii902x_init(sii902x);
70175450f0dSDamon Ding if (ret < 0) {
70275450f0dSDamon Ding dev_err(dev, "Failed to init sii902x %d\n", ret);
70375450f0dSDamon Ding return ret;
70475450f0dSDamon Ding }
70575450f0dSDamon Ding
70675450f0dSDamon Ding printf("sii902x init successfully\n");
70775450f0dSDamon Ding
70875450f0dSDamon Ding return 0;
70975450f0dSDamon Ding }
71075450f0dSDamon Ding
71175450f0dSDamon Ding static struct rockchip_bridge sii902x_driver_data = {
71275450f0dSDamon Ding .funcs = &sii902x_bridge_funcs,
71375450f0dSDamon Ding };
71475450f0dSDamon Ding
71575450f0dSDamon Ding static const struct udevice_id sii902x_of_match[] = {
71675450f0dSDamon Ding {
71775450f0dSDamon Ding .compatible = "sil,sii9022",
71875450f0dSDamon Ding .data = (ulong)&sii902x_driver_data,
71975450f0dSDamon Ding },
72075450f0dSDamon Ding {}
72175450f0dSDamon Ding };
72275450f0dSDamon Ding
sii902x_get_timing(struct udevice * dev)72375450f0dSDamon Ding static int sii902x_get_timing(struct udevice *dev)
72475450f0dSDamon Ding {
72575450f0dSDamon Ding struct sii902x *sii902x = dev_get_priv(dev);
72675450f0dSDamon Ding struct rockchip_bridge *bridge = sii902x->bridge;
72775450f0dSDamon Ding struct display_state *state = bridge->state;
72875450f0dSDamon Ding struct connector_state *conn_state = &state->conn_state;
72975450f0dSDamon Ding struct drm_display_mode *mode = &conn_state->mode;
7309c170041SAlgea Cao int ret = 0;
73175450f0dSDamon Ding
7329c170041SAlgea Cao conn_state->edid = drm_do_get_edid(&sii902x->adap);
7339c170041SAlgea Cao if (conn_state->edid)
73475450f0dSDamon Ding ret = drm_add_edid_modes(&sii902x->edid_data, conn_state->edid);
7359c170041SAlgea Cao
7369c170041SAlgea Cao if (ret <= 0) {
73775450f0dSDamon Ding dev_err(dev, "Failed to get edid %d\n", ret);
73875450f0dSDamon Ding return ret;
73975450f0dSDamon Ding }
74075450f0dSDamon Ding
74175450f0dSDamon Ding drm_mode_max_resolution_filter(&sii902x->edid_data, &state->crtc_state.max_output);
74275450f0dSDamon Ding if (!drm_mode_prune_invalid(&sii902x->edid_data)) {
74375450f0dSDamon Ding dev_err(dev, "Failed to find valid display mode\n");
74475450f0dSDamon Ding return -EINVAL;
74575450f0dSDamon Ding }
74675450f0dSDamon Ding
74775450f0dSDamon Ding drm_mode_sort(&sii902x->edid_data);
74875450f0dSDamon Ding *mode = *sii902x->edid_data.preferred_mode;
74975450f0dSDamon Ding
75075450f0dSDamon Ding return 0;
75175450f0dSDamon Ding }
75275450f0dSDamon Ding
75375450f0dSDamon Ding struct video_bridge_ops sii902x_ops = {
75475450f0dSDamon Ding .get_timing = sii902x_get_timing,
75575450f0dSDamon Ding };
75675450f0dSDamon Ding
75775450f0dSDamon Ding U_BOOT_DRIVER(sii902x) = {
75875450f0dSDamon Ding .name = "sii902x",
75975450f0dSDamon Ding .id = UCLASS_VIDEO_BRIDGE,
76075450f0dSDamon Ding .of_match = sii902x_of_match,
76175450f0dSDamon Ding .probe = sii902x_probe,
76275450f0dSDamon Ding .priv_auto_alloc_size = sizeof(struct sii902x),
76375450f0dSDamon Ding .ops = &sii902x_ops,
76475450f0dSDamon Ding };
765