diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 453bfdba8ab4691ebb33c43de5659b4887acad25..0d6a6aa8f8c4b19e913bb06c5ba019997dd00cdc 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -279,4 +279,12 @@ config DRM_ITE_IT6263 help ITE IT6263 bridge chip driver. +config DRM_ITE_IT6161 + tristate "ITE IT6161 MIPI/HDMI bridge" + depends on OF + select DRM_KMS_HELPER + select REGMAP_I2C + help + ITE IT6161 bridge chip driver. + endmenu diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 17d70fc62f895c0c82c32781989ebd6b8be67cc6..a8420c81f773ebe6414b8bac7df762532fb4ae64 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -29,5 +29,6 @@ obj-y += analogix/ obj-y += cadence/ obj-y += synopsys/ obj-$(CONFIG_DRM_ITE_IT6263) += it6263.o +obj-$(CONFIG_DRM_ITE_IT6263) += it6161.o obj-$(CONFIG_DRM_SEC_MIPI_DSIM) += sec-dsim.o obj-$(CONFIG_DRM_NXP_SEIKO_43WVFIG) += nxp-seiko-43wvfig.o diff --git a/drivers/gpu/drm/bridge/it6161.c b/drivers/gpu/drm/bridge/it6161.c new file mode 100644 index 0000000000000000000000000000000000000000..85cbcee1b7e3174a614905a4a84073eed28fa1ce --- /dev/null +++ b/drivers/gpu/drm/bridge/it6161.c @@ -0,0 +1,2906 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2021 NXP + */ +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_edid.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <sound/hdmi-codec.h> + +#include "it6161.h" + +#define AUX_WAIT_TIMEOUT_MS 100 +#define DEFAULT_DRV_HOLD 0 + +#define RGB_24b 0x3E +#define RGB_18b 0x1E + +#define InvMCLK TRUE +#define PDREFCLK FALSE +#define SkipStg 4 +#define MShift 8 +#define PPSFFRdStg 0x04 +#define RegAutoSync TRUE + +#define LMDbgSel 0 /* 0~7 */ +#define InvPCLK FALSE +#define PDREFCNT 0 /* when PDREFCLK=TRUE, 0:div2, 1:div4, 2:div8, 3:divg16 */ +#define EnIOIDDQ FALSE +#define EnStb2Rst FALSE +#define EnExtStdby FALSE +#define EnStandby FALSE +#define MPLaneSwap FALSE +#define MPPNSwap FALSE /* TRUE: MTK , FALSE: Solomon */ + +/* PPI */ +#define EnContCK TRUE +#define HSSetNum 1 +#define EnDeSkew TRUE +#define PPIDbgSel 12 +#define RegIgnrNull 1 +#define RegIgnrBlk 1 +#define RegEnDummyECC 0 +#define EOTPSel 0 + +/* PPS option */ +#define EnMBPM FALSE /* enable MIPI Bypass Mode */ +#if (EnMBPM == TRUE) + #define PREC_Update TRUE /* enable P-timing update */ + #define MREC_Update TRUE /* enable M-timing update */ + #define EnTBPM TRUE /* enable HDMITX Bypass Mode */ +#else + #define PREC_Update FALSE + #define MREC_Update FALSE + #define EnTBPM FALSE +#endif + +#define REGSELDEF FALSE +#define EnHReSync FALSE +#define EnVReSync FALSE +#define EnFReSync FALSE +#define EnVREnh FALSE +#define EnVREnhSel 1 /* 0:Div2, 1:Div4, 2:Div8, 3:Div16, 4:Div32 */ +#define EnMAvg TRUE + +#define PShift 3 +#define EnFFAutoRst TRUE +#define RegEnSyncErr FALSE +#define EnTxCRC TRUE +#define TxCRCnum 0x20 + +#define ENABLE_MIPI_RX_EXTERNAL_CLOCK FALSE + +#define NRTXRCLK TRUE /* true:set TRCLK by self */ +#define RCLKFreqSel TRUE /* false: 10MHz(div1); true: 20 MHz(OSSDIV2) */ +#define ForceTxCLKStb TRUE + +#define HDMI_TX_PCLK_DIV2 FALSE + +#define HDMI_TX_MODE HDMI_TX_ENABLE_DE_ONLY + +enum hdmi_tx_mode { + HDMI_TX_NONE, + HDMI_TX_BY_PASS, + HDMI_TX_ENABLE_DE_ONLY, + HDMI_TX_ENABLE_PATTERN_GENERATOR, +}; + +enum it6161_active_level { + LOW, + HIGH, +}; + +const struct RegSetEntry HDMITX_Init_Table[] = { + {0x0F, 0x40, 0x00}, + /*PLL Reset */ + {0x62, 0x08, 0x00}, /* XP_RESETB */ + {0x64, 0x04, 0x00}, /* IP_RESETB */ + {0x0F, 0x01, 0x00}, /* bank 0 ;3 */ + {0x8D, 0xFF, CEC_I2C_SLAVE_ADDR}, /* EnCEC */ + {0xA9, 0x80, (EnTBPM << 7)}, + {0xBF, 0x80, (NRTXRCLK << 7)}, + + /* Initial Value */ + {0xF8, 0xFF, 0xC3}, + {0xF8, 0xFF, 0xA5}, + {0xF4, 0x0C, 0x00}, + {0xF3, 0x02, 0x00}, + {0xF8, 0xFF, 0xFF}, + {0x5A, 0x0C, 0x0C}, + {0xD1, 0x0A, ((ForceTxCLKStb) << 3) + 0x02}, + {0x5D, 0x04, ((RCLKFreqSel) << 2)}, + {0x65, 0x03, 0x00}, + {0x71, 0xF9, 0x18}, + {0xCF, 0xFF, 0x00}, + {0xd1, 0x02, 0x00}, + {0x59, 0xD0, 0x40}, + {0xE1, 0x20, 0x00}, + {0xF5, 0x40, 0x00}, + {0x05, 0xC0, 0x40}, /* Setup INT Pin: Active Low & Open-Drain */ + {0x0C, 0xFF, 0xFF}, + {0x0D, 0xFF, 0xFF}, + {0x0E, 0x03, 0x03}, /* Clear all Interrupt */ + {0x0C, 0xFF, 0x00}, + {0x0D, 0xFF, 0x00}, + {0x0E, 0x02, 0x00}, + {0x20, 0x01, 0x00} +}; + +const struct RegSetEntry HDMITX_DefaultVideo_Table[] = { + /* Config default output format */ + {0x72, 0xff, 0x00}, + {0x70, 0xff, 0x00}, +/* GenCSC\RGB2YUV_ITU709_16_235 */ + {0x72, 0xFF, 0x02}, + {0x73, 0xFF, 0x00}, + {0x74, 0xFF, 0x80}, + {0x75, 0xFF, 0x00}, + {0x76, 0xFF, 0xB8}, + {0x77, 0xFF, 0x05}, + {0x78, 0xFF, 0xB4}, + {0x79, 0xFF, 0x01}, + {0x7A, 0xFF, 0x93}, + {0x7B, 0xFF, 0x00}, + {0x7C, 0xFF, 0x49}, + {0x7D, 0xFF, 0x3C}, + {0x7E, 0xFF, 0x18}, + {0x7F, 0xFF, 0x04}, + {0x80, 0xFF, 0x9F}, + {0x81, 0xFF, 0x3F}, + {0x82, 0xFF, 0xD9}, + {0x83, 0xFF, 0x3C}, + {0x84, 0xFF, 0x10}, + {0x85, 0xFF, 0x3F}, + {0x86, 0xFF, 0x18}, + {0x87, 0xFF, 0x04}, + {0x88, 0xF0, 0x00}, +}; + +/* Config default HDMI Mode */ +const struct RegSetEntry HDMITX_SetHDMI_Table[] = { + {0xC0, 0x01, 0x01}, + {0xC1, 0x03, 0x03}, + {0xC6, 0x03, 0x03} +}; + +/* Config default avi infoframe */ +const struct RegSetEntry HDMITX_DefaultAVIInfo_Table[] = { + {0x0F, 0x01, 0x01}, + {0x58, 0xFF, 0x10}, + {0x59, 0xFF, 0x08}, + {0x5A, 0xFF, 0x00}, + {0x5B, 0xFF, 0x00}, + {0x5C, 0xFF, 0x00}, + {0x5D, 0xFF, 0x57}, + {0x5E, 0xFF, 0x00}, + {0x5F, 0xFF, 0x00}, + {0x60, 0xFF, 0x00}, + {0x61, 0xFF, 0x00}, + {0x62, 0xFF, 0x00}, + {0x63, 0xFF, 0x00}, + {0x64, 0xFF, 0x00}, + {0x65, 0xFF, 0x00}, + {0x0F, 0x01, 0x00}, + {0xCD, 0x03, 0x03} +}; + +/* Config default audio infoframe */ +const struct RegSetEntry HDMITX_DeaultAudioInfo_Table[] = { + {0x0F, 0x01, 0x01}, + {0x68, 0xFF, 0x00}, + {0x69, 0xFF, 0x00}, + {0x6A, 0xFF, 0x00}, + {0x6B, 0xFF, 0x00}, + {0x6C, 0xFF, 0x00}, + {0x6D, 0xFF, 0x71}, + {0x0F, 0x01, 0x00}, + {0xCE, 0x03, 0x03} +}; + +const struct RegSetEntry HDMITX_Aud_CHStatus_LPCM_20bit_48Khz[] = { + {0x0F, 0x01, 0x01}, + {0x33, 0xFF, 0x00}, + {0x34, 0xFF, 0x18}, + {0x35, 0xFF, 0x00}, + {0x91, 0xFF, 0x00}, + {0x92, 0xFF, 0x00}, + {0x93, 0xFF, 0x01}, + {0x94, 0xFF, 0x00}, + {0x98, 0xFF, 0x02}, + {0x99, 0xFF, 0xDA}, + {0x0F, 0x01, 0x00} +}; + +const struct RegSetEntry HDMITX_AUD_SPDIF_2ch_24bit[] = { + {0x0F, 0x11, 0x00}, + {0x04, 0x14, 0x04}, + {0xE0, 0xFF, 0xD1}, + {0xE1, 0xFF, 0x01}, + {0xE2, 0xFF, 0xE4}, + {0xE3, 0xFF, 0x10}, + {0xE4, 0xFF, 0x00}, + {0xE5, 0xFF, 0x00}, + {0x04, 0x14, 0x00} +}; + +const struct RegSetEntry HDMITX_PwrOn_Table[] = { + /* PwrOn RCLK , IACLK ,TXCLK */ + {0x0F, 0x70, 0x00}, + /* PLL PwrOn */ + /* PwrOn DRV */ + {0x61, 0x20, 0x00}, + /* PwrOn XPLL */ + {0x62, 0x44, 0x00}, + /* PwrOn IPLL */ + {0x64, 0x40, 0x00}, + /* PLL Reset OFF */ + /* DRV_RST */ + {0x61, 0x10, 0x00}, + /* XP_RESETB */ + {0x62, 0x08, 0x08}, + /* IP_RESETB */ + {0x64, 0x04, 0x04} +}; + +struct it6161 { + struct drm_bridge bridge; + struct drm_connector connector; + struct i2c_client *i2c_mipi_rx; + struct i2c_client *i2c_hdmi_tx; + struct i2c_client *i2c_cec; + struct device_node *host_node; + struct mipi_dsi_device *dsi; + struct mutex mode_lock; + + struct regmap *regmap_mipi_rx; + struct regmap *regmap_hdmi_tx; + struct regmap *regmap_cec; + + u32 it6161_addr_hdmi_tx; + u32 it6161_addr_cec; + struct completion wait_edid_complete; + + u32 hdmi_tx_rclk; /* kHz */ + u32 hdmi_tx_pclk; + u32 mipi_rx_mclk; + u32 mipi_rx_rclk; + u32 mipi_rx_pclk; + + struct drm_display_mode mipi_rx_p_display_mode; + struct drm_display_mode hdmi_tx_display_mode; + struct drm_display_mode source_display_mode; + struct hdmi_avi_infoframe source_avi_infoframe; + + struct edid *edid; + u32 vic; + u8 mipi_rx_lane_count; + bool enable_drv_hold; + u8 hdmi_tx_output_color_space; + u8 hdmi_tx_input_color_space; + u8 hdmi_tx_mode; + u8 support_audio; + bool hdmi_mode; + u8 bAudioChannelEnable; +}; + +struct it6161 *it6161; + +static const struct regmap_range it6161_mipi_rx_bridge_volatile_ranges[] = { + {.range_min = 0, .range_max = 0xFF}, +}; + +static const struct regmap_access_table it6161_mipi_rx_bridge_volatile_table = { + .yes_ranges = it6161_mipi_rx_bridge_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(it6161_mipi_rx_bridge_volatile_ranges), +}; + +static const struct regmap_config it6161_mipi_rx_bridge_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_table = &it6161_mipi_rx_bridge_volatile_table, + .cache_type = REGCACHE_NONE, +}; + +static const struct regmap_range it6161_hdmi_tx_bridge_volatile_ranges[] = { + {.range_min = 0, .range_max = 0xFF}, +}; + +static const struct regmap_access_table it6161_hdmi_tx_bridge_volatile_table = { + .yes_ranges = it6161_hdmi_tx_bridge_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(it6161_hdmi_tx_bridge_volatile_ranges), +}; + +static const struct regmap_config it6161_hdmi_tx_bridge_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_table = &it6161_hdmi_tx_bridge_volatile_table, + .cache_type = REGCACHE_NONE, +}; + +static const struct regmap_range it6161_cec_bridge_volatile_ranges[] = { + {.range_min = 0, .range_max = 0xFF}, +}; + +static const struct regmap_access_table it6161_cec_bridge_volatile_table = { + .yes_ranges = it6161_cec_bridge_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(it6161_cec_bridge_volatile_ranges), +}; + +static const struct regmap_config it6161_cec_bridge_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_table = &it6161_cec_bridge_volatile_table, + .cache_type = REGCACHE_NONE, +}; + +static int it6161_mipi_rx_read(struct it6161 *it6161, u32 reg_addr) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + u32 value; + int err; + + err = regmap_read(it6161->regmap_mipi_rx, reg_addr, &value); + if (err < 0) { + DRM_DEV_ERROR(dev, "mipi rx read failed reg[0x%x] err: %d", reg_addr, err); + return err; + } + + return value; +} + +static int mipi_rx_read_word(struct it6161 *it6161, u32 reg) +{ + int val_0, val_1; + + val_0 = it6161_mipi_rx_read(it6161, reg); + if (val_0 < 0) + return val_0; + + val_1 = it6161_mipi_rx_read(it6161, reg + 1); + if (val_1 < 0) + return val_1; + + return (val_1 << 8) | val_0; +} + +static int it6161_mipi_rx_write(struct it6161 *it6161, u32 addr, u32 val) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + int err; + + err = regmap_write(it6161->regmap_mipi_rx, addr, val); + if (err < 0) { + DRM_DEV_ERROR(dev, "mipi rx write failed reg[0x%x] = 0x%x err = %d", + addr, val, err); + return err; + } + + return 0; +} + +static int it6161_mipi_rx_set_bits(struct it6161 *it6161, u32 reg, + u32 mask, u32 value) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + int err; + + err = regmap_update_bits(it6161->regmap_mipi_rx, reg, mask, value); + if (err < 0) { + DRM_DEV_ERROR(dev, "mipi rx set reg[0x%x] = 0x%x mask = 0x%x failed err %d", + reg, value, mask, err); + return err; + } + + return 0; +} + +static int it6161_hdmi_tx_read(struct it6161 *it6161, u32 reg_addr) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + u32 value; + int err; + + err = regmap_read(it6161->regmap_hdmi_tx, reg_addr, &value); + if (err < 0) { + DRM_DEV_ERROR(dev, "hdmi tx read failed reg[0x%x] err: %d", + reg_addr, err); + return err; + } + + return value; +} + +static int hdmi_tx_read_word(struct it6161 *it6161, u32 reg) +{ + int val_0, val_1; + + val_0 = it6161_hdmi_tx_read(it6161, reg); + if (val_0 < 0) + return val_0; + + val_1 = it6161_hdmi_tx_read(it6161, reg + 1); + if (val_1 < 0) + return val_1; + + return (val_1 << 8) | val_0; +} + +static int it6161_hdmi_tx_write(struct it6161 *it6161, u32 addr, u32 val) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + int err; + + err = regmap_write(it6161->regmap_hdmi_tx, addr, val); + + if (err < 0) { + DRM_DEV_ERROR(dev, "hdmi tx write failed reg[0x%x] = 0x%x err = %d", + addr, val, err); + return err; + } + + return 0; +} + +static int it6161_hdmi_tx_set_bits(struct it6161 *it6161, u32 reg, + u32 mask, u32 value) +{ + int err; + struct device *dev = &it6161->i2c_mipi_rx->dev; + + err = regmap_update_bits(it6161->regmap_hdmi_tx, reg, mask, value); + if (err < 0) { + DRM_DEV_ERROR(dev, "hdmi tx set reg[0x%x] = 0x%x mask = 0x%x failed err %d", + reg, value, mask, err); + return err; + } + + return 0; +} + +static inline int it6161_hdmi_tx_change_bank(struct it6161 *it6161, int x) +{ + return it6161_hdmi_tx_set_bits(it6161, 0x0F, 0x03, x & 0x03); +} + +static int it6161_cec_read(struct it6161 *it6161, u32 addr) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + u32 value; + int err; + + err = regmap_read(it6161->regmap_cec, addr, &value); + if (err < 0) { + DRM_DEV_ERROR(dev, "cec read failed reg[0x%x] err: %d", addr, err); + return err; + } + + return value; +} + +static int it6161_cec_write(struct it6161 *it6161, u32 addr, u32 val) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + int err; + + err = regmap_write(it6161->regmap_cec, addr, val); + if (err < 0) { + DRM_DEV_ERROR(dev, "cec write failed reg[0x%x] = 0x%x err = %d", addr, val, err); + return err; + } + + return 0; +} + +static inline struct it6161 *connector_to_it6161(struct drm_connector *c) +{ + return container_of(c, struct it6161, connector); +} + +static inline struct it6161 *bridge_to_it6161(struct drm_bridge *bridge) +{ + return container_of(bridge, struct it6161, bridge); +} + +static void mipi_rx_logic_reset(struct it6161 *it6161) +{ + it6161_mipi_rx_set_bits(it6161, 0x05, 0x08, 0x08); +} + +static void mipi_rx_logic_reset_release(struct it6161 *it6161) +{ + it6161_mipi_rx_set_bits(it6161, 0x05, 0x08, 0x00); +} + +static void hdmi_tx_logic_reset(struct it6161 *it6161) +{ + it6161_hdmi_tx_set_bits(it6161, 0x04, 0x20, 0x20); +} + +static void it6161_mipi_rx_int_mask_disable(struct it6161 *it6161) +{ + it6161_mipi_rx_set_bits(it6161, 0x0F, 0x03, 0x00); + it6161_mipi_rx_write(it6161, 0x09, 0x00); + it6161_mipi_rx_write(it6161, 0x0A, 0x00); + it6161_mipi_rx_write(it6161, 0x0B, 0x00); +} + +static void it6161_mipi_rx_int_mask_enable(struct it6161 *it6161) +{ + it6161_hdmi_tx_set_bits(it6161, 0x0F, 0x03, 0x00); + it6161_mipi_rx_write(it6161, 0x09, 0x11); + it6161_mipi_rx_write(it6161, 0x0A, 0xFF); + it6161_mipi_rx_write(it6161, 0x0B, 0x3F); +} + +static void it6161_hdmi_tx_int_mask_disable(struct it6161 *it6161) +{ + it6161_mipi_rx_set_bits(it6161, 0x0F, 0x03, 0x00); + it6161_hdmi_tx_write(it6161, REG_TX_INT_MASK1, 0xFF); + it6161_hdmi_tx_write(it6161, REG_TX_INT_MASK3, 0xFF); +} + +static void it6161_hdmi_tx_int_mask_enable(struct it6161 *it6161) +{ + it6161_hdmi_tx_set_bits(it6161, 0x0F, 0x03, 0x00); + it6161_hdmi_tx_write(it6161, REG_TX_INT_MASK1, + ~(B_TX_AUDIO_OVFLW_MASK | B_TX_DDC_FIFO_ERR_MASK | + B_TX_DDC_BUS_HANG_MASK | B_TX_HPD_MASK | B_TX_RXSEN_MASK)); + it6161_hdmi_tx_write(it6161, REG_TX_INT_MASK3, ~B_TX_VIDSTABLE_MASK); +} + +static void it6161_hdmi_tx_write_table(struct it6161 *it6161, + const struct RegSetEntry table[], int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (table[i].mask == 0 && table[i].value == 0) + msleep(table[i].offset); + else if (table[i].mask == 0xFF) + it6161_hdmi_tx_write(it6161, table[i].offset, table[i].value); + else + it6161_hdmi_tx_set_bits(it6161, table[i].offset, table[i].mask, table[i].value); + } +} + +static inline void it6161_set_interrupts_active_level(enum it6161_active_level level) +{ + it6161_mipi_rx_set_bits(it6161, 0x0D, 0x02, level == HIGH ? 0x02 : 0x00); + it6161_hdmi_tx_set_bits(it6161, 0x05, 0xC0, level == HIGH ? 0x80 : 0x40); +} + +static void hdmi_tx_init(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "nit6161 %s\n", __func__); + + it6161_hdmi_tx_write_table(it6161, HDMITX_Init_Table, + ARRAY_SIZE(HDMITX_Init_Table)); + it6161_hdmi_tx_write_table(it6161, HDMITX_PwrOn_Table, + ARRAY_SIZE(HDMITX_PwrOn_Table)); + it6161_hdmi_tx_write_table(it6161, HDMITX_DefaultVideo_Table, + ARRAY_SIZE(HDMITX_DefaultVideo_Table)); + it6161_hdmi_tx_write_table(it6161, HDMITX_SetHDMI_Table, + ARRAY_SIZE(HDMITX_SetHDMI_Table)); + it6161_hdmi_tx_write_table(it6161, HDMITX_DefaultAVIInfo_Table, + ARRAY_SIZE(HDMITX_DefaultAVIInfo_Table)); + it6161_hdmi_tx_write_table(it6161, HDMITX_DeaultAudioInfo_Table, + ARRAY_SIZE(HDMITX_DeaultAudioInfo_Table)); + it6161_hdmi_tx_write_table(it6161, HDMITX_Aud_CHStatus_LPCM_20bit_48Khz, + ARRAY_SIZE(HDMITX_Aud_CHStatus_LPCM_20bit_48Khz)); + it6161_hdmi_tx_write_table(it6161, HDMITX_AUD_SPDIF_2ch_24bit, + ARRAY_SIZE(HDMITX_AUD_SPDIF_2ch_24bit)); +} + +static bool mipi_rx_get_m_video_stable(struct it6161 *it6161) +{ + return it6161_mipi_rx_read(it6161, 0x0D) & 0x10; +} + +static bool mipi_rx_get_p_video_stable(struct it6161 *it6161) +{ + return it6161_mipi_rx_read(it6161, 0x0D) & 0x20; +} + +static void mipi_rx_setup_polarity(struct it6161 *it6161) +{ + struct drm_display_mode *display_mode = &it6161->source_display_mode; + u8 polarity; + + polarity = ((display_mode->flags & DRM_MODE_FLAG_PHSYNC) == DRM_MODE_FLAG_PHSYNC) ? 0x01 : 0x00; + polarity |= ((display_mode->flags & DRM_MODE_FLAG_PVSYNC) == DRM_MODE_FLAG_PVSYNC) ? 0x02 : 0x00; + + it6161_mipi_rx_set_bits(it6161, 0x4E, 0x03, polarity); +} + +static void mipi_rx_afe_configuration(struct it6161 *it6161, u8 data_id) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + u8 MPLaneNum = (it6161->mipi_rx_lane_count - 1); + + DRM_DEV_DEBUG_DRIVER(dev, "nafe configuration data_id: 0x%02x", data_id); + + if (data_id == RGB_18b) { + if (MPLaneNum == 3) + /* MPPCLKSel = 1; 4-lane : MCLK = 1/1 PCLK */ + it6161_mipi_rx_set_bits(it6161, 0x80, 0x1F, 0x02); + else if (MPLaneNum == 1) + /* MPPCLKSel = 6; 2-lane : MCLK = 1/1 PCLK */ + it6161_mipi_rx_set_bits(it6161, 0x80, 0x1F, 0x05); + else if (MPLaneNum == 0) + /* MPPCLKSel = 8; 1-lane : MCLK = 3/4 PCLK */ + it6161_mipi_rx_set_bits(it6161, 0x80, 0x1F, 0x08); + } else { + if (MPLaneNum == 3) + /* MPPCLKSel = 1; 4-lane : MCLK = 3/4 PCLK */ + it6161_mipi_rx_set_bits(it6161, 0x80, 0x1F, 0x02); + else if (MPLaneNum == 1) + /* MPPCLKSel = 3; 2-lane : MCLK = 3/4 PCLK */ + it6161_mipi_rx_set_bits(it6161, 0x80, 0x1F, 0x05); + else if (MPLaneNum == 0) + /* MPPCLKSel = 5; 1-lane : MCLK = 3/4 PCLK */ + it6161_mipi_rx_set_bits(it6161, 0x80, 0x1F, 0x0b); + } +} + +static void mipi_rx_configuration(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + u8 mipi_lane_config = (it6161->mipi_rx_lane_count - 1); + + DRM_DEV_DEBUG_DRIVER(dev, "n%s, MIPI_LANE=%d\n", __func__, it6161->mipi_rx_lane_count); + + it6161_mipi_rx_set_bits(it6161, 0x10, 0x0F, 0x0F); + msleep(1); + it6161_mipi_rx_set_bits(it6161, 0x10, 0x0F, 0x00); + + mipi_rx_logic_reset(it6161); + msleep(1); + mipi_rx_logic_reset_release(it6161); + + it6161_mipi_rx_int_mask_disable(it6161); + + /* setup INT pin: active low */ + it6161_mipi_rx_set_bits(it6161, 0x0d, 0x02, 0x00); + + it6161_mipi_rx_set_bits(it6161, 0x0C, 0x0F, + (MPLaneSwap << 3) + (MPPNSwap << 2) + mipi_lane_config); + + it6161_mipi_rx_set_bits(it6161, 0x11, 0x3F, + (EnIOIDDQ << 5) + (EnStb2Rst << 4) + (EnExtStdby << 3) + + (EnStandby << 2) + (InvPCLK << 1) + InvMCLK); + + it6161_mipi_rx_set_bits(it6161, 0x12, 0x03, (PDREFCNT << 1) + PDREFCLK); + + it6161_mipi_rx_set_bits(it6161, 0x18, 0xf7, + (RegEnSyncErr << 7) + (SkipStg << 4) + HSSetNum); + it6161_mipi_rx_set_bits(it6161, 0x19, 0xf3, + (PPIDbgSel << 4) + (EnContCK << 1) + EnDeSkew); + it6161_mipi_rx_set_bits(it6161, 0x20, 0xf7, + (EOTPSel << 4) + (RegEnDummyECC << 2) + (RegIgnrBlk << 1) + RegIgnrNull); + it6161_mipi_rx_set_bits(it6161, 0x21, 0x07, LMDbgSel); + + it6161_mipi_rx_set_bits(it6161, 0x44, 0x3a, + (MREC_Update << 5) + (PREC_Update << 4) + (REGSELDEF << 3) + (RegAutoSync << 1)); + it6161_mipi_rx_set_bits(it6161, 0x4B, 0x1f, + (EnFReSync << 4) + (EnVREnh << 3) + EnVREnhSel); + it6161_mipi_rx_write(it6161, 0x4C, PPSFFRdStg); + it6161_mipi_rx_set_bits(it6161, 0x4D, 0x01, (PPSFFRdStg >> 8) & 0x01); + it6161_mipi_rx_set_bits(it6161, 0x4E, 0x0C, + (EnVReSync << 3) + (EnHReSync << 2)); + it6161_mipi_rx_set_bits(it6161, 0x4F, 0x03, EnFFAutoRst); + + it6161_mipi_rx_set_bits(it6161, 0x70, 0x01, EnMAvg); + it6161_mipi_rx_write(it6161, 0x72, MShift); + it6161_mipi_rx_write(it6161, 0x73, PShift); + it6161_mipi_rx_set_bits(it6161, 0x80, 0x20, ENABLE_MIPI_RX_EXTERNAL_CLOCK << 5); + + it6161_mipi_rx_write(it6161, 0x21, 0x00); + it6161_mipi_rx_set_bits(it6161, 0x84, 0x70, 0x00); + + it6161_mipi_rx_set_bits(it6161, 0xA0, 0x01, EnMBPM); + + /* enable auto detect format */ + it6161_mipi_rx_set_bits(it6161, 0x21, 0x08, 0x08); + + it6161_mipi_rx_set_bits(it6161, 0x70, 0x01, EnMAvg); + /* Video Clock Domain Reset */ + it6161_mipi_rx_set_bits(it6161, 0x05, 0x02, 0x02); + + if (EnMBPM) { + /* HRS offset */ + it6161_mipi_rx_write(it6161, 0xA1, 0x00); + /* VRS offset */ + it6161_mipi_rx_write(it6161, 0xA2, 0x00); + it6161_mipi_rx_write(it6161, 0xA3, 0x08); + it6161_mipi_rx_write(it6161, 0xA5, 0x04); + } + + if (REGSELDEF == false) { + it6161_mipi_rx_set_bits(it6161, 0x31, 0x80, 0x00); + it6161_mipi_rx_set_bits(it6161, 0x33, 0x80, 0x00); + it6161_mipi_rx_set_bits(it6161, 0x35, 0x80, 0x00); + it6161_mipi_rx_set_bits(it6161, 0x37, 0x80, 0x00); + it6161_mipi_rx_set_bits(it6161, 0x39, 0x80, 0x00); + it6161_mipi_rx_set_bits(it6161, 0x3A, 0x80, 0x00); + it6161_mipi_rx_set_bits(it6161, 0x3C, 0x80, 0x00); + it6161_mipi_rx_set_bits(it6161, 0x3E, 0x80, 0x00); + it6161_mipi_rx_set_bits(it6161, 0x41, 0x80, 0x00); + it6161_mipi_rx_set_bits(it6161, 0x43, 0x80, 0x00); + } +} + +static void mipi_rx_init(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "n%s\n", __func__); + mipi_rx_configuration(it6161); + /* Enable MPRX clock domain */ + it6161_mipi_rx_set_bits(it6161, 0x05, 0x03, 0x00); +} + +static void hdmi_tx_video_reset(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "n%s, reg04: 0x%02x reg05: 0x%02x reg6: 0x%02x reg07: 0x%02x reg08: 0x%02x reg0e: 0x%02x", __func__, + it6161_hdmi_tx_read(it6161, 0x04), + it6161_hdmi_tx_read(it6161, 0x05), + it6161_hdmi_tx_read(it6161, 0x06), + it6161_hdmi_tx_read(it6161, 0x07), + it6161_hdmi_tx_read(it6161, 0x08), + it6161_hdmi_tx_read(it6161, 0x0e)); + + it6161_hdmi_tx_set_bits(it6161, REG_TX_SW_RST, B_HDMITX_VID_RST, B_HDMITX_VID_RST); + it6161_hdmi_tx_set_bits(it6161, REG_TX_SW_RST, B_HDMITX_VID_RST, 0x00); + msleep(10); +} + +/* DDC master will set to be host */ +static void it6161_hdmi_tx_clear_ddc_fifo(struct it6161 *it6161) +{ + it6161_hdmi_tx_change_bank(it6161, 0); + it6161_hdmi_tx_write(it6161, REG_TX_DDC_MASTER_CTRL, + B_TX_MASTERDDC | B_TX_MASTERHOST); + it6161_hdmi_tx_write(it6161, REG_TX_DDC_CMD, CMD_FIFO_CLR); + it6161_hdmi_tx_set_bits(it6161, REG_TX_DDC_MASTER_CTRL, B_TX_MASTERHOST, 0x00); +} + +static void hdmi_tx_generate_blank_timing(struct it6161 *it6161) +{ + struct drm_display_mode *display_mode = &it6161->hdmi_tx_display_mode; + bool force_hdmi_tx_clock_stable = true; + bool force_hdmi_tx_video_stable = true; + bool hdmi_tx_by_pass_mode = false; + bool de_generation = false; + bool enable_de_only = true; + u8 polarity; + u16 hsync_start, hsync_end, vsync_start, vsync_end, htotal, hde_start, vtotal; + u16 vsync_start_2nd, vsync_end_2nd, vsync_rising_at_h_2nd; + + polarity = + ((display_mode->flags & DRM_MODE_FLAG_PHSYNC) == DRM_MODE_FLAG_PHSYNC) ? 0x02 : 0x00; + polarity |= + ((display_mode->flags & DRM_MODE_FLAG_PVSYNC) == DRM_MODE_FLAG_PVSYNC) ? 0x04 : 0x00; + + hsync_start = display_mode->hsync_start - display_mode->hdisplay - 1; + hsync_end = hsync_start + display_mode->hsync_end - display_mode->hsync_start; + vsync_rising_at_h_2nd = hsync_start + display_mode->htotal / 2; + hde_start = display_mode->htotal - display_mode->hsync_start; + + it6161_hdmi_tx_set_bits(it6161, 0xD1, 0x0C, + force_hdmi_tx_clock_stable << 3 | force_hdmi_tx_video_stable << 2); + it6161_hdmi_tx_set_bits(it6161, 0xA9, 0x80, hdmi_tx_by_pass_mode << 7); + it6161_hdmi_tx_set_bits(it6161, 0x90, 0x01, de_generation); + it6161_hdmi_tx_write(it6161, 0x91, vsync_rising_at_h_2nd >> 4); + it6161_hdmi_tx_set_bits(it6161, 0x90, 0xF0, (vsync_rising_at_h_2nd & 0x00F) << 4); + it6161_hdmi_tx_set_bits(it6161, 0x90, 0x06, polarity); + it6161_hdmi_tx_write(it6161, 0x95, (u8) hsync_start); + it6161_hdmi_tx_write(it6161, 0x96, (u8) hsync_end); + it6161_hdmi_tx_write(it6161, 0x97, (hsync_end & 0x0F00) >> 4 | hsync_start >> 8); + + vsync_start = display_mode->vsync_start - display_mode->vdisplay; + vsync_end = display_mode->vsync_end - display_mode->vdisplay; + + if ((display_mode->flags & DRM_MODE_FLAG_INTERLACE) != DRM_MODE_FLAG_INTERLACE) { + vsync_start_2nd = 0x0FFF; + vsync_end_2nd = 0x3F; + vtotal = display_mode->vtotal - 1; + it6161_hdmi_tx_set_bits(it6161, 0xA5, 0x10, 0x00); + } else { + vtotal = display_mode->vtotal * 2; + it6161_hdmi_tx_set_bits(it6161, 0xA5, 0x10, 0x10); + } + it6161_hdmi_tx_write(it6161, 0xA0, (u8) vsync_start); + it6161_hdmi_tx_write(it6161, 0xA1, (vsync_end & 0x0F) << 4 | vsync_start >> 8); + it6161_hdmi_tx_write(it6161, 0xA2, (u8) vsync_start_2nd); + it6161_hdmi_tx_write(it6161, 0xA6, (vsync_end_2nd & 0xF0) | vsync_end >> 4); + it6161_hdmi_tx_write(it6161, 0xA3, (vsync_end_2nd & 0x0F) << 4 | vsync_start_2nd >> 8); + it6161_hdmi_tx_write(it6161, 0xA4, vsync_rising_at_h_2nd); + + it6161_hdmi_tx_set_bits(it6161, 0xB1, 0x51, + (hsync_end & 0x1000) >> 6 | (hsync_start & 0x1000) >> 8 | hde_start >> 12); + it6161_hdmi_tx_set_bits(it6161, 0xA5, 0x2F, + enable_de_only << 5 | vsync_rising_at_h_2nd >> 8); + it6161_hdmi_tx_set_bits(it6161, 0xB2, 0x05, + (vsync_rising_at_h_2nd & 0x1000) >> 10 | (vsync_rising_at_h_2nd & 0x1000) >> 12); + + htotal = display_mode->htotal - 1; + it6161_hdmi_tx_set_bits(it6161, 0x90, 0xF0, (htotal & 0x0F) << 4); + it6161_hdmi_tx_write(it6161, 0x91, (htotal & 0x0FF0) >> 4); + it6161_hdmi_tx_set_bits(it6161, 0xB2, 0x01, (htotal & 0x1000) >> 12); + it6161_hdmi_tx_write(it6161, 0x98, vtotal & 0x0FF); + it6161_hdmi_tx_write(it6161, 0x99, (vtotal & 0xF00) >> 8); +} + +/* force abort DDC and reset DDC bus */ +static void it6161_hdmi_tx_abort_ddc(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + u8 sw_reset, ddc_master, retry = 2; + u8 uc, timeout, i; + + DRM_DEV_DEBUG_DRIVER(dev, "n%s", __func__); + /* save the sw reset, ddc master and cp desire setting */ + sw_reset = it6161_hdmi_tx_read(it6161, REG_TX_SW_RST); + ddc_master = it6161_hdmi_tx_read(it6161, REG_TX_DDC_MASTER_CTRL); + + it6161_hdmi_tx_write(it6161, REG_TX_SW_RST, sw_reset | B_TX_HDCP_RST_HDMITX); + it6161_hdmi_tx_write(it6161, REG_TX_DDC_MASTER_CTRL, B_TX_MASTERDDC | B_TX_MASTERHOST); + + /* do abort DDC */ + for (i = 0; i < retry; i++) { + it6161_hdmi_tx_write(it6161, REG_TX_DDC_CMD, CMD_DDC_ABORT); + it6161_hdmi_tx_write(it6161, REG_TX_DDC_CMD, CMD_GEN_SCLCLK); + + for (timeout = 0; timeout < 200; timeout++) { + uc = it6161_hdmi_tx_read(it6161, REG_TX_DDC_STATUS); + if (uc & B_TX_DDC_DONE) + break; + + if (uc & (B_TX_DDC_NOACK | B_TX_DDC_WAITBUS | B_TX_DDC_ARBILOSE)) { + DRM_DEV_ERROR(dev, "nit6161_hdmi_tx_abort_ddc Fail by reg16=%02X\n", (int)uc); + break; + } + /* delay 1 ms to stable */ + msleep(1); + } + } +} + +static bool hdmi_tx_get_video_state(struct it6161 *it6161) +{ + return B_TXVIDSTABLE & it6161_hdmi_tx_read(it6161, REG_TX_SYS_STATUS); +} + +static inline bool hdmi_tx_get_sink_hpd(struct it6161 *it6161) +{ + return it6161_hdmi_tx_read(it6161, REG_TX_SYS_STATUS) & B_TX_HPDETECT; +} + +static bool it6161_ddc_op_finished(struct it6161 *it6161) +{ + int reg16 = it6161_hdmi_tx_read(it6161, REG_TX_DDC_STATUS); + + if (reg16 < 0) + return false; + + return (reg16 & B_TX_DDC_DONE) == B_TX_DDC_DONE; +} + +static int it6161_ddc_wait(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + int status; + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(AUX_WAIT_TIMEOUT_MS) + 1; + + while (!it6161_ddc_op_finished(it6161)) { + if (time_after(jiffies, timeout)) { + DRM_DEV_ERROR(dev, "nTimed out waiting AUX to finish"); + return -ETIMEDOUT; + } + usleep_range(1000, 2000); + } + + status = it6161_hdmi_tx_read(it6161, REG_TX_DDC_STATUS); + if (status < 0) { + DRM_DEV_ERROR(dev, "nFailed to read DDC channel: 0x%02x", status); + return status; + } + + if (status & B_TX_DDC_DONE) + return 0; + else { + DRM_DEV_ERROR(dev, "nDDC error: 0x%02x", status); + return -EIO; + } +} + +static void hdmi_tx_ddc_operation(struct it6161 *it6161, u8 addr, u8 offset, u8 size, + u8 segment, u8 cmd) +{ + size = min_t(u8, size, DDC_FIFO_MAXREQ); + it6161_hdmi_tx_change_bank(it6161, 0); + it6161_hdmi_tx_write(it6161, REG_TX_DDC_MASTER_CTRL, B_TX_MASTERDDC | B_TX_MASTERHOST); + it6161_hdmi_tx_write(it6161, REG_TX_DDC_HEADER, addr); + it6161_hdmi_tx_write(it6161, REG_TX_DDC_REQOFF, offset); + it6161_hdmi_tx_write(it6161, REG_TX_DDC_REQCOUNT, size); + it6161_hdmi_tx_write(it6161, REG_TX_DDC_EDIDSEG, segment); + it6161_hdmi_tx_write(it6161, REG_TX_DDC_CMD, cmd); +} + +static int it6161_ddc_get_edid_operation(struct it6161 *it6161, u8 *buffer, + u8 segment, u8 offset, u8 size) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + u8 status, i; + + if (!buffer) + return -ENOMEM; + + if (it6161_hdmi_tx_read(it6161, REG_TX_INT_STAT1) & B_TX_INT_DDC_BUS_HANG) { + DRM_DEV_ERROR(dev, "nCalled it6161_hdmi_tx_abort_ddc()"); + it6161_hdmi_tx_abort_ddc(it6161); + } + + it6161_hdmi_tx_clear_ddc_fifo(it6161); + status = it6161_ddc_wait(it6161); + if (status < 0) + goto error; + + hdmi_tx_ddc_operation(it6161, DDC_EDID_ADDRESS, offset, size, segment, CMD_EDID_READ); + status = it6161_ddc_wait(it6161); + if (status < 0) + goto error; + + for (i = 0; i < size; i++) { + status = it6161_hdmi_tx_read(it6161, REG_TX_DDC_READFIFO); + if (status < 0) + goto error; + + buffer[i] = status; + } + + return i; + +error: + return status; +} + +static int it6161_get_edid_block(void *data, u8 *buf, u32 block_num, size_t len) +{ + struct it6161 *it6161 = data; + u8 offset, ret, step = 8; + + step = min_t(u8, step, DDC_FIFO_MAXREQ); + + for (offset = 0; offset < len; offset += step) { + ret = it6161_ddc_get_edid_operation(it6161, buf + offset, + block_num / 2, (block_num % 2) * EDID_LENGTH + offset, step); + if (ret < 0) + return ret; + } + return 0; +} + +static void hdmi_tx_set_capability_from_edid_parse(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + struct drm_display_info *info = &it6161->connector.display_info; + + it6161->hdmi_mode = drm_detect_hdmi_monitor(it6161->edid); + it6161->support_audio = drm_detect_monitor_audio(it6161->edid); + if (it6161->hdmi_tx_output_color_space == F_MODE_YUV444) { + if ((info->color_formats & DRM_COLOR_FORMAT_YCRCB444) != DRM_COLOR_FORMAT_YCRCB444) { + it6161->hdmi_tx_output_color_space &= ~F_MODE_CLRMOD_MASK; + it6161->hdmi_tx_output_color_space |= F_MODE_RGB444; + } + } + + if (it6161->hdmi_tx_output_color_space == F_MODE_YUV422) { + if ((info->color_formats & DRM_COLOR_FORMAT_YCRCB422) != DRM_COLOR_FORMAT_YCRCB422) { + it6161->hdmi_tx_output_color_space &= ~F_MODE_CLRMOD_MASK; + it6161->hdmi_tx_output_color_space |= F_MODE_RGB444; + } + } + DRM_DEV_INFO(dev, "n%s mode, monitor %ssupport audio, outputcolormode:%d color_formats:0x%08x color_depth:%d", + it6161->hdmi_mode ? "HDMI" : "DVI", + it6161->support_audio ? "" : "not ", + it6161->hdmi_tx_output_color_space, + info->color_formats, + info->bpc); + + it6161->support_audio = 1; + it6161->hdmi_mode = 1; + + if ((info->color_formats & DRM_COLOR_FORMAT_RGB444) == DRM_COLOR_FORMAT_RGB444) + DRM_DEV_INFO(dev, "nsupport RGB444 output"); + if ((info->color_formats & DRM_COLOR_FORMAT_YCRCB444) == DRM_COLOR_FORMAT_YCRCB444) + DRM_DEV_INFO(dev, "nsupport YUV444 output"); + if ((info->color_formats & DRM_COLOR_FORMAT_YCRCB422) == DRM_COLOR_FORMAT_YCRCB422) + DRM_DEV_INFO(dev, "nsupport YUV422 output"); +} + +static void it6161_variable_config(struct it6161 *it6161) +{ + it6161->hdmi_tx_mode = HDMI_TX_MODE; + it6161->mipi_rx_lane_count = MIPI_RX_LANE_COUNT; +} + +static int it6161_get_modes(struct drm_connector *connector) +{ + struct it6161 *it6161 = connector_to_it6161(connector); + int err, num_modes = 0, i, retry = 3; + struct device *dev = &it6161->i2c_mipi_rx->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "n%s start", __func__); + + mutex_lock(&it6161->mode_lock); + reinit_completion(&it6161->wait_edid_complete); + + for (i = 0; i < retry; i++) { + it6161->edid = drm_do_get_edid(&it6161->connector, it6161_get_edid_block, it6161); + if (it6161->edid) + break; + } + if (!it6161->edid) { + DRM_DEV_ERROR(dev, "Failed to read EDID"); + goto unlock; + } + + err = drm_connector_update_edid_property(connector, it6161->edid); + if (err) { + DRM_DEV_ERROR(dev, "Failed to update EDID property: %d", err); + goto unlock; + } + + num_modes = drm_add_edid_modes(connector, it6161->edid); + +unlock: + complete(&it6161->wait_edid_complete); + DRM_DEV_DEBUG_DRIVER(dev, "nedid mode number:%d", num_modes); + mutex_unlock(&it6161->mode_lock); + + return num_modes; +} + +static const struct drm_connector_helper_funcs it6161_connector_helper_funcs = { + .get_modes = it6161_get_modes, +}; + +static enum drm_connector_status it6161_detect(struct drm_connector *connector, bool force) +{ + struct it6161 *it6161 = connector_to_it6161(connector); + struct device *dev = &it6161->i2c_hdmi_tx->dev; + enum drm_connector_status status = connector_status_disconnected; + bool hpd; + + hpd = hdmi_tx_get_sink_hpd(it6161); + if (hpd) { + it6161_variable_config(it6161); + status = connector_status_connected; + } + DRM_DEV_INFO(dev, "n%s, hpd:%s", __func__, hpd ? "high" : "low"); + + it6161_set_interrupts_active_level(HIGH); + it6161_mipi_rx_int_mask_enable(it6161); + it6161_hdmi_tx_int_mask_enable(it6161); + + return status; +} + +static const struct drm_connector_funcs it6161_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = it6161_detect, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int it6161_attach_dsi(struct it6161 *it6161) +{ + struct mipi_dsi_host *host; + struct mipi_dsi_device *dsi; + int ret = 0; + const struct mipi_dsi_device_info info = {.type = "it6161", }; + struct device *dev = &it6161->i2c_hdmi_tx->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "n%s\n", __func__); + host = of_find_mipi_dsi_host_by_node(it6161->host_node); + if (!host) { + DRM_DEV_ERROR(dev, "it6161 failed to find dsi host\n"); + return -EPROBE_DEFER; + } + + dsi = mipi_dsi_device_register_full(host, &info); + if (IS_ERR(dsi)) { + DRM_DEV_ERROR(dev, "it6161 failed to create dsi device\n"); + ret = PTR_ERR(dsi); + goto err_dsi_device; + } + + it6161->dsi = dsi; + + dsi->lanes = MIPI_RX_LANE_COUNT; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE; + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + DRM_DEV_ERROR(dev, "it6161 failed to attach dsi to host\n"); + goto err_dsi_attach; + } + + return 0; + +err_dsi_attach: + mipi_dsi_device_unregister(dsi); +err_dsi_device: + return ret; +} + +static int it6161_connector_init(struct drm_bridge *bridge, struct it6161 *it6161) +{ + struct device *dev; + int ret; + + dev = &it6161->i2c_mipi_rx->dev; + + if (!bridge->encoder) { + DRM_DEV_ERROR(dev, "Parent encoder object not found"); + return -ENODEV; + } + + it6161->connector.polled = DRM_CONNECTOR_POLL_HPD; + + ret = drm_connector_init(bridge->dev, &it6161->connector, + &it6161_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); + if (ret < 0) { + DRM_DEV_ERROR(dev, "Failed to initialize connector: %d", ret); + return ret; + } + + drm_connector_helper_add(&it6161->connector, &it6161_connector_helper_funcs); + drm_connector_attach_encoder(&it6161->connector, bridge->encoder); + + return 0; +} + +static int it6161_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct it6161 *it6161 = bridge_to_it6161(bridge); + int ret; + + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { + ret = it6161_connector_init(bridge, it6161); + if (ret < 0) + return ret; + } + + ret = it6161_attach_dsi(it6161); + + return ret; +} + +static void it6161_detach_dsi(struct it6161 *it6161) +{ + mipi_dsi_detach(it6161->dsi); + mipi_dsi_device_unregister(it6161->dsi); +} + +static void it6161_bridge_detach(struct drm_bridge *bridge) +{ + struct it6161 *it6161 = bridge_to_it6161(bridge); + + drm_connector_unregister(&it6161->connector); + drm_connector_cleanup(&it6161->connector); + it6161_detach_dsi(it6161); +} + +static enum drm_mode_status +it6161_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + if (mode->clock > 108000) + return MODE_CLOCK_HIGH; + + /* TODO, Only 480p60 work with imx8ulp now */ + if (mode->vdisplay > 480) + return MODE_BAD_VVALUE; + + return MODE_OK; +} + +static void it6161_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct it6161 *it6161 = bridge_to_it6161(bridge); + struct device *dev = &it6161->i2c_hdmi_tx->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "n mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode)); + DRM_DEV_DEBUG_DRIVER(dev, "nadj mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(adjusted_mode)); + + memcpy(&it6161->source_display_mode, adjusted_mode, sizeof(struct drm_display_mode)); + memcpy(&it6161->hdmi_tx_display_mode, mode, sizeof(struct drm_display_mode)); +} + +static void it6161_bridge_enable(struct drm_bridge *bridge) +{ + struct it6161 *it6161 = bridge_to_it6161(bridge); + struct device *dev = &it6161->i2c_hdmi_tx->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "n%s start", __func__); + mipi_rx_init(it6161); + hdmi_tx_init(it6161); + it6161_set_interrupts_active_level(HIGH); + it6161_mipi_rx_int_mask_enable(it6161); + it6161_hdmi_tx_int_mask_enable(it6161); + +} + +static void it6161_bridge_disable(struct drm_bridge *bridge) +{ + struct it6161 *it6161 = bridge_to_it6161(bridge); + struct device *dev = &it6161->i2c_hdmi_tx->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "n%s start", __func__); + mipi_rx_logic_reset(it6161); + hdmi_tx_logic_reset(it6161); + it6161_set_interrupts_active_level(HIGH); + it6161_mipi_rx_int_mask_enable(it6161); + it6161_hdmi_tx_int_mask_enable(it6161); + kfree(it6161->edid); + it6161->edid = NULL; +} + +static enum drm_connector_status it6161_bridge_detect(struct drm_bridge *bridge) +{ + struct it6161 *it6161 = bridge_to_it6161(bridge); + enum drm_connector_status status = connector_status_disconnected; + bool hpd = hdmi_tx_get_sink_hpd(it6161); + struct device *dev = &it6161->i2c_hdmi_tx->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "n%s, hpd:%s", __func__, hpd ? "high" : "low"); + + if (hpd) { + it6161_variable_config(it6161); + status = connector_status_connected; + } + + it6161_set_interrupts_active_level(HIGH); + it6161_mipi_rx_int_mask_enable(it6161); + it6161_hdmi_tx_int_mask_enable(it6161); + + return status; +} + +static struct edid *it6161_bridge_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct it6161 *it6161 = bridge_to_it6161(bridge); + + it6161->edid = drm_do_get_edid(&it6161->connector, it6161_get_edid_block, it6161); + hdmi_tx_set_capability_from_edid_parse(it6161); + return it6161->edid; +} + +static const struct drm_bridge_funcs it6161_bridge_funcs = { + .attach = it6161_bridge_attach, + .detach = it6161_bridge_detach, + .mode_valid = it6161_bridge_mode_valid, + .mode_set = it6161_bridge_mode_set, + .enable = it6161_bridge_enable, + .disable = it6161_bridge_disable, + .detect = it6161_bridge_detect, + .get_edid = it6161_bridge_get_edid, +}; + +static bool it6161_check_device_ready(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + u8 Vendor_ID[2], Device_ID[2]; + + Vendor_ID[0] = it6161_mipi_rx_read(it6161, 0x00); + Vendor_ID[1] = it6161_mipi_rx_read(it6161, 0x01); + Device_ID[0] = it6161_mipi_rx_read(it6161, 0x02); + Device_ID[1] = it6161_mipi_rx_read(it6161, 0x03); + if (Vendor_ID[0] == 0x54 && Vendor_ID[1] == 0x49 && + Device_ID[0] == 0x61 && Device_ID[1] == 0x61) { + DRM_DEV_INFO(dev, "Find it6161 revision: 0x%2x", + (u32) it6161_mipi_rx_read(it6161, 0x04)); + return true; + } + DRM_DEV_INFO(dev, "nfind it6161 Fail"); + return false; +} + +static u32 hdmi_tx_calc_rclk(struct it6161 *it6161) +{ + int i; + long sum = 0, RCLKCNT, TimeLoMax, retry = 5; + struct device *dev = &it6161->i2c_hdmi_tx->dev; + + /* Init CEC */ + it6161_hdmi_tx_write(it6161, 0x8D, (CEC_I2C_SLAVE_ADDR|0x01)); + msleep(10); + + for (i = 0; i < retry; i++) { + it6161_cec_write(it6161, 0x09, 1); + msleep(100); + it6161_cec_write(it6161, 0x09, 0); + RCLKCNT = it6161_cec_read(it6161, 0x47); + RCLKCNT <<= 8; + RCLKCNT |= it6161_cec_read(it6161, 0x46); + RCLKCNT <<= 8; + RCLKCNT |= it6161_cec_read(it6161, 0x45); + sum += RCLKCNT; + } + + sum /= retry; + RCLKCNT = sum / 1000; + it6161_cec_write(it6161, 0x0C, (RCLKCNT & 0xFF)); + + /* Disable CEC */ + it6161_hdmi_tx_set_bits(it6161, 0x8D, 0x01, 0x00); + + it6161->hdmi_tx_rclk = (sum << 4) / 108; + DRM_DEV_DEBUG_DRIVER(dev, "nhdmi tx rclk = %d.%d MHz", it6161->hdmi_tx_rclk / 1000, + it6161->hdmi_tx_rclk % 1000); + + TimeLoMax = (sum << 4) / 10; /* 10*TxRCLK; */ + if (TimeLoMax > 0x3FFFF) + TimeLoMax = 0x3FFFF; + + DRM_DEV_DEBUG_DRIVER(dev, "nTimeLoMax = %08lx\n", TimeLoMax); + it6161_hdmi_tx_write(it6161, 0x47, (TimeLoMax & 0xFF)); + it6161_hdmi_tx_write(it6161, 0x48, ((TimeLoMax & 0xFF00) >> 8)); + it6161_hdmi_tx_set_bits(it6161, 0x49, 0x03, ((TimeLoMax & 0x30000) >> 16)); + + return it6161->hdmi_tx_rclk; +} + +u32 hdmi_tx_calc_pclk(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + u8 uc, RCLKFreqSelRead; + int div, i; + u32 sum, count; + + it6161_hdmi_tx_change_bank(it6161, 0); + + RCLKFreqSelRead = (it6161_hdmi_tx_read(it6161, 0x5D) & 0x04) >> 2; + /* PCLK Count Pre-Test */ + it6161_hdmi_tx_set_bits(it6161, 0xD7, 0xF0, 0x80); + msleep(1); + it6161_hdmi_tx_set_bits(it6161, 0xD7, 0x80, 0x00); + + count = it6161_hdmi_tx_read(it6161, 0xD7) & 0xF; + count <<= 8; + count |= it6161_hdmi_tx_read(it6161, 0xD8); + + if (RCLKFreqSelRead) + count <<= 1; + + for (div = 7; div > 0; div--) + if (count < (1 << (11 - div))) + break; + + if (div < 0) + div = 0; + + it6161_hdmi_tx_set_bits(it6161, 0xD7, 0x70, div << 4); + + uc = it6161_hdmi_tx_read(it6161, 0xD7) & 0x7F; + for (i = 0, sum = 0; i < 100; i++) { + it6161_hdmi_tx_write(it6161, 0xD7, uc | 0x80); + msleep(1); + it6161_hdmi_tx_write(it6161, 0xD7, uc); + + count = it6161_hdmi_tx_read(it6161, 0xD7) & 0xF; + count <<= 8; + count |= it6161_hdmi_tx_read(it6161, 0xD8); + if (RCLKFreqSelRead) + count <<= 1; + sum += count; + } + sum /= 100; + count = sum; + + it6161->hdmi_tx_pclk = it6161->hdmi_tx_rclk * 128 / count * 16; /* 128*16=2048 */ + it6161->hdmi_tx_pclk *= (1 << div); + + DRM_DEV_DEBUG_DRIVER(dev, "nhdmi tx pclk = %d.%d MHz", + it6161->hdmi_tx_pclk / 1000, it6161->hdmi_tx_pclk % 1000); + return it6161->hdmi_tx_pclk; +} + +static void hdmi_tx_get_display_mode(struct it6161 *it6161) +{ + struct drm_display_mode *display_mode = &it6161->hdmi_tx_display_mode; + struct device *dev = &it6161->i2c_hdmi_tx->dev; + u32 hsyncpol, vsyncpol, interlaced; + u32 htotal, hdes, hdee, hsyncw, hactive, hfront_porch, H2ndVRRise; + u32 vtotal, vdes, vdee, vsyncw, vactive, vfront_porch, vdes2nd, vdee2nd; + u32 vsyncw2nd, VRS2nd, vdew2nd, vfph2nd, vbph2nd; + u8 rega9; + + DRM_DEV_DEBUG_DRIVER(dev, "n%s\n", __func__); + hdmi_tx_calc_rclk(it6161); + hdmi_tx_calc_pclk(it6161); + + /* enable video timing read back */ + it6161_hdmi_tx_set_bits(it6161, 0xA8, 0x08, 0x08); + + rega9 = it6161_hdmi_tx_read(it6161, 0xa9); + hsyncpol = rega9 & 0x01; + vsyncpol = (rega9 & 0x02) >> 1; + interlaced = (rega9 & 0x04) >> 2; + + htotal = hdmi_tx_read_word(it6161, 0x98) & 0x0FFF; + hdes = hdmi_tx_read_word(it6161, 0x90) & 0x0FFF; + hdee = hdmi_tx_read_word(it6161, 0x92) & 0x0FFF; + hsyncw = hdmi_tx_read_word(it6161, 0x94) & 0x0FFF; + hactive = hdee - hdes; + hfront_porch = htotal - hdee; + + vtotal = hdmi_tx_read_word(it6161, 0xA6) & 0x0FFF; + vdes = hdmi_tx_read_word(it6161, 0x9C) & 0x0FFF; + vdee = hdmi_tx_read_word(it6161, 0x9E) & 0x0FFF; + vsyncw = it6161_hdmi_tx_read(it6161, 0xA0); + vactive = vdee - vdes; + vfront_porch = (interlaced == 0x01) ? (vtotal / 2 - vdee) : (vtotal - vdee); + + display_mode->clock = it6161->hdmi_tx_pclk; + display_mode->hdisplay = hactive; + display_mode->hsync_start = hactive + hfront_porch; + display_mode->hsync_end = hactive + hfront_porch + hsyncw; + display_mode->htotal = htotal; + display_mode->vdisplay = vactive; + display_mode->vsync_start = vactive + vfront_porch; + display_mode->vsync_end = vactive + vfront_porch + vsyncw; + display_mode->vtotal = vtotal; + display_mode->flags = + ((hsyncpol == 0x01) ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC) | + ((vsyncpol == 0x01) ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC) + | ((interlaced == 0x01) ? DRM_MODE_FLAG_INTERLACE : 0x00); + + if (interlaced) { + vdes2nd = hdmi_tx_read_word(it6161, 0xA2) & 0x0FFF; + vdee2nd = hdmi_tx_read_word(it6161, 0xA4) & 0x0FFF; + VRS2nd = hdmi_tx_read_word(it6161, 0xB1) & 0x0FFF; + vsyncw2nd = it6161_hdmi_tx_read(it6161, 0xA1); + H2ndVRRise = hdmi_tx_read_word(it6161, 0x96) & 0x0FFF; + vdew2nd = vdee2nd - vdes2nd; + vfph2nd = VRS2nd - vdee; + vbph2nd = vdes2nd - VRS2nd - vsyncw2nd; + DRM_DEV_DEBUG_DRIVER(dev, "nvdew2nd = %d\n", vdew2nd); + DRM_DEV_DEBUG_DRIVER(dev, "nvfph2nd = %d\n", vfph2nd); + DRM_DEV_DEBUG_DRIVER(dev, "nVSyncW2nd = %d\n", vsyncw2nd); + DRM_DEV_DEBUG_DRIVER(dev, "nvbph2nd = %d\n", vbph2nd); + DRM_DEV_DEBUG_DRIVER(dev, "nH2ndVRRise = %d\n", H2ndVRRise); + } + + /* disable video timing read back */ + it6161_hdmi_tx_set_bits(it6161, 0xA8, 0x08, 0x00); +} + +static void it6161_hdmi_tx_set_av_mute(struct it6161 *it6161, u8 bEnable) +{ + it6161_hdmi_tx_change_bank(it6161, 0); + it6161_hdmi_tx_set_bits(it6161, REG_TX_GCP, + B_TX_SETAVMUTE, bEnable ? B_TX_SETAVMUTE : 0); + it6161_hdmi_tx_write(it6161, REG_TX_PKT_GENERAL_CTRL, + B_TX_ENABLE_PKT | B_TX_REPEAT_PKT); +} + +static void hdmi_tx_setup_pclk_div2(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + + if (HDMI_TX_PCLK_DIV2) { + DRM_DEV_DEBUG_DRIVER(dev, "nPCLK Divided by 2 mode"); + it6161_hdmi_tx_set_bits(it6161, REG_TX_INPUT_MODE, + B_TX_PCLKDIV2, B_TX_PCLKDIV2); + } +} + +/************************************************************************* + * Function: hdmi_tx_setup_csc + * Parameter: input_mode - + * D[1:0] - Color Mode + * D[4] - Colorimetry 0: ITU_BT601 1: ITU_BT709 + * D[5] - Quantization 0: 0_255 1: 16_235 + * D[6] - Up/Dn Filter 'Required' + * 0: no up/down filter + * 1: enable up/down filter when csc need. + * D[7] - Dither Filter 'Required' + * 0: no dither enabled. + * 1: enable dither and dither free go "when required". + * output_mode - + * D[1:0] - Color mode. + * Return: N/A + * Remark: reg72~reg8D will be programmed depended the input with table + * **********************************************************************/ +static void hdmi_tx_setup_csc(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + u8 ucData, csc, i; + u8 filter = 0; /* filter is for Video CTRL DN_FREE_GO,EN_DITHER,and ENUDFILT */ + u8 input_mode = it6161->hdmi_tx_input_color_space; + u8 output_mode = it6161->hdmi_tx_output_color_space; + u8 *ptable; + + /* (1) YUV422 in,RGB/YUV444 output (Output is 8-bit,input is 12-bit) + * (2) YUV444/422 in,RGB output (CSC enable,and output is not YUV422) + * (3) RGB in,YUV444 output (CSC enable,and output is not YUV422) + * + * YUV444/RGB24 <-> YUV422 need set up/down filter. + */ + DRM_DEV_DEBUG_DRIVER(dev, "nhdmi_tx_setup_csc(u8 input_mode = %x,u8 output_mode = %x)\n", + (int)input_mode, (int)output_mode); + + switch (input_mode & F_MODE_CLRMOD_MASK) { + /* YUV444 INPUT */ + case F_MODE_YUV444: + switch (output_mode & F_MODE_CLRMOD_MASK) { + case F_MODE_YUV444: + csc = B_HDMITX_CSC_BYPASS; + break; + case F_MODE_YUV422: + /* YUV444 to YUV422 need up/down filter for processing. */ + if (input_mode & F_VIDMODE_EN_UDFILT) + filter |= B_TX_EN_UDFILTER; + csc = B_HDMITX_CSC_BYPASS; + break; + case F_MODE_RGB444: + csc = B_HDMITX_CSC_YUV2RGB; + /* YUV444 to RGB24 need dither */ + if (input_mode & F_VIDMODE_EN_DITHER) + filter |= B_TX_EN_DITHER | B_TX_DNFREE_GO; + break; + } + break; + + /* YUV422 INPUT */ + case F_MODE_YUV422: + switch (output_mode & F_MODE_CLRMOD_MASK) { + case F_MODE_YUV444: + csc = B_HDMITX_CSC_BYPASS; + if (input_mode & F_VIDMODE_EN_UDFILT) + filter |= B_TX_EN_UDFILTER; + else if (input_mode & F_VIDMODE_EN_DITHER) + filter |= B_TX_EN_DITHER | B_TX_DNFREE_GO; + break; + case F_MODE_YUV422: + csc = B_HDMITX_CSC_BYPASS; + break; + case F_MODE_RGB444: + csc = B_HDMITX_CSC_YUV2RGB; + if (input_mode & F_VIDMODE_EN_UDFILT) + filter |= B_TX_EN_UDFILTER; + else if (input_mode & F_VIDMODE_EN_DITHER) + filter |= B_TX_EN_DITHER | B_TX_DNFREE_GO; + break; + } + break; + + /* RGB444 INPUT */ + case F_MODE_RGB444: + switch (output_mode & F_MODE_CLRMOD_MASK) { + case F_MODE_YUV444: + csc = B_HDMITX_CSC_RGB2YUV; + if (input_mode & F_VIDMODE_EN_DITHER) + filter |= B_TX_EN_DITHER | B_TX_DNFREE_GO; + break; + case F_MODE_YUV422: + if (input_mode & F_VIDMODE_EN_UDFILT) + filter |= B_TX_EN_UDFILTER; + else if (input_mode & F_VIDMODE_EN_DITHER) + filter |= B_TX_EN_DITHER | B_TX_DNFREE_GO; + csc = B_HDMITX_CSC_RGB2YUV; + break; + case F_MODE_RGB444: + csc = B_HDMITX_CSC_BYPASS; + break; + } + break; + } + + /* set the CSC metrix registers by colorimetry and quantization */ + if (csc == B_HDMITX_CSC_RGB2YUV) { + switch (input_mode & (F_VIDMODE_ITU709 | F_VIDMODE_16_235)) { + case F_VIDMODE_ITU709 | F_VIDMODE_16_235: + ptable = bCSCMtx_RGB2YUV_ITU709_16_235; + break; + case F_VIDMODE_ITU709 | F_VIDMODE_0_255: + ptable = bCSCMtx_RGB2YUV_ITU709_0_255; + break; + case F_VIDMODE_ITU601 | F_VIDMODE_16_235: + ptable = bCSCMtx_RGB2YUV_ITU601_16_235; + break; + case F_VIDMODE_ITU601 | F_VIDMODE_0_255: + default: + ptable = bCSCMtx_RGB2YUV_ITU601_0_255; + break; + } + } + + if (csc == B_HDMITX_CSC_YUV2RGB) { + switch (input_mode & (F_VIDMODE_ITU709 | F_VIDMODE_16_235)) { + case F_VIDMODE_ITU709 | F_VIDMODE_16_235: + ptable = bCSCMtx_YUV2RGB_ITU709_16_235; + break; + case F_VIDMODE_ITU709 | F_VIDMODE_0_255: + ptable = bCSCMtx_YUV2RGB_ITU709_0_255; + break; + case F_VIDMODE_ITU601 | F_VIDMODE_16_235: + ptable = bCSCMtx_YUV2RGB_ITU601_16_235; + break; + case F_VIDMODE_ITU601 | F_VIDMODE_0_255: + default: + ptable = bCSCMtx_YUV2RGB_ITU601_0_255; + break; + } + } + + if (csc == B_HDMITX_CSC_BYPASS) + it6161_hdmi_tx_set_bits(it6161, 0xF, 0x10, 0x10); + else { + if (ptable != NULL) + for (i = 0; i < SIZEOF_CSCMTX; i++) + it6161_hdmi_tx_write(it6161, REG_TX_CSC_YOFF + i, ptable[i]); + it6161_hdmi_tx_set_bits(it6161, 0xF, 0x10, 0x00); + } + + ucData = it6161_hdmi_tx_read(it6161, + REG_TX_CSC_CTRL) & ~(M_TX_CSC_SEL | + B_TX_DNFREE_GO | + B_TX_EN_DITHER | + B_TX_EN_UDFILTER); + ucData |= filter | csc; + + it6161_hdmi_tx_write(it6161, REG_TX_CSC_CTRL, ucData); +} + +static void hdmi_tx_setup_afe(struct it6161 *it6161, u8 level) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + + it6161_hdmi_tx_write(it6161, REG_TX_AFE_DRV_CTRL, B_TX_AFE_DRV_RST); + switch (level) { + case PCLK_HIGH: + it6161_hdmi_tx_set_bits(it6161, 0x62, 0x90, 0x80); + it6161_hdmi_tx_set_bits(it6161, 0x64, 0x89, 0x80); + it6161_hdmi_tx_set_bits(it6161, 0x68, 0x10, 0x00); + it6161_hdmi_tx_set_bits(it6161, 0x66, 0x80, 0x80); + break; + default: + it6161_hdmi_tx_set_bits(it6161, 0x62, 0x90, 0x10); + it6161_hdmi_tx_set_bits(it6161, 0x64, 0x89, 0x09); + it6161_hdmi_tx_set_bits(it6161, 0x68, 0x10, 0x10); + break; + } + DRM_DEV_DEBUG_DRIVER(dev, "nsetup afe: %s", level ? "high" : "low"); +} + +static void hdmi_tx_fire_afe(struct it6161 *it6161) +{ + it6161_hdmi_tx_change_bank(it6161, 0x00); + it6161_hdmi_tx_write(it6161, REG_TX_AFE_DRV_CTRL, 0x00); +} + +static void hdmi_tx_disable_video_output(struct it6161 *it6161) +{ + it6161_hdmi_tx_set_bits(it6161, REG_TX_SW_RST, B_HDMITX_VID_RST, B_HDMITX_VID_RST); + it6161_hdmi_tx_write(it6161, REG_TX_AFE_DRV_CTRL, B_TX_AFE_DRV_RST | B_TX_AFE_DRV_PWD); + it6161_hdmi_tx_set_bits(it6161, 0x62, 0x90, 0x00); + it6161_hdmi_tx_set_bits(it6161, 0x64, 0x89, 0x00); +} + +static void hdmi_tx_enable_video_output(struct it6161 *it6161, u8 level) +{ + it6161_hdmi_tx_write(it6161, REG_TX_SW_RST, + B_HDMITX_AUD_RST | B_TX_AREF_RST | B_TX_HDCP_RST_HDMITX); + it6161_hdmi_tx_change_bank(it6161, 1); + it6161_hdmi_tx_write(it6161, REG_TX_AVIINFO_DB1, 0x00); + it6161_hdmi_tx_change_bank(it6161, 0); + + if (it6161->hdmi_mode) + it6161_hdmi_tx_set_av_mute(it6161, true); + + hdmi_tx_setup_pclk_div2(it6161); + hdmi_tx_setup_csc(it6161); + it6161_hdmi_tx_write(it6161, REG_TX_HDMI_MODE, + it6161->hdmi_mode ? B_TX_HDMI_MODE : B_TX_DVI_MODE); + hdmi_tx_setup_afe(it6161, level); + hdmi_tx_fire_afe(it6161); +} + +static void setHDMITX_ChStat(struct it6161 *it6161, u8 ucIEC60958ChStat[]) +{ + u8 uc; + + it6161_hdmi_tx_change_bank(it6161, 1); + uc = (ucIEC60958ChStat[0] << 1) & 0x7C; + it6161_hdmi_tx_write(it6161, REG_TX_AUDCHST_MODE, uc); + it6161_hdmi_tx_write(it6161, REG_TX_AUDCHST_CAT, ucIEC60958ChStat[1]); + it6161_hdmi_tx_write(it6161, REG_TX_AUDCHST_SRCNUM, ucIEC60958ChStat[2] & 0xF); + it6161_hdmi_tx_write(it6161, REG_TX_AUD0CHST_CHTNUM, (ucIEC60958ChStat[2] >> 4) & 0xF); + it6161_hdmi_tx_write(it6161, REG_TX_AUDCHST_CA_FS, ucIEC60958ChStat[3]); + it6161_hdmi_tx_write(it6161, REG_TX_AUDCHST_OFS_WL, ucIEC60958ChStat[4]); + it6161_hdmi_tx_change_bank(it6161, 0); +} + +static void setHDMITX_LPCMAudio(u8 AudioSrcNum, u8 AudSWL, u8 bAudInterface) +{ + u8 AudioEnable, AudioFormat, bTDMSetting; + + switch (AudSWL) { + case 16: + AudioEnable |= M_TX_AUD_16BIT; + break; + case 18: + AudioEnable |= M_TX_AUD_18BIT; + break; + case 20: + AudioEnable |= M_TX_AUD_20BIT; + break; + case 24: + default: + AudioEnable |= M_TX_AUD_24BIT; + break; + } + if (bAudInterface == SPDIF) { + AudioFormat &= ~0x40; + AudioEnable |= B_TX_AUD_SPDIF | B_TX_AUD_EN_I2S0; + } else { + AudioFormat |= 0x40; + switch (AudioSrcNum) { + case 4: + AudioEnable |= + B_TX_AUD_EN_I2S3 | B_TX_AUD_EN_I2S2 | + B_TX_AUD_EN_I2S1 | B_TX_AUD_EN_I2S0; + break; + + case 3: + AudioEnable |= + B_TX_AUD_EN_I2S2 | B_TX_AUD_EN_I2S1 | B_TX_AUD_EN_I2S0; + break; + + case 2: + AudioEnable |= B_TX_AUD_EN_I2S1 | B_TX_AUD_EN_I2S0; + break; + + case 1: + default: + AudioFormat &= ~0x40; + AudioEnable |= B_TX_AUD_EN_I2S0; + break; + + } + } + AudioFormat |= 0x01; + it6161->bAudioChannelEnable = AudioEnable; + + it6161_hdmi_tx_change_bank(it6161, 0); + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL0, AudioEnable & 0xF0); + + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL1, AudioFormat); + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_FIFOMAP, 0xE4); + + if (bAudInterface == SPDIF) + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL3, B_TX_CHSTSEL); + else + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL3, 0); + + it6161_hdmi_tx_write(it6161, REG_TX_AUD_SRCVALID_FLAT, 0x00); + it6161_hdmi_tx_write(it6161, REG_TX_AUD_HDAUDIO, 0x00); + + if (bAudInterface == SPDIF) { + u8 i; + + it6161_hdmi_tx_set_bits(it6161, 0x5c, (1 << 6), (1 << 6)); + for (i = 0; i < 100; i++) + if (it6161_hdmi_tx_read(it6161, REG_TX_CLK_STATUS2) & B_TX_OSF_LOCK) + break; /* stable clock. */ + } else { + bTDMSetting = it6161_hdmi_tx_read(it6161, REG_TX_AUD_HDAUDIO); + if (bAudInterface == TDM) { + bTDMSetting |= B_TX_TDM; + bTDMSetting &= 0x9F; + bTDMSetting |= (AudioSrcNum - 1) << 5; + } else + bTDMSetting &= ~B_TX_TDM; + + /* 1 channel NLPCM, no TDM mode. */ + it6161_hdmi_tx_write(it6161, REG_TX_AUD_HDAUDIO, bTDMSetting); + } +} + +static void setHDMITX_NLPCMAudio(u8 bAudInterface) +{ + u8 AudioEnable, AudioFormat; + u8 i; + + /* NLPCM must use standard I2S mode. */ + AudioFormat = 0x01; + if (bAudInterface == SPDIF) + AudioEnable = M_TX_AUD_24BIT | B_TX_AUD_SPDIF; + else + AudioEnable = M_TX_AUD_24BIT; + + it6161_hdmi_tx_change_bank(it6161, 0); + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL0, AudioEnable); + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL1, 0x01); + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_FIFOMAP, 0xE4); + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL3, B_TX_CHSTSEL); + it6161_hdmi_tx_write(it6161, REG_TX_AUD_SRCVALID_FLAT, 0x00); + it6161_hdmi_tx_write(it6161, REG_TX_AUD_HDAUDIO, 0x00); + + if (bAudInterface == SPDIF) { + for (i = 0; i < 100; i++) + if (it6161_hdmi_tx_read(it6161, REG_TX_CLK_STATUS2) & B_TX_OSF_LOCK) + break; + } else { + i = it6161_hdmi_tx_read(it6161, REG_TX_AUD_HDAUDIO); + i &= ~B_TX_TDM; + /* 2 channel NLPCM, no TDM mode. */ + it6161_hdmi_tx_write(it6161, REG_TX_AUD_HDAUDIO, i); + } + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL0, + AudioEnable | B_TX_AUD_EN_I2S0); +} + +static void setHDMITX_HBRAudio(u8 bAudInterface) +{ + it6161_hdmi_tx_change_bank(it6161, 0); + + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL1, 0x47); + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_FIFOMAP, 0xE4); + + if (bAudInterface == SPDIF) { + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL0, + M_TX_AUD_24BIT | B_TX_AUD_SPDIF); + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL3, B_TX_CHSTSEL); + } else { + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL0, M_TX_AUD_24BIT); + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL3, 0); + } + it6161_hdmi_tx_write(it6161, REG_TX_AUD_SRCVALID_FLAT, 0x08); + it6161_hdmi_tx_write(it6161, REG_TX_AUD_HDAUDIO, B_TX_HBR); + + if (bAudInterface == SPDIF) { + u8 i; + + for (i = 0; i < 100; i++) + if (it6161_hdmi_tx_read(it6161, REG_TX_CLK_STATUS2) & B_TX_OSF_LOCK) + break; + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL0, + M_TX_AUD_24BIT | B_TX_AUD_SPDIF | B_TX_AUD_EN_SPDIF); + } else { + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL0, + M_TX_AUD_24BIT | B_TX_AUD_EN_I2S3 | + B_TX_AUD_EN_I2S2 | B_TX_AUD_EN_I2S1 | + B_TX_AUD_EN_I2S0); + } + it6161_hdmi_tx_set_bits(it6161, 0x5c, 1 << 6, 0x00); + it6161->bAudioChannelEnable = it6161_hdmi_tx_read(it6161, REG_TX_AUDIO_CTRL0); +} + +static void setHDMITX_DSDAudio(void) +{ + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL1, 0x41); + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_FIFOMAP, 0xE4); + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL0, M_TX_AUD_24BIT); + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL3, 0); + it6161_hdmi_tx_write(it6161, REG_TX_AUD_SRCVALID_FLAT, 0x00); + it6161_hdmi_tx_write(it6161, REG_TX_AUD_HDAUDIO, B_TX_DSD); + + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL0, + M_TX_AUD_24BIT | B_TX_AUD_EN_I2S3 | + B_TX_AUD_EN_I2S2 | B_TX_AUD_EN_I2S1 | + B_TX_AUD_EN_I2S0); +} + +static void HDMITX_DisableAudioOutput(struct it6161 *it6161) +{ + it6161_hdmi_tx_set_bits(it6161, REG_TX_SW_RST, + (B_HDMITX_AUD_RST | B_TX_AREF_RST), + (B_HDMITX_AUD_RST | B_TX_AREF_RST)); + it6161_hdmi_tx_set_bits(it6161, 0x0F, 0x10, 0x10); +} + +static void setHDMITX_NCTS(u8 Fs) +{ + u32 n, LastCTS = 0; + bool HBR_mode; + + if (B_TX_HBR & it6161_hdmi_tx_read(it6161, REG_TX_AUD_HDAUDIO)) + HBR_mode = true; + else + HBR_mode = false; + + switch (Fs) { + case AUDFS_32KHz: + n = 4096; + break; + case AUDFS_44p1KHz: + n = 6272; + break; + case AUDFS_48KHz: + n = 6144; + break; + case AUDFS_88p2KHz: + n = 12544; + break; + case AUDFS_96KHz: + n = 12288; + break; + case AUDFS_176p4KHz: + n = 25088; + break; + case AUDFS_192KHz: + n = 24576; + break; + case AUDFS_768KHz: + n = 24576; + break; + default: + n = 6144; + } + + it6161_hdmi_tx_change_bank(it6161, 1); + it6161_hdmi_tx_write(it6161, REGPktAudN0, (u8) ((n) & 0xFF)); + it6161_hdmi_tx_write(it6161, REGPktAudN1, (u8) ((n >> 8) & 0xFF)); + it6161_hdmi_tx_write(it6161, REGPktAudN2, (u8) ((n >> 16) & 0xF)); + + it6161_hdmi_tx_write(it6161, REGPktAudCTS0, (u8) ((LastCTS) & 0xFF)); + it6161_hdmi_tx_write(it6161, REGPktAudCTS1, (u8) ((LastCTS >> 8) & 0xFF)); + it6161_hdmi_tx_write(it6161, REGPktAudCTS2, (u8) ((LastCTS >> 16) & 0xF)); + it6161_hdmi_tx_change_bank(it6161, 0); + + it6161_hdmi_tx_write(it6161, 0xF8, 0xC3); + it6161_hdmi_tx_write(it6161, 0xF8, 0xA5); + /* D[1] = 0, HW auto count CTS */ + it6161_hdmi_tx_set_bits(it6161, REG_TX_PKT_SINGLE_CTRL, B_TX_SW_CTS, 0x00); + it6161_hdmi_tx_write(it6161, 0xF8, 0xFF); + + if (false == HBR_mode) { + /* LPCM */ + u8 uData; + + it6161_hdmi_tx_change_bank(it6161, 1); + Fs = AUDFS_768KHz; + it6161_hdmi_tx_write(it6161, REG_TX_AUDCHST_CA_FS, 0x00 | Fs); + /* OFS is the one's complement of FS */ + Fs = ~Fs; + uData = (0x0f & it6161_hdmi_tx_read(it6161, REG_TX_AUDCHST_OFS_WL)); + it6161_hdmi_tx_write(it6161, REG_TX_AUDCHST_OFS_WL, (Fs << 4) | uData); + it6161_hdmi_tx_change_bank(it6161, 0); + } +} + +static void HDMITX_EnableAudioOutput(struct it6161 *it6161, u8 AudioType, + u8 bAudInterface, u32 SampleFreq, u8 ChNum, u8 *pIEC60958ChStat) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + static u8 ucIEC60958ChStat[5]; + u8 Fs; + + it6161->bAudioChannelEnable = 0; + it6161_hdmi_tx_set_bits(it6161, REG_TX_SW_RST, + (B_HDMITX_AUD_RST | B_TX_AREF_RST), + (B_HDMITX_AUD_RST | B_TX_AREF_RST)); + it6161_hdmi_tx_write(it6161, REG_TX_CLK_CTRL0, + B_TX_AUTO_OVER_SAMPLING_CLOCK | B_TX_EXT_256FS | 0x01); + + /* power on the ACLK */ + it6161_hdmi_tx_set_bits(it6161, 0x0F, 0x10, 0x00); + + if (bAudInterface == SPDIF) { + if (AudioType == T_AUDIO_HBR) + it6161_hdmi_tx_write(it6161, REG_TX_CLK_CTRL0, 0x81); + + it6161_hdmi_tx_set_bits(it6161, REG_TX_AUDIO_CTRL0, + B_TX_AUD_SPDIF, B_TX_AUD_SPDIF); + } else + it6161_hdmi_tx_set_bits(it6161, REG_TX_AUDIO_CTRL0, B_TX_AUD_SPDIF, 0x00); + + if (AudioType != T_AUDIO_DSD) { + /* one bit audio have no channel status. */ + switch (SampleFreq) { + case 44100L: + Fs = AUDFS_44p1KHz; + break; + case 88200L: + Fs = AUDFS_88p2KHz; + break; + case 176400L: + Fs = AUDFS_176p4KHz; + break; + case 32000L: + Fs = AUDFS_32KHz; + break; + case 48000L: + Fs = AUDFS_48KHz; + break; + case 96000L: + Fs = AUDFS_96KHz; + break; + case 192000L: + Fs = AUDFS_192KHz; + break; + case 768000L: + Fs = AUDFS_768KHz; + break; + default: + SampleFreq = 48000L; + Fs = AUDFS_48KHz; + break; + } + + setHDMITX_NCTS(Fs); + if (pIEC60958ChStat == NULL) { + ucIEC60958ChStat[0] = 0; + ucIEC60958ChStat[1] = 0; + ucIEC60958ChStat[2] = (ChNum + 1) / 2; + + if (ucIEC60958ChStat[2] < 1) + ucIEC60958ChStat[2] = 1; + else if (ucIEC60958ChStat[2] > 4) + ucIEC60958ChStat[2] = 4; + + ucIEC60958ChStat[3] = Fs; + ucIEC60958ChStat[4] = (((~Fs) << 4) & 0xF0) | CHTSTS_SWCODE; + pIEC60958ChStat = ucIEC60958ChStat; + } + } + it6161_hdmi_tx_set_bits(it6161, REG_TX_SW_RST, + (B_HDMITX_AUD_RST | B_TX_AREF_RST), B_TX_AREF_RST); + + switch (AudioType) { + case T_AUDIO_HBR: + DRM_DEV_DEBUG_DRIVER(dev, "nT_AUDIO_HBR\n"); + pIEC60958ChStat[0] |= 1 << 1; + pIEC60958ChStat[2] = 0; + pIEC60958ChStat[3] &= 0xF0; + pIEC60958ChStat[3] |= AUDFS_768KHz; + pIEC60958ChStat[4] |= (((~AUDFS_768KHz) << 4) & 0xF0) | 0xB; + setHDMITX_ChStat(it6161, pIEC60958ChStat); + setHDMITX_HBRAudio(bAudInterface); + break; + case T_AUDIO_DSD: + DRM_DEV_DEBUG_DRIVER(dev, "nT_AUDIO_DSD\n"); + setHDMITX_DSDAudio(); + break; + case T_AUDIO_NLPCM: + DRM_DEV_DEBUG_DRIVER(dev, "nT_AUDIO_NLPCM\n"); + pIEC60958ChStat[0] |= 1 << 1; + setHDMITX_ChStat(it6161, pIEC60958ChStat); + setHDMITX_NLPCMAudio(bAudInterface); + break; + case T_AUDIO_LPCM: + DRM_DEV_DEBUG_DRIVER(dev, "nT_AUDIO_LPCM\n"); + pIEC60958ChStat[0] &= ~(1 << 1); + + setHDMITX_ChStat(it6161, pIEC60958ChStat); + setHDMITX_LPCMAudio((ChNum + 1) / 2, SUPPORT_AUDI_AudSWL, bAudInterface); + break; + } + it6161_hdmi_tx_set_bits(it6161, REG_TX_INT_MASK1, B_TX_AUDIO_OVFLW_MASK, 0x00); + it6161_hdmi_tx_write(it6161, REG_TX_AUDIO_CTRL0, it6161->bAudioChannelEnable); + + it6161_hdmi_tx_set_bits(it6161, REG_TX_SW_RST, (B_HDMITX_AUD_RST | B_TX_AREF_RST), 0); +} + +static void hdmi_audio_info_frame_set(struct it6161 *it6161, u8 channels) +{ + struct hdmi_audio_infoframe frame; + u8 buf[16]; + int ret; + + hdmi_audio_infoframe_init(&frame); + + frame.channels = channels; + frame.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; + + ret = hdmi_audio_infoframe_pack(&frame, buf, sizeof(buf)); + if (ret < 0) { + DRM_ERROR("failed to pack audio infoframe: %d\n", ret); + return; + } + + /* set audio Data byte */ + it6161_hdmi_tx_change_bank(it6161, 1); + it6161_hdmi_tx_write(it6161, REG_TX_PKT_AUDINFO_SUM, buf[3]); + it6161_hdmi_tx_write(it6161, REG_TX_PKT_AUDINFO_CC, buf[4]); + it6161_hdmi_tx_write(it6161, REG_TX_PKT_AUDINFO_SF, buf[5]); + it6161_hdmi_tx_write(it6161, REG_TX_PKT_AUDINFO_CA, buf[7]); + it6161_hdmi_tx_write(it6161, REG_TX_PKT_AUDINFO_DM_LSV, buf[8]); + + /* Enable Audio info frame */ + it6161_hdmi_tx_change_bank(it6161, 0); + it6161_hdmi_tx_write(it6161, REG_TX_AUD_INFOFRM_CTRL, B_TX_ENABLE_PKT | B_TX_REPEAT_PKT); +} + +static void hdmi_tx_audio_process(struct it6161 *it6161) +{ + if (it6161->support_audio) { + hdmi_audio_info_frame_set(it6161, (u8) OUTPUT_CHANNEL); + + HDMITX_EnableAudioOutput(it6161, + CNOFIG_INPUT_AUDIO_TYPE, + CONFIG_INPUT_AUDIO_INTERFACE, + INPUT_SAMPLE_FREQ, + OUTPUT_CHANNEL, + NULL); + } +} + +static inline void hdmi_tx_disable_avi_infoframe(struct it6161 *it6161) +{ + it6161_hdmi_tx_change_bank(it6161, 0); + it6161_hdmi_tx_write(it6161, REG_TX_AVI_INFOFRM_CTRL, 0x00); +} + +static inline void hdmi_tx_enable_avi_infoframe(struct it6161 *it6161) +{ + it6161_hdmi_tx_change_bank(it6161, 0); + it6161_hdmi_tx_write(it6161, REG_TX_AVI_INFOFRM_CTRL, + B_TX_ENABLE_PKT | B_TX_REPEAT_PKT); +} + +static int hdmi_tx_avi_infoframe_set(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + struct hdmi_avi_infoframe *frame = &it6161->source_avi_infoframe; + struct drm_display_mode *display_mode = &it6161->hdmi_tx_display_mode; + u8 buf[32], i, *ptr; + int ret; + + DRM_DEV_DEBUG_DRIVER(dev, "%s\n", __func__); + hdmi_tx_disable_avi_infoframe(it6161); + + ret = drm_hdmi_avi_infoframe_from_display_mode( + frame, &it6161->connector, display_mode); + if (ret) { + DRM_DEV_ERROR(dev, "Failed to setup AVI infoframe: %d", ret); + return ret; + } + + if ((it6161->hdmi_tx_output_color_space & F_MODE_CLRMOD_MASK) == F_MODE_RGB444) + frame->colorspace = HDMI_COLORSPACE_RGB; + + if ((it6161->hdmi_tx_output_color_space & F_MODE_CLRMOD_MASK) == F_MODE_YUV444) + frame->colorspace = HDMI_COLORSPACE_YUV444; + + if ((it6161->hdmi_tx_output_color_space & F_MODE_CLRMOD_MASK) == F_MODE_YUV422) + frame->colorspace = HDMI_COLORSPACE_YUV422; + + ret = hdmi_avi_infoframe_pack(&it6161->source_avi_infoframe, buf, sizeof(buf)); + if (ret < 0) { + DRM_DEV_ERROR(dev, "Failed to pack AVI infoframe: %d", ret); + return ret; + } + + /* fill PB */ + it6161_hdmi_tx_change_bank(it6161, 1); + ptr = buf + HDMI_INFOFRAME_HEADER_SIZE; + for (i = 0; i < it6161->source_avi_infoframe.length; i++) + it6161_hdmi_tx_write(it6161, REG_TX_AVIINFO_DB1 + i, ptr[i]); + + it6161_hdmi_tx_write(it6161, REG_TX_AVIINFO_SUM, buf[3]); + + /* Enable */ + hdmi_tx_enable_avi_infoframe(it6161); + return 0; +} + +static void hdmi_tx_set_output_process(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + u8 level; + u32 TMDSClock; + + DRM_DEV_DEBUG_DRIVER(dev, "n%s\n", __func__); + + TMDSClock = it6161->hdmi_tx_pclk * 1000 * + (it6161->source_avi_infoframe.pixel_repeat + 1); + + HDMITX_DisableAudioOutput(it6161); + hdmi_tx_disable_avi_infoframe(it6161); + + if (TMDSClock > 80000000L) + level = PCLK_HIGH; + else if (TMDSClock > 20000000L) + level = PCLK_MEDIUM; + else + level = PCLK_LOW; + + hdmi_tx_enable_video_output(it6161, level); + + if (it6161->hdmi_mode) { + hdmi_tx_avi_infoframe_set(it6161); + hdmi_tx_audio_process(it6161); + } + + it6161_hdmi_tx_set_av_mute(it6161, false); +} + +static void mipi_rx_calc_rclk(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + u32 sum = 0, i, retry = 5; + int t10usint; + + for (i = 0; i < retry; i++) { + /* Enable RCLK 100ms count */ + it6161_mipi_rx_set_bits(it6161, 0x94, 0x80, 0x80); + msleep(100); + /* Disable RCLK 100ms count */ + it6161_mipi_rx_set_bits(it6161, 0x94, 0x80, 0x00); + + it6161->mipi_rx_rclk = it6161_mipi_rx_read(it6161, 0x97); + it6161->mipi_rx_rclk <<= 8; + it6161->mipi_rx_rclk += it6161_mipi_rx_read(it6161, 0x96); + it6161->mipi_rx_rclk <<= 8; + it6161->mipi_rx_rclk += it6161_mipi_rx_read(it6161, 0x95); + sum += it6161->mipi_rx_rclk; + } + + sum /= retry; + it6161->mipi_rx_rclk = sum / 108; + t10usint = it6161->mipi_rx_rclk; + DRM_DEV_DEBUG_DRIVER(dev, "mipi_rx_rclk = %d,%03d,%03d\n", + (sum * 10) / 1000000, ((sum * 10) % 1000000) / 1000, ((sum * 10) % 100)); + it6161_mipi_rx_write(it6161, 0x91, t10usint & 0xFF); +} + +static void mipi_rx_calc_mclk(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + u32 i, rddata, sum = 0, calc_time = 3; + + for (i = 0; i < calc_time; i++) { + it6161_mipi_rx_set_bits(it6161, 0x9B, 0x80, 0x80); + msleep(5); + it6161_mipi_rx_set_bits(it6161, 0x9B, 0x80, 0x00); + + rddata = it6161_mipi_rx_read(it6161, 0x9B) & 0x0F; + rddata <<= 8; + rddata += it6161_mipi_rx_read(it6161, 0x9A); + sum += rddata; + } + + sum /= calc_time; + it6161->mipi_rx_mclk = it6161->mipi_rx_rclk * 2048 / sum; + DRM_DEV_DEBUG_DRIVER(dev, "MCLK = %d.%03dMHz", + it6161->mipi_rx_mclk / 1000, it6161->mipi_rx_mclk % 1000); +} + +static void mipi_rx_calc_pclk(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + u32 rddata, sum = 0, retry = 3; + u8 i; + + it6161_mipi_rx_set_bits(it6161, 0x99, 0x80, 0x00); + + for (i = 0; i < retry; i++) { + it6161_mipi_rx_set_bits(it6161, 0x99, 0x80, 0x80); + msleep(5); + it6161_mipi_rx_set_bits(it6161, 0x99, 0x80, 0x00); + msleep(1); + + rddata = it6161_mipi_rx_read(it6161, 0x99) & 0x0F; + rddata <<= 8; + rddata += it6161_mipi_rx_read(it6161, 0x98); + sum += rddata; + } + + sum /= retry; + it6161->mipi_rx_pclk = it6161->mipi_rx_rclk * 2048 / sum; + DRM_DEV_DEBUG_DRIVER(dev, "nit6161->mipi_rx_pclk = %d.%03dMHz", + it6161->mipi_rx_pclk / 1000, it6161->mipi_rx_pclk % 1000); +} + +static void mipi_rx_show_mrec(struct it6161 *it6161) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + int m_hfront_porch, m_hsyncw, m_hback_porch, m_hactive, MHVR2nd, MHBlank; + int m_vfront_porch, m_vsyncw, m_vback_porch, m_vactive, MVFP2nd, MVTotal; + + m_hfront_porch = mipi_rx_read_word(it6161, 0x50) & 0x3FFF; + m_hsyncw = mipi_rx_read_word(it6161, 0x52) & 0x3FFF; + m_hback_porch = mipi_rx_read_word(it6161, 0x54) & 0x3FFF; + m_hactive = mipi_rx_read_word(it6161, 0x56) & 0x3FFF; + MHVR2nd = mipi_rx_read_word(it6161, 0x58) & 0x3FFF; + + MHBlank = m_hfront_porch + m_hsyncw + m_hback_porch; + + m_vfront_porch = mipi_rx_read_word(it6161, 0x5A) & 0x3FFF; + m_vsyncw = mipi_rx_read_word(it6161, 0x5C) & 0x3FFF; + m_vback_porch = mipi_rx_read_word(it6161, 0x5E) & 0x3FFF; + m_vactive = mipi_rx_read_word(it6161, 0x60) & 0x3FFF; + MVFP2nd = mipi_rx_read_word(it6161, 0x62) & 0x3FFF; + + MVTotal = m_vfront_porch + m_vsyncw + m_vback_porch + m_vactive; + + DRM_DEV_DEBUG_DRIVER(dev, "nm_hfront_porch = %d\n", m_hfront_porch); + DRM_DEV_DEBUG_DRIVER(dev, "nm_hsyncw = %d\n", m_hsyncw); + DRM_DEV_DEBUG_DRIVER(dev, "nm_hback_porch = %d\n", m_hback_porch); + DRM_DEV_DEBUG_DRIVER(dev, "nm_hactive = %d\n", m_hactive); + DRM_DEV_DEBUG_DRIVER(dev, "nMHVR2nd = %d\n", MHVR2nd); + DRM_DEV_DEBUG_DRIVER(dev, "nMHBlank = %d\n", MHBlank); + + DRM_DEV_DEBUG_DRIVER(dev, "nm_vfront_porch = %d\n", m_vfront_porch); + DRM_DEV_DEBUG_DRIVER(dev, "nm_vsyncw = %d\n", m_vsyncw); + DRM_DEV_DEBUG_DRIVER(dev, "nm_vback_porch = %d\n", m_vback_porch); + DRM_DEV_DEBUG_DRIVER(dev, "nm_vactive = %d\n", m_vactive); + DRM_DEV_DEBUG_DRIVER(dev, "nMVFP2nd = %d\n", MVFP2nd); + DRM_DEV_DEBUG_DRIVER(dev, "nMVTotal = %d\n", MVTotal); +} + +static void mipi_rx_prec_get_display_mode(struct it6161 *it6161) +{ + struct drm_display_mode *display_mode = &it6161->mipi_rx_p_display_mode; + + int p_hfront_porch, p_hsyncw, p_hback_porch, p_hactive, p_htotal; + int p_vfront_porch, p_vsyncw, p_vback_porch, p_vactive, p_vtotal; + + p_hfront_porch = mipi_rx_read_word(it6161, 0x30) & 0x3FFF; + p_hsyncw = mipi_rx_read_word(it6161, 0x32) & 0x3FFF; + p_hback_porch = mipi_rx_read_word(it6161, 0x34) & 0x3FFF; + p_hactive = mipi_rx_read_word(it6161, 0x36) & 0x3FFF; + p_htotal = mipi_rx_read_word(it6161, 0x38) & 0x3FFF; + + p_vfront_porch = mipi_rx_read_word(it6161, 0x3A) & 0x3FFF; + p_vsyncw = mipi_rx_read_word(it6161, 0x3C) & 0x3FFF; + p_vback_porch = mipi_rx_read_word(it6161, 0x3E) & 0x3FFF; + p_vactive = mipi_rx_read_word(it6161, 0x40) & 0x3FFF; + p_vtotal = mipi_rx_read_word(it6161, 0x42) & 0x3FFF; + + display_mode->clock = it6161->mipi_rx_pclk; + display_mode->hdisplay = p_hactive; + display_mode->hsync_start = p_hactive + p_hfront_porch; + display_mode->hsync_end = p_hactive + p_hfront_porch + p_hsyncw; + display_mode->htotal = p_htotal; + display_mode->vdisplay = p_vactive; + display_mode->vsync_start = p_vactive + p_vfront_porch; + display_mode->vsync_end = p_vactive + p_vfront_porch + p_vsyncw; + display_mode->vtotal = p_vtotal; +} + +static void mipi_rx_reset_p_domain(struct it6161 *it6161) +{ + /* Video Clock Domain Reset */ + it6161_mipi_rx_set_bits(it6161, 0x05, 0x04, 0x04); + /* Release Video Clock Domain Reset */ + it6161_mipi_rx_set_bits(it6161, 0x05, 0x04, 0x00); +} + +static void it6161_mipi_rx_interrupt_clear(struct it6161 *it6161, u8 reg06, u8 reg07, u8 reg08) +{ + it6161_mipi_rx_write(it6161, 0x06, reg06); + it6161_mipi_rx_write(it6161, 0x07, reg07); + it6161_mipi_rx_write(it6161, 0x08, reg08); +} + +static void it6161_mipi_rx_interrupt_reg06_process(struct it6161 *it6161, u8 reg06) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + bool m_video_stable, p_video_stable; + u8 data_id; + + if (reg06 & 0x01) { + m_video_stable = mipi_rx_get_m_video_stable(it6161); + DRM_DEV_DEBUG_DRIVER(dev, "nPPS M video stable Change Interrupt, %sstable", + m_video_stable ? "" : "un"); + + if (m_video_stable) { + data_id = it6161_mipi_rx_read(it6161, 0x28); + DRM_DEV_DEBUG_DRIVER(dev, "nmipi receive video format: 0x%02x", data_id); + mipi_rx_calc_rclk(it6161); + mipi_rx_calc_mclk(it6161); + mipi_rx_show_mrec(it6161); + mipi_rx_afe_configuration(it6161, data_id); + mipi_rx_reset_p_domain(it6161); + } + } else if (reg06 & 0x10) { + + p_video_stable = mipi_rx_get_p_video_stable(it6161); + DRM_DEV_DEBUG_DRIVER(dev, "nPPS P video stable Change Interrupt, %sstable", p_video_stable ? "" : "un"); + if (p_video_stable) { + DRM_DEV_DEBUG_DRIVER(dev, "nPVidStb Change to HIGH"); + mipi_rx_calc_rclk(it6161); + mipi_rx_calc_pclk(it6161); + mipi_rx_prec_get_display_mode(it6161); + it6161->vic = drm_match_cea_mode(&it6161->mipi_rx_p_display_mode); + + DRM_DEV_DEBUG_DRIVER(dev, "nsource output vic: %d, %s cea timing", + it6161->vic, it6161->vic ? " standard" : " not"); + DRM_DEV_DEBUG_DRIVER(dev, "nsource mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(&it6161->source_display_mode)); + DRM_DEV_DEBUG_DRIVER(dev, "nmipi rx pmode " DRM_MODE_FMT "\n", DRM_MODE_ARG(&it6161->mipi_rx_p_display_mode)); + + mipi_rx_setup_polarity(it6161); + it6161_mipi_rx_write(it6161, 0xC0, (EnTxCRC << 7) + TxCRCnum); + /* setup 1 sec timer interrupt */ + it6161_mipi_rx_set_bits(it6161, 0x0b, 0x40, 0x40); + + switch (it6161->hdmi_tx_mode) { + case HDMI_TX_BY_PASS: + it6161_hdmi_tx_set_bits(it6161, 0xA9, 0x80, 0x80); + break; + + case HDMI_TX_ENABLE_DE_ONLY: + hdmi_tx_generate_blank_timing(it6161); + break; + + default: + DRM_DEV_ERROR(dev, "nuse hdmi tx normal mode"); + break; + } + + hdmi_tx_video_reset(it6161); + } + } else if (reg06 & 0x02) + DRM_DEV_DEBUG_DRIVER(dev, "nPPS MHSync error interrupt"); + else if (reg06 & 0x04) + DRM_DEV_ERROR(dev, "nPPS MHDE Error Interrupt"); + else if (reg06 & 0x08) + DRM_DEV_ERROR(dev, "nPPS MVSync Error Interrupt"); + else if (reg06 & 0x20) + DRM_DEV_ERROR(dev, "nPPS PHSync Error Interrupt"); + else if (reg06 & 0x40) + DRM_DEV_ERROR(dev, "nPPS PHDE Error Interrupt"); + else if (reg06 & 0x80) + DRM_DEV_ERROR(dev, "nPPS MVDE Error Interrupt"); +} + +static void it6161_mipi_rx_interrupt_reg07_process(struct it6161 *it6161, u8 reg07) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + + if (reg07 & 0x01) + DRM_DEV_ERROR(dev, "nPatGen PPGVidStb change interrupt !!!\n"); + else if (reg07 & 0x02) + DRM_DEV_ERROR(dev, "nPPS Data Byte Error Interrupt !!!\n"); + else if (reg07 & 0x04) + DRM_DEV_ERROR(dev, "nPPS CMOff Interrupt !!!\n"); + else if (reg07 & 0x08) + DRM_DEV_ERROR(dev, "nPPS CMOn Interrupt !!!\n"); + else if (reg07 & 0x10) + DRM_DEV_ERROR(dev, "nPPS ShutDone cmd Interrupt !!!\n"); + else if (reg07 & 0x20) + DRM_DEV_ERROR(dev, "nPPS TurnOn Interrupt !!!\n"); + else if (reg07 & 0x40) { + DRM_DEV_DEBUG_DRIVER(dev, "nPPS FIFO over read Interrupt !!! tx video statle:%d", + hdmi_tx_get_video_state(it6161)); + it6161_mipi_rx_set_bits(it6161, 0x07, 0x40, 0x40); + } else if (reg07 & 0x80) { + DRM_DEV_DEBUG_DRIVER(dev, "nPPS FIFO over write Interrupt !!!\n"); + it6161_mipi_rx_set_bits(it6161, 0x07, 0x80, 0x80); + } +} + +static void it6161_mipi_rx_interrupt_reg08_process(struct it6161 *it6161, u8 reg08) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + int crc; + + if (reg08 & 0x01) + DRM_DEV_ERROR(dev, "nECC 1-bit Error Interrupt !!!\n"); + else if (reg08 & 0x02) + DRM_DEV_ERROR(dev, "nECC 2-bit Error Interrupt !!!\n"); + else if (reg08 & 0x04) + DRM_DEV_ERROR(dev, "nLM FIFO Error Interrupt !!!\n"); + else if (reg08 & 0x08) + DRM_DEV_ERROR(dev, "nCRC Error Interrupt !!!\n"); + else if (reg08 & 0x10) + DRM_DEV_ERROR(dev, "nMCLK Off Interrupt !!!\n"); + else if (reg08 & 0x20) + DRM_DEV_ERROR(dev, "nPPI FIFO OverWrite Interrupt !!!\n"); + else if (reg08 & 0x40) { + it6161_mipi_rx_set_bits(it6161, 0x0b, 0x40, 0x00); + + if ((it6161_mipi_rx_read(it6161, 0xC1) & 0x03) == 0x03) + DRM_DEV_ERROR(dev, "nCRC Fail !!!\n"); + + if ((it6161_mipi_rx_read(it6161, 0xC1) & 0x05) == 0x05) { + DRM_DEV_ERROR(dev, "nCRC Pass !!!\n"); + crc = it6161_mipi_rx_read(it6161, 0xC2) + + (it6161_mipi_rx_read(it6161, 0xC3) << 8); + DRM_DEV_ERROR(dev, "nCRCR = 0x%x !!!\n", crc); + crc = it6161_mipi_rx_read(it6161, 0xC4) + + (it6161_mipi_rx_read(it6161, 0xC5) << 8); + DRM_DEV_ERROR(dev, "nCRCG = 0x%x !!!\n", crc); + crc = it6161_mipi_rx_read(it6161, 0xC6) + + (it6161_mipi_rx_read(it6161, 0xC7) << 8); + DRM_DEV_ERROR(dev, "nCRCB = 0x%x !!!\n", crc); + } + } +} + +static void it6161_hdmi_tx_interrupt_clear(struct it6161 *it6161, u8 reg06, u8 reg07, + u8 reg08, u8 regee) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + u8 int_clear; + + if (reg06 & B_TX_INT_AUD_OVERFLOW) { + DRM_DEV_ERROR(dev, "nB_TX_INT_AUD_OVERFLOW"); + it6161_hdmi_tx_set_bits(it6161, REG_TX_SW_RST, + (B_HDMITX_AUD_RST | B_TX_AREF_RST), + (B_HDMITX_AUD_RST | B_TX_AREF_RST)); + it6161_hdmi_tx_set_bits(it6161, REG_TX_SW_RST, B_HDMITX_AUD_RST | B_TX_AREF_RST, 0x00); + } else if (reg06 & B_TX_INT_DDCFIFO_ERR) { + DRM_DEV_ERROR(dev, "nDDC FIFO Error"); + it6161_hdmi_tx_clear_ddc_fifo(it6161); + } else if (reg06 & B_TX_INT_DDC_BUS_HANG) { + DRM_DEV_ERROR(dev, "nDDC BUS HANG"); + it6161_hdmi_tx_abort_ddc(it6161); + } + + /* clear ext interrupt */ + it6161_hdmi_tx_write(it6161, 0xEE, regee); + it6161_hdmi_tx_write(it6161, REG_TX_INT_CLR0, 0xFF); + it6161_hdmi_tx_write(it6161, REG_TX_INT_CLR1, 0xFF); + /* write B_TX_INTACTDONE '1' to trigger clear interrupt */ + int_clear = (it6161_hdmi_tx_read(it6161, REG_TX_SYS_STATUS)) | B_TX_CLR_AUD_CTS | B_TX_INTACTDONE; + it6161_hdmi_tx_write(it6161, REG_TX_SYS_STATUS, int_clear); +} + +static void it6161_hdmi_tx_interrupt_reg06_process(struct it6161 *it6161, u8 reg06) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + u8 ret; + + if (reg06 & B_TX_INT_HPD_PLUG) { + drm_helper_hpd_irq_event(it6161->bridge.dev); + if (hdmi_tx_get_sink_hpd(it6161)) { + DRM_INFO("hpd on"); + ret = wait_for_completion_timeout(&it6161->wait_edid_complete, + msecs_to_jiffies(2000)); + if (ret == 0) + DRM_DEV_ERROR(dev, "nwait edid timeout"); + + it6161->hdmi_tx_output_color_space = OUTPUT_COLOR_MODE; + it6161->hdmi_tx_input_color_space = INPUT_COLOR_MODE; + hdmi_tx_set_capability_from_edid_parse(it6161); + hdmi_tx_video_reset(it6161); + + /* 1. not only HDMI but DVI need the set the upstream HPD + * 2. Before set upstream HPD , the EDID must be ready. */ + } else { + DRM_DEV_INFO(dev, "hpd off"); + hdmi_tx_disable_video_output(it6161); + kfree(it6161->edid); + it6161->edid = NULL; + } + } +} + +static void it6161_hdmi_tx_interrupt_reg08_process(struct it6161 *it6161, u8 reg08) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + + if (reg08 & B_TX_INT_VIDSTABLE) { + it6161_hdmi_tx_write(it6161, REG_TX_INT_STAT3, reg08); + if (hdmi_tx_get_video_state(it6161)) { + hdmi_tx_get_display_mode(it6161); + + DRM_DEV_DEBUG_DRIVER(dev, "nsource mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(&it6161->source_display_mode)); + DRM_DEV_DEBUG_DRIVER(dev, "nhdmi tx mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(&it6161->hdmi_tx_display_mode)); + + hdmi_tx_set_output_process(it6161); + it6161_hdmi_tx_set_av_mute(it6161, FALSE); + } + } +} + +static void it6161_hdmi_tx_interrupt_regee_process(struct it6161 *it6161, u8 regee) +{ + struct device *dev = &it6161->i2c_hdmi_tx->dev; + + if (regee != 0x00) { + DRM_DEV_ERROR(dev, "n%s%s%s%s%s%s%s", + (regee & 0x40) ? "video parameter change " : "", + (regee & 0x20) ? "HDCP Pj check done " : "", + (regee & 0x10) ? "HDCP Ri check done " : "", + (regee & 0x8) ? "DDC bus hang " : "", + (regee & 0x4) ? "Video input FIFO auto reset " : "", + (regee & 0x2) ? "No audio input interrupt " : "", + (regee & 0x1) ? "Audio decode error interrupt " : ""); + } +} + +static irqreturn_t it6161_intp_threaded_handler(int unused, void *data) +{ + struct it6161 *it6161 = data; + struct device *dev = &it6161->i2c_hdmi_tx->dev; + u8 mipi_rx_reg06, mipi_rx_reg07, mipi_rx_reg08, mipi_rx_reg0d; + u8 hdmi_tx_reg06, hdmi_tx_reg07, hdmi_tx_reg08, hdmi_tx_regee; + u8 hdmi_tx_reg0e; + + if (it6161->enable_drv_hold) + goto unlock; + + mipi_rx_reg06 = it6161_mipi_rx_read(it6161, 0x06); + mipi_rx_reg07 = it6161_mipi_rx_read(it6161, 0x07); + mipi_rx_reg08 = it6161_mipi_rx_read(it6161, 0x08); + mipi_rx_reg0d = it6161_mipi_rx_read(it6161, 0x0D); + + hdmi_tx_reg06 = it6161_hdmi_tx_read(it6161, 0x06); + hdmi_tx_reg07 = it6161_hdmi_tx_read(it6161, 0x07); + hdmi_tx_reg08 = it6161_hdmi_tx_read(it6161, 0x08); + hdmi_tx_reg0e = it6161_hdmi_tx_read(it6161, 0x0E); + hdmi_tx_regee = it6161_hdmi_tx_read(it6161, 0xEE); + + if ((mipi_rx_reg06 != 0) || (mipi_rx_reg07 != 0) || (mipi_rx_reg08 != 0)) { + DRM_DEV_DEBUG_DRIVER(dev, "n[MIPI rx ++] reg06: 0x%02x reg07: 0x%02x reg08: 0x%02x reg0d: 0x%02x", + mipi_rx_reg06, mipi_rx_reg07, mipi_rx_reg08, mipi_rx_reg0d); + it6161_mipi_rx_interrupt_clear(it6161, mipi_rx_reg06, mipi_rx_reg07, mipi_rx_reg08); + } + + if ((hdmi_tx_reg06 != 0) || (hdmi_tx_reg07 != 0) || (hdmi_tx_reg08 != 0)) { + DRM_DEV_DEBUG_DRIVER(dev, "n[HDMI tx ++] reg06: 0x%02x reg07: 0x%02x reg08: 0x%02x reg0e: 0x%02x regee: 0x%02x", + hdmi_tx_reg06, hdmi_tx_reg07, hdmi_tx_reg08, hdmi_tx_reg0e, hdmi_tx_regee); + it6161_hdmi_tx_interrupt_clear(it6161, hdmi_tx_reg06, hdmi_tx_reg07, hdmi_tx_reg08, hdmi_tx_regee); + } + + it6161_mipi_rx_interrupt_reg08_process(it6161, mipi_rx_reg08); + it6161_mipi_rx_interrupt_reg06_process(it6161, mipi_rx_reg06); + it6161_mipi_rx_interrupt_reg07_process(it6161, mipi_rx_reg07); + it6161_hdmi_tx_interrupt_reg06_process(it6161, hdmi_tx_reg06); + it6161_hdmi_tx_interrupt_reg08_process(it6161, hdmi_tx_reg08); + it6161_hdmi_tx_interrupt_regee_process(it6161, hdmi_tx_regee); + +unlock: + return IRQ_HANDLED; +} + +static ssize_t enable_drv_hold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct it6161 *it6161 = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "drv_hold: %d\n", it6161->enable_drv_hold); +} + +static ssize_t enable_drv_hold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct it6161 *it6161 = dev_get_drvdata(dev); + u32 drv_hold; + + if (kstrtoint(buf, 10, &drv_hold) < 0) + return -EINVAL; + + it6161->enable_drv_hold = drv_hold; + + if (it6161->enable_drv_hold) { + it6161_mipi_rx_int_mask_disable(it6161); + it6161_hdmi_tx_int_mask_disable(it6161); + } else { + it6161_mipi_rx_interrupt_clear(it6161, 0xFF, 0xFF, 0xFF); + it6161_hdmi_tx_interrupt_clear(it6161, 0xFF, 0xFF, 0xFF, 0xFF); + it6161_mipi_rx_int_mask_enable(it6161); + it6161_hdmi_tx_int_mask_enable(it6161); + } + return count; +} + +static ssize_t hdmi_output_color_space_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct it6161 *it6161 = dev_get_drvdata(dev); + + DRM_DEV_DEBUG_DRIVER(dev, "nconfig color space: %s", buf); + it6161->hdmi_tx_output_color_space &= ~F_MODE_CLRMOD_MASK; + + if (strncmp(buf, "ycbcr444", strlen(buf) - 1) == 0 + || strncmp(buf, "yuv444", strlen(buf) - 1) == 0) { + it6161->hdmi_tx_output_color_space |= F_MODE_YUV444; + goto end; + } + + if (strncmp(buf, "ycbcr422", strlen(buf) - 1) == 0 + || strncmp(buf, "yuv422", strlen(buf) - 1) == 0) { + it6161->hdmi_tx_output_color_space |= F_MODE_YUV422; + goto end; + } + + if (strncmp(buf, "rgb444", strlen(buf) - 1) == 0) { + it6161->hdmi_tx_output_color_space |= F_MODE_RGB444; + goto end; + } + + DRM_DEV_DEBUG_DRIVER(dev, + "nnot support this color space, only support ycbcr444/yuv444, ycbcr422/yuv422, rgb444"); + return count; + +end: + DRM_DEV_INFO(dev, "nconfig color space: %s value:0x%02x", buf, + it6161->hdmi_tx_output_color_space); + return count; +} + +static ssize_t hdmi_output_color_space_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct it6161 *it6161 = dev_get_drvdata(dev); + char *str = buf, *end = buf + PAGE_SIZE; + + str += scnprintf(str, end - str, + "it6161->hdmi_tx_output_color_space:%d\n", it6161->hdmi_tx_output_color_space); + + return str - buf; +} + +static DEVICE_ATTR_RW(enable_drv_hold); +static DEVICE_ATTR_RW(hdmi_output_color_space); + +static const struct attribute *it6161_attrs[] = { + &dev_attr_enable_drv_hold.attr, + &dev_attr_hdmi_output_color_space.attr, + NULL, +}; + +static int it6161_parse_dt(struct it6161 *it6161, struct device_node *np) +{ + struct device *dev = &it6161->i2c_mipi_rx->dev; + + it6161->host_node = of_graph_get_remote_node(np, 0, 0); + if (!it6161->host_node) { + DRM_DEV_ERROR(dev, "nno host node"); + return -ENODEV; + } + of_node_put(it6161->host_node); + + return 0; +} + +static int it6161_i2c_probe(struct i2c_client *i2c_mipi_rx, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c_mipi_rx->dev; + int err, intp_irq; + + DRM_DEV_DEBUG_DRIVER(dev, "n%s++\n", __func__); + it6161 = devm_kzalloc(dev, sizeof(*it6161), GFP_KERNEL); + if (!it6161) + return -ENOMEM; + + it6161->i2c_mipi_rx = i2c_mipi_rx; + mutex_init(&it6161->mode_lock); + init_completion(&it6161->wait_edid_complete); + + it6161->bridge.of_node = i2c_mipi_rx->dev.of_node; + + it6161_parse_dt(it6161, dev->of_node); + it6161->regmap_mipi_rx = devm_regmap_init_i2c(i2c_mipi_rx, &it6161_mipi_rx_bridge_regmap_config); + if (IS_ERR(it6161->regmap_mipi_rx)) { + DRM_DEV_ERROR(dev, "regmap_mipi_rx i2c init failed"); + return PTR_ERR(it6161->regmap_mipi_rx); + } + + if (device_property_read_u32(dev, "it6161-addr-hdmi-tx", &it6161->it6161_addr_hdmi_tx) < 0) + it6161->it6161_addr_hdmi_tx = 0x4C; + it6161->i2c_hdmi_tx = i2c_new_dummy_device(i2c_mipi_rx->adapter, it6161->it6161_addr_hdmi_tx); + it6161->regmap_hdmi_tx = devm_regmap_init_i2c(it6161->i2c_hdmi_tx, &it6161_hdmi_tx_bridge_regmap_config); + if (IS_ERR(it6161->regmap_hdmi_tx)) { + DRM_DEV_ERROR(dev, "regmap_hdmi_tx i2c init failed"); + return PTR_ERR(it6161->regmap_mipi_rx); + } + + if (device_property_read_u32(dev, "it6161-addr-cec", &it6161->it6161_addr_cec) < 0) + it6161->it6161_addr_cec = 0x4E; + it6161->i2c_cec = i2c_new_dummy_device(i2c_mipi_rx->adapter, it6161->it6161_addr_cec); + it6161->regmap_cec = devm_regmap_init_i2c(it6161->i2c_cec, &it6161_cec_bridge_regmap_config); + if (IS_ERR(it6161->regmap_cec)) { + DRM_DEV_ERROR(dev, "regmap_cec i2c init failed"); + return PTR_ERR(it6161->regmap_cec); + } + + if (!it6161_check_device_ready(it6161)) + return -ENODEV; + + it6161->enable_drv_hold = DEFAULT_DRV_HOLD; + it6161_set_interrupts_active_level(HIGH); + + intp_irq = i2c_mipi_rx->irq; + + if (!intp_irq) { + DRM_DEV_ERROR(dev, "it6112 failed to get INTP IRQ"); + return -ENODEV; + } + + err = devm_request_threaded_irq(&i2c_mipi_rx->dev, intp_irq, NULL, + it6161_intp_threaded_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "it6161-intp", it6161); + if (err) { + DRM_DEV_ERROR(dev, "it6112 failed to request INTP threaded IRQ: %d", err); + return err; + } + + i2c_set_clientdata(i2c_mipi_rx, it6161); + it6161->bridge.funcs = &it6161_bridge_funcs; + it6161->bridge.of_node = i2c_mipi_rx->dev.of_node; + it6161->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | + DRM_BRIDGE_OP_HPD | DRM_BRIDGE_OP_MODES; + it6161->bridge.type = DRM_MODE_CONNECTOR_HDMIA; + + drm_bridge_add(&it6161->bridge); + + err = sysfs_create_files(&i2c_mipi_rx->dev.kobj, it6161_attrs); + if (err) + return err; + + DRM_DEV_DEBUG_DRIVER(dev, "n%s--\n", __func__); + return 0; +} + +static int it6161_remove(struct i2c_client *i2c_mipi_rx) +{ + struct it6161 *it6161 = i2c_get_clientdata(i2c_mipi_rx); + + drm_connector_unregister(&it6161->connector); + drm_connector_cleanup(&it6161->connector); + drm_bridge_remove(&it6161->bridge); + return 0; +} + +static const struct i2c_device_id it6161_id[] = { + {"it6161", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, it6161_id); + +static const struct of_device_id it6161_of_match[] = { + {.compatible = "ite,it6161"}, + {} +}; + +static struct i2c_driver it6161_i2c_driver = { + .driver = { + .name = "it6161_mipirx_hdmitx", + .of_match_table = it6161_of_match, + }, + .id_table = it6161_id, + .probe = it6161_i2c_probe, + .remove = it6161_remove, +}; + +module_i2c_driver(it6161_i2c_driver); + +MODULE_AUTHOR("allen chen <allen.chen@ite.com.tw>"); +MODULE_DESCRIPTION("it6161 HDMI Transmitter driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/bridge/it6161.h b/drivers/gpu/drm/bridge/it6161.h new file mode 100644 index 0000000000000000000000000000000000000000..93900406bcb3245f865cac44e7f6f664fe2f3735 --- /dev/null +++ b/drivers/gpu/drm/bridge/it6161.h @@ -0,0 +1,322 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * IT6161 MIPI to HDMI Converter driver + * + * Copyright (C) 2021 NXP + */ +#ifndef __IT6161_H__ +#define __IT6161_H__ + +/* Video Configuration */ +#define F_MODE_RGB444 0 +#define F_MODE_YUV422 1 +#define F_MODE_YUV444 2 +#define F_MODE_CLRMOD_MASK 3 + +#define F_VIDMODE_ITU709 (1<<4) +#define F_VIDMODE_ITU601 0 +#define F_VIDMODE_16_235 (1<<5) +#define F_VIDMODE_0_255 0 + +#define F_VIDMODE_EN_UDFILT (1<<6) +#define F_VIDMODE_EN_DITHER (1<<7) + +#define INPUT_COLOR_MODE F_MODE_RGB444 +#define OUTPUT_COLOR_MODE F_MODE_RGB444 + +/* Audio Configuration */ + +/* Audio interface */ +#define I2S 0 +#define SPDIF 1 +#define TDM 2 + +/* Audio sample clock */ +#define AUDFS_44p1KHz 0 +#define AUDFS_88p2KHz 8 +#define AUDFS_176p4KHz 12 +#define AUDFS_32KHz 3 +#define AUDFS_48KHz 2 +#define AUDFS_96KHz 10 +#define AUDFS_192KHz 14 +#define AUDFS_768KHz 9 + +#define F_AUDIO_ON (1<<7) +#define F_AUDIO_HBR (1<<6) +#define F_AUDIO_DSD (1<<5) +#define F_AUDIO_NLPCM (1<<4) +#define F_AUDIO_LAYOUT_1 (1<<3) +#define F_AUDIO_LAYOUT_0 (0<<3) + +#define T_AUDIO_HBR (F_AUDIO_ON | F_AUDIO_HBR) +#define T_AUDIO_DSD (F_AUDIO_ON | F_AUDIO_DSD) +#define T_AUDIO_NLPCM (F_AUDIO_ON | F_AUDIO_NLPCM) +#define T_AUDIO_LPCM (F_AUDIO_ON) + +/* #define SUPPORT_HBR_AUDIO */ +#ifndef SUPPORT_HBR_AUDIO + #define INPUT_SAMPLE_FREQ AUDFS_48KHz + #define INPUT_SAMPLE_FREQ_HZ 48000L + #define OUTPUT_CHANNEL 2 /* 3,4,5,6,7,8 */ + #define CNOFIG_INPUT_AUDIO_TYPE T_AUDIO_LPCM + #define CONFIG_INPUT_AUDIO_INTERFACE SPDIF + #define I2S_FORMAT 0x01 /* 32bit I2S audio */ +#else /* SUPPORT_HBR_AUDIO */ + #define INPUT_SAMPLE_FREQ AUDFS_768KHz + #define INPUT_SAMPLE_FREQ_HZ 768000L + #define OUTPUT_CHANNEL 8 + #define CNOFIG_INPUT_AUDIO_TYPE T_AUDIO_HBR + #define CONFIG_INPUT_AUDIO_INTERFACE SPDIF + #define I2S_FORMAT 0x47 /* 32bit audio */ +#endif + +/* MIPI Rx Configuration */ +#define MIPI_RX_LANE_COUNT 2 /* 1~4 */ + +#define SUPPORT_AUDI_AudSWL 24 + +#if (SUPPORT_AUDI_AudSWL == 16) + #define CHTSTS_SWCODE 0x02 +#elif (SUPPORT_AUDI_AudSWL == 18) + #define CHTSTS_SWCODE 0x04 +#elif (SUPPORT_AUDI_AudSWL == 20) + #define CHTSTS_SWCODE 0x03 +#else + #define CHTSTS_SWCODE 0x0B +#endif + +#define DDC_FIFO_MAXREQ 0x20 + +/* I2C address */ +#define DDC_EDID_ADDRESS 0xA0 +#define CEC_I2C_SLAVE_ADDR 0x9C + +/* HDMI TX Register offset */ +#define REG_TX_SW_RST 0x04 +#define B_TX_AREF_RST (1<<4) +#define B_HDMITX_VID_RST (1<<3) +#define B_HDMITX_AUD_RST (1<<2) +#define B_TX_HDMI_RST (1<<1) +#define B_TX_HDCP_RST_HDMITX (1<<0) + +#define REG_TX_INT_STAT1 0x06 +#define B_TX_INT_AUD_OVERFLOW (1<<7) +#define B_TX_INT_DDCFIFO_ERR (1<<4) +#define B_TX_INT_DDC_BUS_HANG (1<<2) +#define B_TX_INT_HPD_PLUG (1<<0) + +#define REG_TX_INT_STAT3 0x08 +#define B_TX_INT_VIDSTABLE (1<<4) + +#define REG_TX_INT_MASK1 0x09 +#define B_TX_AUDIO_OVFLW_MASK (1<<7) +#define B_TX_DDC_FIFO_ERR_MASK (1<<4) +#define B_TX_DDC_BUS_HANG_MASK (1<<2) +#define B_TX_RXSEN_MASK (1<<1) +#define B_TX_HPD_MASK (1<<0) + +#define REG_TX_INT_MASK3 0x0B +#define B_TX_VIDSTABLE_MASK (1<<3) + +#define REG_TX_INT_CLR0 0x0C +#define REG_TX_INT_CLR1 0x0D +#define REG_TX_SYS_STATUS 0x0E +#define B_TX_HPDETECT (1<<6) +#define B_TX_RXSENDETECT (1<<5) +#define B_TXVIDSTABLE (1<<4) +#define B_TX_CLR_AUD_CTS (1<<1) +#define B_TX_INTACTDONE (1<<0) + +/* DDC */ +#define REG_TX_DDC_MASTER_CTRL 0x10 +#define B_TX_MASTERROM (1<<1) +#define B_TX_MASTERDDC (0<<1) +#define B_TX_MASTERHOST (1<<0) + +#define REG_TX_DDC_HEADER 0x11 +#define REG_TX_DDC_REQOFF 0x12 +#define REG_TX_DDC_REQCOUNT 0x13 +#define REG_TX_DDC_EDIDSEG 0x14 +#define REG_TX_DDC_CMD 0x15 +#define CMD_EDID_READ 3 +#define CMD_FIFO_CLR 9 +#define CMD_GEN_SCLCLK 0xA +#define CMD_DDC_ABORT 0xF + +#define REG_TX_DDC_STATUS 0x16 +#define B_TX_DDC_DONE (1<<7) +#define B_TX_DDC_NOACK (1<<5) +#define B_TX_DDC_WAITBUS (1<<4) +#define B_TX_DDC_ARBILOSE (1<<3) + +#define REG_TX_DDC_READFIFO 0x17 +#define REG_TX_CLK_CTRL0 0x58 +#define B_TX_AUTO_OVER_SAMPLING_CLOCK (1<<4) +#define O_TX_EXT_MCLK_SEL 2 +#define M_TX_EXT_MCLK_SEL (3<<O_TX_EXT_MCLK_SEL) +#define B_TX_EXT_128FS (0<<O_TX_EXT_MCLK_SEL) +#define B_TX_EXT_256FS (1<<O_TX_EXT_MCLK_SEL) +#define B_TX_EXT_512FS (2<<O_TX_EXT_MCLK_SEL) +#define B_TX_EXT_1024FS (3<<O_TX_EXT_MCLK_SEL) + +#define REG_TX_CLK_STATUS2 0x5F +#define B_TX_OSF_LOCK (1<<5) + +#define REG_TX_AFE_DRV_CTRL 0x61 +#define B_TX_AFE_DRV_PWD (1<<5) +#define B_TX_AFE_DRV_RST (1<<4) + +/* Input Data Format Register */ +#define REG_TX_INPUT_MODE 0x70 +#define B_TX_PCLKDIV2 (1<<5) + +#define REG_TX_CSC_CTRL 0x72 +#define B_HDMITX_CSC_BYPASS 0 +#define B_HDMITX_CSC_RGB2YUV 2 +#define B_HDMITX_CSC_YUV2RGB 3 +#define M_TX_CSC_SEL 3 +#define B_TX_EN_DITHER (1<<7) +#define B_TX_EN_UDFILTER (1<<6) +#define B_TX_DNFREE_GO (1<<5) +#define SIZEOF_CSCMTX 21 + +#define REG_TX_CSC_YOFF 0x73 +#define REG_TX_CSC_COFF 0x74 +#define REG_TX_CSC_RGBOFF 0x75 + +/* HDMI General Control Registers */ +#define REG_TX_HDMI_MODE 0xC0 +#define B_TX_HDMI_MODE 1 +#define B_TX_DVI_MODE 0 +#define REG_TX_GCP 0xC1 +#define B_TX_SETAVMUTE (1<<0) + +#define O_TX_COLOR_DEPTH 4 +#define M_TX_COLOR_DEPTH 7 +#define B_TX_COLOR_DEPTH_MASK (M_TX_COLOR_DEPTH << O_TX_COLOR_DEPTH) +#define REG_TX_PKT_GENERAL_CTRL 0xC6 + +/* Audio Channel Control */ +#define REG_TX_AUDIO_CTRL0 0xE0 +#define M_TX_AUD_16BIT (0<<6) +#define M_TX_AUD_18BIT (1<<6) +#define M_TX_AUD_20BIT (2<<6) +#define M_TX_AUD_24BIT (3<<6) +#define B_TX_AUD_SPDIF (1<<4) +#define B_TX_AUD_I2S (0<<4) +#define B_TX_AUD_EN_I2S3 (1<<3) +#define B_TX_AUD_EN_I2S2 (1<<2) +#define B_TX_AUD_EN_I2S1 (1<<1) +#define B_TX_AUD_EN_I2S0 (1<<0) +#define B_TX_AUD_EN_SPDIF 1 + +#define REG_TX_AUDIO_CTRL1 0xE1 +#define REG_TX_AUDIO_FIFOMAP 0xE2 +#define REG_TX_AUDIO_CTRL3 0xE3 +#define B_TX_CHSTSEL (1<<4) + +#define REG_TX_AUD_SRCVALID_FLAT 0xE4 + +#define REG_TX_AUD_HDAUDIO 0xE5 +#define B_TX_HBR (1<<3) +#define B_TX_DSD (1<<1) +#define B_TX_TDM (1<<0) + +/* HDMI TX reg Bank 1 */ +#define REGPktAudCTS0 0x30 /* 7:0 */ +#define REGPktAudCTS1 0x31 /* 15:8 */ +#define REGPktAudCTS2 0x32 /* 19:16 */ +#define REGPktAudN0 0x33 /* 7:0 */ +#define REGPktAudN1 0x34 /* 15:8 */ +#define REGPktAudN2 0x35 /* 19:16 */ +#define REG_TX_AVIINFO_DB1 0x58 +#define REG_TX_AVIINFO_SUM 0x5D +#define REG_TX_PKT_AUDINFO_CC 0x68 /* [2:0] */ +#define REG_TX_PKT_AUDINFO_SF 0x69 /* [4:2] */ +#define REG_TX_PKT_AUDINFO_CA 0x6B /* [7:0] */ +#define REG_TX_PKT_AUDINFO_DM_LSV 0x6C /* [7][6:3] */ +#define REG_TX_PKT_AUDINFO_SUM 0x6D /* [7:0] */ +#define REG_TX_AUDCHST_MODE 0x91 /* 191 REG_TX_AUD_CHSTD[2:0] 6:4 */ +#define REG_TX_AUDCHST_CAT 0x92 /* 192 REG_TX_AUD_CHSTCAT 7:0 */ +#define REG_TX_AUDCHST_SRCNUM 0x93 /* 193 REG_TX_AUD_CHSTSRC 3:0 */ +#define REG_TX_AUD0CHST_CHTNUM 0x94 /* 194 REG_TX_AUD0_CHSTCHR 7:4 */ +#define REG_TX_AUDCHST_CA_FS 0x98 /* 198 REG_TX_AUD_CHSTCA 5:4 */ +#define REG_TX_AUDCHST_OFS_WL 0x99 /* 199 REG_TX_AUD_CHSTOFS 7:4 */ + +#define REG_TX_PKT_SINGLE_CTRL 0xC5 +#define B_TX_SW_CTS (1<<1) + +#define REG_TX_AVI_INFOFRM_CTRL 0xCD +#define REG_TX_AUD_INFOFRM_CTRL 0xCE +#define B_TX_ENABLE_PKT 1 +#define B_TX_REPEAT_PKT (1<<1) + +struct RegSetEntry { + u8 offset; + u8 mask; + u8 value; +}; + +enum { + PCLK_LOW = 0, + PCLK_MEDIUM, + PCLK_HIGH +}; + +u8 bCSCMtx_RGB2YUV_ITU601_16_235[] = { + 0x00, 0x80, 0x00, + 0xB2, 0x04, 0x65, 0x02, 0xE9, 0x00, + 0x93, 0x3C, 0x18, 0x04, 0x55, 0x3F, + 0x49, 0x3D, 0x9F, 0x3E, 0x18, 0x04 +}; + +u8 bCSCMtx_RGB2YUV_ITU601_0_255[] = { + 0x10, 0x80, 0x10, + 0x09, 0x04, 0x0E, 0x02, 0xC9, 0x00, + 0x0F, 0x3D, 0x84, 0x03, 0x6D, 0x3F, + 0xAB, 0x3D, 0xD1, 0x3E, 0x84, 0x03 +}; + +u8 bCSCMtx_RGB2YUV_ITU709_16_235[] = { + 0x00, 0x80, 0x00, + 0xB8, 0x05, 0xB4, 0x01, 0x94, 0x00, + 0x4a, 0x3C, 0x17, 0x04, 0x9F, 0x3F, + 0xD9, 0x3C, 0x10, 0x3F, 0x17, 0x04 +}; + +u8 bCSCMtx_RGB2YUV_ITU709_0_255[] = { + 0x10, 0x80, 0x10, + 0xEa, 0x04, 0x77, 0x01, 0x7F, 0x00, + 0xD0, 0x3C, 0x83, 0x03, 0xAD, 0x3F, + 0x4B, 0x3D, 0x32, 0x3F, 0x83, 0x03 +}; + +u8 bCSCMtx_YUV2RGB_ITU601_16_235[] = { + 0x00, 0x00, 0x00, + 0x00, 0x08, 0x6B, 0x3A, 0x50, 0x3D, + 0x00, 0x08, 0xF5, 0x0A, 0x02, 0x00, + 0x00, 0x08, 0xFD, 0x3F, 0xDA, 0x0D +}; + +u8 bCSCMtx_YUV2RGB_ITU601_0_255[] = { + 0x04, 0x00, 0xA7, + 0x4F, 0x09, 0x81, 0x39, 0xDD, 0x3C, + 0x4F, 0x09, 0xC4, 0x0C, 0x01, 0x00, + 0x4F, 0x09, 0xFD, 0x3F, 0x1F, 0x10 +}; + +u8 bCSCMtx_YUV2RGB_ITU709_16_235[] = { + 0x00, 0x00, 0x00, + 0x00, 0x08, 0x55, 0x3C, 0x88, 0x3E, + 0x00, 0x08, 0x51, 0x0C, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x84, 0x0E +}; + +u8 bCSCMtx_YUV2RGB_ITU709_0_255[] = { + 0x04, 0x00, 0xA7, + 0x4F, 0x09, 0xBA, 0x3B, 0x4B, 0x3E, + 0x4F, 0x09, 0x57, 0x0E, 0x02, 0x00, + 0x4F, 0x09, 0xFE, 0x3F, 0xE8, 0x10 +}; + +#endif