/* SPDX-License-Identifier: GPL-2.0 */ #include <linux/clk.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/rockchip/grf.h> #include "rockchip_hdmiv2.h" #include "rockchip_hdmiv2_hw.h" #define HDMI_SEL_LCDC(x, bit) ((((x) & 1) << bit) | (1 << (16 + bit))) #define RK3399_GRF_SOC_CON20 0x6250 static const struct phy_mpll_config_tab PHY_MPLL_TABLE[] = { /* tmdsclk = (pixclk / ref_cntrl ) * (fbdiv2 * fbdiv1) / nctrl / tmdsmhl * opmode: 0:HDMI1.4 1:HDMI2.0 * * |pixclock| tmdsclock|pixrepet|colordepth|prepdiv|tmdsmhl|opmode| * fbdiv2|fbdiv1|ref_cntrl|nctrl|propctrl|intctrl|gmpctrl| */ {27000000, 27000000, 0, 8, 0, 0, 0, 2, 3, 0, 3, 3, 0, 0}, {27000000, 27000000, 1, 8, 0, 0, 0, 2, 3, 0, 3, 3, 0, 0}, {27000000, 33750000, 0, 10, 1, 0, 0, 5, 1, 0, 3, 3, 0, 0}, {27000000, 33750000, 1, 10, 1, 0, 0, 5, 1, 0, 3, 3, 0, 0}, {27000000, 40500000, 0, 12, 2, 0, 0, 3, 3, 0, 3, 3, 0, 0}, {27000000, 54000000, 0, 16, 3, 0, 0, 2, 3, 0, 2, 5, 0, 1}, {59400000, 59400000, 0, 8, 0, 0, 0, 1, 3, 0, 2, 5, 0, 1}, {59400000, 74250000, 0, 10, 1, 0, 0, 5, 0, 0, 2, 5, 0, 1}, {59400000, 89100000, 0, 12, 2, 0, 0, 2, 2, 0, 2, 5, 0, 1}, {59400000, 118800000, 0, 16, 3, 0, 0, 1, 3, 0, 1, 7, 0, 2}, {65000000, 65000000, 0, 8, 0, 0, 0, 1, 3, 0, 2, 5, 0, 1}, {74250000, 74250000, 0, 8, 0, 0, 0, 4, 3, 3, 2, 7, 0, 3}, {74250000, 92812500, 0, 10, 1, 0, 0, 5, 0, 1, 1, 7, 0, 2}, {74250000, 111375000, 0, 12, 2, 0, 0, 1, 2, 0, 1, 7, 0, 2}, {74250000, 148500000, 0, 16, 3, 0, 0, 1, 3, 0, 1, 7, 0, 2}, {83500000, 83500000, 0, 8, 0, 0, 0, 1, 3, 0, 2, 5, 0, 1}, {85500000, 85500000, 0, 8, 0, 0, 0, 1, 3, 0, 2, 5, 0, 1}, {106500000, 106500000, 0, 8, 0, 0, 0, 1, 1, 0, 1, 7, 0, 2}, {108000000, 108000000, 0, 8, 0, 0, 0, 1, 1, 0, 1, 7, 0, 2}, {146250000, 146250000, 0, 8, 0, 0, 0, 1, 1, 0, 1, 7, 0, 2}, {148500000, 74250000, 0, 8, 0, 0, 0, 1, 1, 1, 1, 0, 0, 3}, {148500000, 148500000, 0, 8, 0, 0, 0, 1, 1, 0, 1, 0, 0, 3}, {148500000, 185625000, 0, 10, 1, 0, 0, 5, 0, 3, 0, 7, 0, 3}, {148500000, 222750000, 0, 12, 2, 0, 0, 1, 2, 1, 0, 7, 0, 3}, {148500000, 297000000, 0, 16, 3, 0, 0, 1, 1, 0, 0, 7, 0, 3}, {148500000, 297000000, 0, 8, 0, 0, 0, 1, 1, 0, 0, 0, 0, 3}, {148500000, 594000000, 0, 8, 0, 3, 1, 1, 3, 0, 0, 0, 0, 3}, {269390000, 269390000, 0, 8, 0, 0, 0, 1, 0, 0, 0, 0, 0, 3}, {285000000, 285000000, 0, 8, 0, 0, 0, 1, 0, 0, 0, 0, 0, 3}, {297000000, 148500000, 0, 8, 0, 0, 0, 1, 0, 1, 0, 0, 0, 3}, {297000000, 297000000, 0, 8, 0, 0, 0, 1, 0, 0, 0, 0, 0, 3}, {297000000, 371250000, 0, 10, 1, 3, 1, 5, 1, 3, 1, 7, 0, 3}, {297000000, 445500000, 0, 12, 2, 3, 1, 1, 2, 0, 1, 7, 0, 3}, {297000000, 594000000, 0, 16, 3, 3, 1, 1, 3, 1, 0, 0, 0, 3}, {340000000, 340000000, 0, 8, 0, 0, 0, 1, 0, 0, 0, 0, 0, 3}, {403000000, 403000000, 0, 8, 0, 3, 1, 1, 3, 3, 0, 0, 0, 3}, {594000000, 297000000, 0, 8, 0, 0, 0, 1, 0, 1, 0, 0, 0, 3}, {594000000, 371250000, 0, 10, 1, 3, 1, 5, 0, 3, 1, 7, 0, 3}, {594000000, 445500000, 0, 12, 2, 3, 1, 1, 2, 1, 1, 7, 0, 3}, {594000000, 594000000, 0, 16, 3, 3, 1, 1, 3, 3, 0, 0, 0, 3}, {594000000, 594000000, 0, 8, 0, 3, 1, 1, 3, 3, 0, 0, 0, 3}, }; static const struct ext_pll_config_tab EXT_PLL_TABLE[] = { {27000000, 27000000, 8, 1, 90, 3, 2, 2, 10, 3, 3, 4, 0, 1, 40, 8}, {27000000, 33750000, 10, 1, 90, 1, 3, 3, 10, 3, 3, 4, 0, 1, 40, 8}, {59400000, 59400000, 8, 1, 99, 3, 2, 2, 1, 3, 3, 4, 0, 1, 40, 8}, {59400000, 74250000, 10, 1, 99, 1, 2, 2, 1, 3, 3, 4, 0, 1, 40, 8}, {74250000, 74250000, 8, 1, 99, 1, 2, 2, 1, 2, 3, 4, 0, 1, 40, 8}, {74250000, 92812500, 10, 4, 495, 1, 2, 2, 1, 3, 3, 4, 0, 2, 40, 4}, {148500000, 148500000, 8, 1, 99, 1, 1, 1, 1, 2, 2, 2, 0, 2, 40, 4}, {148500000, 185625000, 10, 4, 495, 0, 2, 2, 1, 3, 2, 2, 0, 4, 40, 2}, {297000000, 297000000, 8, 1, 99, 0, 1, 1, 1, 0, 2, 2, 0, 4, 40, 2}, {297000000, 371250000, 10, 4, 495, 1, 2, 0, 1, 3, 1, 1, 0, 8, 40, 1}, {594000000, 297000000, 8, 1, 99, 0, 1, 1, 1, 0, 2, 1, 0, 4, 40, 2}, {594000000, 371250000, 10, 4, 495, 1, 2, 0, 1, 3, 1, 1, 1, 8, 40, 1}, {594000000, 594000000, 8, 1, 99, 0, 2, 0, 1, 0, 1, 1, 0, 8, 40, 1}, }; /* ddc i2c master reset */ static void rockchip_hdmiv2_i2cm_reset(struct hdmi_dev *hdmi_dev) { hdmi_msk_reg(hdmi_dev, I2CM_SOFTRSTZ, m_I2CM_SOFTRST, v_I2CM_SOFTRST(0)); usleep_range(90, 100); } /*set read/write offset,set read/write mode*/ static void rockchip_hdmiv2_i2cm_write_request(struct hdmi_dev *hdmi_dev, u8 offset, u8 data) { hdmi_writel(hdmi_dev, I2CM_ADDRESS, offset); hdmi_writel(hdmi_dev, I2CM_DATAO, data); hdmi_msk_reg(hdmi_dev, I2CM_OPERATION, m_I2CM_WR, v_I2CM_WR(1)); } static void rockchip_hdmiv2_i2cm_read_request(struct hdmi_dev *hdmi_dev, u8 offset) { hdmi_writel(hdmi_dev, I2CM_ADDRESS, offset); hdmi_msk_reg(hdmi_dev, I2CM_OPERATION, m_I2CM_RD, v_I2CM_RD(1)); } static void rockchip_hdmiv2_i2cm_write_data(struct hdmi_dev *hdmi_dev, u8 data, u8 offset) { u8 interrupt = 0; int trytime = 2; int i = 20; while (trytime-- > 0) { rockchip_hdmiv2_i2cm_write_request(hdmi_dev, offset, data); while (i--) { usleep_range(900, 1000); interrupt = hdmi_readl(hdmi_dev, IH_I2CM_STAT0); if (interrupt) hdmi_writel(hdmi_dev, IH_I2CM_STAT0, interrupt); if (interrupt & (m_SCDC_READREQ | m_I2CM_DONE | m_I2CM_ERROR)) break; } if (interrupt & m_I2CM_DONE) { dev_dbg(hdmi_dev->hdmi->dev, "[%s] write offset %02x data %02x success\n", __func__, offset, data); trytime = 0; } else if ((interrupt & m_I2CM_ERROR) || (i == -1)) { dev_err(hdmi_dev->hdmi->dev, "[%s] write data error\n", __func__); rockchip_hdmiv2_i2cm_reset(hdmi_dev); } } } static int rockchip_hdmiv2_i2cm_read_data(struct hdmi_dev *hdmi_dev, u8 offset) { u8 interrupt = 0, val; int trytime = 2; int i = 20; while (trytime-- > 0) { rockchip_hdmiv2_i2cm_read_request(hdmi_dev, offset); while (i--) { usleep_range(900, 1000); interrupt = hdmi_readl(hdmi_dev, IH_I2CM_STAT0); if (interrupt) hdmi_writel(hdmi_dev, IH_I2CM_STAT0, interrupt); if (interrupt & (m_SCDC_READREQ | m_I2CM_DONE | m_I2CM_ERROR)) break; } if (interrupt & m_I2CM_DONE) { val = hdmi_readl(hdmi_dev, I2CM_DATAI); trytime = 0; } else if ((interrupt & m_I2CM_ERROR) || (i == -1)) { pr_err("[%s] read data error\n", __func__); rockchip_hdmiv2_i2cm_reset(hdmi_dev); } } return val; } static void rockchip_hdmiv2_i2cm_mask_int(struct hdmi_dev *hdmi_dev, int mask) { if (!mask) { hdmi_msk_reg(hdmi_dev, I2CM_INT, m_I2CM_DONE_MASK, v_I2CM_DONE_MASK(0)); hdmi_msk_reg(hdmi_dev, I2CM_CTLINT, m_I2CM_NACK_MASK | m_I2CM_ARB_MASK, v_I2CM_NACK_MASK(0) | v_I2CM_ARB_MASK(0)); } else { hdmi_msk_reg(hdmi_dev, I2CM_INT, m_I2CM_DONE_MASK, v_I2CM_DONE_MASK(1)); hdmi_msk_reg(hdmi_dev, I2CM_CTLINT, m_I2CM_NACK_MASK | m_I2CM_ARB_MASK, v_I2CM_NACK_MASK(1) | v_I2CM_ARB_MASK(1)); } } #define I2C_DIV_FACTOR 1000000 static u16 i2c_count(u16 sfrclock, u16 sclmintime) { unsigned long tmp_scl_period = 0; if (((sfrclock * sclmintime) % I2C_DIV_FACTOR) != 0) tmp_scl_period = (unsigned long)((sfrclock * sclmintime) + (I2C_DIV_FACTOR - ((sfrclock * sclmintime) % I2C_DIV_FACTOR))) / I2C_DIV_FACTOR; else tmp_scl_period = (unsigned long)(sfrclock * sclmintime) / I2C_DIV_FACTOR; return (u16)(tmp_scl_period); } #define EDID_I2C_MIN_SS_SCL_HIGH_TIME 9625 #define EDID_I2C_MIN_SS_SCL_LOW_TIME 10000 static void rockchip_hdmiv2_i2cm_clk_init(struct hdmi_dev *hdmi_dev) { int value; /* Set DDC I2C CLK which divided from DDC_CLK. */ value = i2c_count(24000, EDID_I2C_MIN_SS_SCL_HIGH_TIME); hdmi_writel(hdmi_dev, I2CM_SS_SCL_HCNT_0_ADDR, value & 0xff); hdmi_writel(hdmi_dev, I2CM_SS_SCL_HCNT_1_ADDR, (value >> 8) & 0xff); value = i2c_count(24000, EDID_I2C_MIN_SS_SCL_LOW_TIME); hdmi_writel(hdmi_dev, I2CM_SS_SCL_LCNT_0_ADDR, value & 0xff); hdmi_writel(hdmi_dev, I2CM_SS_SCL_LCNT_1_ADDR, (value >> 8) & 0xff); hdmi_msk_reg(hdmi_dev, I2CM_DIV, m_I2CM_FAST_STD_MODE, v_I2CM_FAST_STD_MODE(STANDARD_MODE)); } static int rockchip_hdmiv2_scdc_get_sink_version(struct hdmi_dev *hdmi_dev) { return rockchip_hdmiv2_i2cm_read_data(hdmi_dev, SCDC_SINK_VER); } static void rockchip_hdmiv2_scdc_set_source_version(struct hdmi_dev *hdmi_dev, u8 version) { rockchip_hdmiv2_i2cm_write_data(hdmi_dev, version, SCDC_SOURCE_VER); } static void rockchip_hdmiv2_scdc_read_request(struct hdmi_dev *hdmi_dev, int enable) { hdmi_msk_reg(hdmi_dev, I2CM_SCDC_READ_UPDATE, m_I2CM_READ_REQ_EN, v_I2CM_READ_REQ_EN(enable)); rockchip_hdmiv2_i2cm_write_data(hdmi_dev, enable, SCDC_CONFIG_0); } #ifdef HDMI_20_SCDC static void rockchip_hdmiv2_scdc_update_read(struct hdmi_dev *hdmi_dev) { hdmi_msk_reg(hdmi_dev, I2CM_SCDC_READ_UPDATE, m_I2CM_READ_UPDATE, v_I2CM_READ_UPDATE(1)); } static int rockchip_hdmiv2_scdc_get_scambling_status(struct hdmi_dev *hdmi_dev) { int val; val = rockchip_hdmiv2_i2cm_read_data(hdmi_dev, SCDC_SCRAMBLER_STAT); return val; } static void rockchip_hdmiv2_scdc_enable_polling(struct hdmi_dev *hdmi_dev, int enable) { rockchip_hdmiv2_scdc_read_request(hdmi_dev, enable); hdmi_msk_reg(hdmi_dev, I2CM_SCDC_READ_UPDATE, m_I2CM_UPRD_VSYNC_EN, v_I2CM_UPRD_VSYNC_EN(enable)); } static int rockchip_hdmiv2_scdc_get_status_reg0(struct hdmi_dev *hdmi_dev) { rockchip_hdmiv2_scdc_read_request(hdmi_dev, 1); rockchip_hdmiv2_scdc_update_read(hdmi_dev); return hdmi_readl(hdmi_dev, I2CM_SCDC_UPDATE0); } static int rockchip_hdmiv2_scdc_get_status_reg1(struct hdmi_dev *hdmi_dev) { rockchip_hdmiv2_scdc_read_request(hdmi_dev, 1); rockchip_hdmiv2_scdc_update_read(hdmi_dev); return hdmi_readl(hdmi_dev, I2CM_SCDC_UPDATE1); } #endif static void rockchip_hdmiv2_scdc_init(struct hdmi_dev *hdmi_dev) { rockchip_hdmiv2_i2cm_reset(hdmi_dev); rockchip_hdmiv2_i2cm_mask_int(hdmi_dev, 1); rockchip_hdmiv2_i2cm_clk_init(hdmi_dev); /* set scdc i2c addr */ hdmi_writel(hdmi_dev, I2CM_SLAVE, DDC_I2C_SCDC_ADDR); rockchip_hdmiv2_i2cm_mask_int(hdmi_dev, 0);/*enable interrupt*/ } static void rockchip_hdmiv2_scdc_set_tmds_rate(struct hdmi_dev *hdmi_dev) { int stat; mutex_lock(&hdmi_dev->ddc_lock); rockchip_hdmiv2_scdc_init(hdmi_dev); stat = rockchip_hdmiv2_i2cm_read_data(hdmi_dev, SCDC_TMDS_CONFIG); if (hdmi_dev->tmdsclk > 340000000) stat |= 2; else stat &= 0x1; rockchip_hdmiv2_i2cm_write_data(hdmi_dev, stat, SCDC_TMDS_CONFIG); mutex_unlock(&hdmi_dev->ddc_lock); } static int rockchip_hdmiv2_scrambling_enable(struct hdmi_dev *hdmi_dev, int enable) { HDMIDBG(2, "%s enable %d\n", __func__, enable); if (enable == 1) { /* Write on Rx the bit Scrambling_Enable, register 0x20 */ rockchip_hdmiv2_i2cm_write_data(hdmi_dev, 1, SCDC_TMDS_CONFIG); /* TMDS software reset request */ hdmi_msk_reg(hdmi_dev, MC_SWRSTZREQ, m_TMDS_SWRST, v_TMDS_SWRST(0)); /* Enable/Disable Scrambling */ hdmi_msk_reg(hdmi_dev, FC_SCRAMBLER_CTRL, m_FC_SCRAMBLE_EN, v_FC_SCRAMBLE_EN(1)); } else { /* Enable/Disable Scrambling */ hdmi_msk_reg(hdmi_dev, FC_SCRAMBLER_CTRL, m_FC_SCRAMBLE_EN, v_FC_SCRAMBLE_EN(0)); /* TMDS software reset request */ hdmi_msk_reg(hdmi_dev, MC_SWRSTZREQ, m_TMDS_SWRST, v_TMDS_SWRST(0)); /* Write on Rx the bit Scrambling_Enable, register 0x20 */ rockchip_hdmiv2_i2cm_write_data(hdmi_dev, 0, SCDC_TMDS_CONFIG); } return 0; } static const struct ext_pll_config_tab *get_phy_ext_tab( unsigned int pixclock, unsigned int tmdsclk, char colordepth) { int i; if (pixclock == 0) return NULL; HDMIDBG(2, "%s pixClock %u tmdsclk %u colorDepth %d\n", __func__, pixclock, tmdsclk, colordepth); for (i = 0; i < ARRAY_SIZE(EXT_PLL_TABLE); i++) { if ((EXT_PLL_TABLE[i].pix_clock == pixclock) && (EXT_PLL_TABLE[i].tmdsclock == tmdsclk) && (EXT_PLL_TABLE[i].color_depth == colordepth)) return &EXT_PLL_TABLE[i]; } return NULL; } static const struct phy_mpll_config_tab *get_phy_mpll_tab( unsigned int pixclock, unsigned int tmdsclk, char pixrepet, char colordepth) { int i; if (pixclock == 0) return NULL; HDMIDBG(2, "%s pixClock %u tmdsclk %u pixRepet %d colorDepth %d\n", __func__, pixclock, tmdsclk, pixrepet, colordepth); for (i = 0; i < ARRAY_SIZE(PHY_MPLL_TABLE); i++) { if ((PHY_MPLL_TABLE[i].pix_clock == pixclock) && (PHY_MPLL_TABLE[i].tmdsclock == tmdsclk) && (PHY_MPLL_TABLE[i].pix_repet == pixrepet) && (PHY_MPLL_TABLE[i].color_depth == colordepth)) return &PHY_MPLL_TABLE[i]; } return NULL; } static void rockchip_hdmiv2_powerdown(struct hdmi_dev *hdmi_dev) { hdmi_msk_reg(hdmi_dev, PHY_MASK, m_PHY_LOCK, v_PHY_LOCK(1)); if (hdmi_dev->soctype != HDMI_SOC_RK322X) { hdmi_msk_reg(hdmi_dev, PHY_CONF0, m_PDDQ_SIG | m_TXPWRON_SIG | m_ENHPD_RXSENSE_SIG | m_SVSRET_SIG, v_PDDQ_SIG(1) | v_TXPWRON_SIG(0) | v_ENHPD_RXSENSE_SIG(1)) | v_SVSRET_SIG(0); } else { hdmi_msk_reg(hdmi_dev, PHY_CONF0, m_TXPWRON_SIG | m_ENHPD_RXSENSE_SIG, v_TXPWRON_SIG(0) | v_ENHPD_RXSENSE_SIG(0)); regmap_write(hdmi_dev->grf_base, RK322X_GRF_SOC_CON2, RK322X_PLL_PDATA_DEN); } hdmi_writel(hdmi_dev, MC_CLKDIS, 0x7f); } int rockchip_hdmiv2_write_phy(struct hdmi_dev *hdmi_dev, int reg_addr, int val) { int trytime = 2, i = 0, op_status = 0; if (hdmi_dev->phybase) { writel_relaxed(val, hdmi_dev->phybase + (reg_addr) * 0x04); return 0; } while (trytime--) { hdmi_writel(hdmi_dev, PHY_I2CM_ADDRESS, reg_addr); hdmi_writel(hdmi_dev, PHY_I2CM_DATAO_1, (val >> 8) & 0xff); hdmi_writel(hdmi_dev, PHY_I2CM_DATAO_0, val & 0xff); hdmi_writel(hdmi_dev, PHY_I2CM_OPERATION, m_PHY_I2CM_WRITE); i = 20; while (i--) { usleep_range(900, 1000); op_status = hdmi_readl(hdmi_dev, IH_I2CMPHY_STAT0); if (op_status) hdmi_writel(hdmi_dev, IH_I2CMPHY_STAT0, op_status); if (op_status & (m_I2CMPHY_DONE | m_I2CMPHY_ERR)) break; } if (!(op_status & m_I2CMPHY_DONE)) dev_err(hdmi_dev->hdmi->dev, "[%s] operation error,trytime=%d\n", __func__, trytime); else return 0; msleep(100); } return -1; } int rockchip_hdmiv2_read_phy(struct hdmi_dev *hdmi_dev, int reg_addr) { int trytime = 2, i = 0, op_status = 0; int val = 0; if (hdmi_dev->phybase) return readl_relaxed(hdmi_dev->phybase + (reg_addr) * 0x04); while (trytime--) { hdmi_writel(hdmi_dev, PHY_I2CM_ADDRESS, reg_addr); hdmi_writel(hdmi_dev, PHY_I2CM_DATAI_1, 0x00); hdmi_writel(hdmi_dev, PHY_I2CM_DATAI_0, 0x00); hdmi_writel(hdmi_dev, PHY_I2CM_OPERATION, m_PHY_I2CM_READ); i = 20; while (i--) { usleep_range(900, 1000); op_status = hdmi_readl(hdmi_dev, IH_I2CMPHY_STAT0); if (op_status) hdmi_writel(hdmi_dev, IH_I2CMPHY_STAT0, op_status); if (op_status & (m_I2CMPHY_DONE | m_I2CMPHY_ERR)) break; } if (!(op_status & m_I2CMPHY_DONE)) { pr_err("[%s] operation error,trytime=%d\n", __func__, trytime); } else { val = hdmi_readl(hdmi_dev, PHY_I2CM_DATAI_1); val = (val & 0xff) << 8; val += (hdmi_readl(hdmi_dev, PHY_I2CM_DATAI_0) & 0xff); pr_debug("phy_reg0x%02x: 0x%04x", reg_addr, val); return val; } msleep(100); } return -1; } #define PHY_TIMEOUT 10000 static int ext_phy_config(struct hdmi_dev *hdmi_dev) { int stat = 0, i = 0, temp; const struct ext_pll_config_tab *phy_ext = NULL; if (hdmi_dev->grf_base) regmap_write(hdmi_dev->grf_base, RK322X_GRF_SOC_CON2, RK322X_PLL_POWER_DOWN | RK322X_PLL_PDATA_DEN); if (hdmi_dev->tmdsclk_ratio_change && hdmi_dev->hdmi->edid.scdc_present == 1) rockchip_hdmiv2_scdc_set_tmds_rate(hdmi_dev); /* config the required PHY I2C register */ phy_ext = get_phy_ext_tab(hdmi_dev->pixelclk, hdmi_dev->tmdsclk, hdmi_dev->colordepth); if (phy_ext) { stat = ((phy_ext->pll_nf >> 1) & EXT_PHY_PLL_FB_BIT8_MASK) | ((phy_ext->vco_div_5 & 1) << 5) | (phy_ext->pll_nd & EXT_PHY_PLL_PRE_DIVIDER_MASK); rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_PLL_PRE_DIVIDER, stat); stat = phy_ext->pll_nf & 0xff; rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_PLL_FB_DIVIDER, stat); stat = (phy_ext->pclk_divider_a & EXT_PHY_PCLK_DIVIDERA_MASK) | ((phy_ext->pclk_divider_b & 3) << 5); rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_PCLK_DIVIDER1, stat); stat = (phy_ext->pclk_divider_d & EXT_PHY_PCLK_DIVIDERD_MASK) | ((phy_ext->pclk_divider_c & 3) << 5); rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_PCLK_DIVIDER2, stat); stat = ((phy_ext->tmsd_divider_c & 3) << 4) | ((phy_ext->tmsd_divider_a & 3) << 2) | (phy_ext->tmsd_divider_b & 3); rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_TMDSCLK_DIVIDER, stat); rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_PPLL_FB_DIVIDER, phy_ext->ppll_nf); if (phy_ext->ppll_no == 1) { rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_PPLL_POST_DIVIDER, 0); stat = 0x20 | phy_ext->ppll_nd; rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_PPLL_PRE_DIVIDER, stat); } else { stat = ((phy_ext->ppll_no / 2) - 1) << 4; rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_PPLL_POST_DIVIDER, stat); stat = 0xe0 | phy_ext->ppll_nd; rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_PPLL_PRE_DIVIDER, stat); } } else { pr_err("%s no supported phy configuration.\n", __func__); return -1; } if (hdmi_dev->phy_table) { for (i = 0; i < hdmi_dev->phy_table_size; i++) { temp = hdmi_dev->phy_table[i].maxfreq; if (hdmi_dev->tmdsclk <= temp) break; } } if (i != hdmi_dev->phy_table_size && hdmi_dev->phy_table) { if (hdmi_dev->phy_table[i].slopeboost) { rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_SIGNAL_CTRL, 0xff); temp = hdmi_dev->phy_table[i].slopeboost - 1; stat = ((temp & 3) << 6) | ((temp & 3) << 4) | ((temp & 3) << 2) | (temp & 3); rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_SLOPEBOOST, stat); } else { rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_SIGNAL_CTRL, 0x0f); } stat = ((hdmi_dev->phy_table[i].pre_emphasis & 3) << 4) | ((hdmi_dev->phy_table[i].pre_emphasis & 3) << 2) | (hdmi_dev->phy_table[i].pre_emphasis & 3); rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_PREEMPHASIS, stat); stat = ((hdmi_dev->phy_table[i].clk_level & 0xf) << 4) | (hdmi_dev->phy_table[i].data2_level & 0xf); rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_LEVEL1, stat); stat = ((hdmi_dev->phy_table[i].data1_level & 0xf) << 4) | (hdmi_dev->phy_table[i].data0_level & 0xf); rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_LEVEL2, stat); } else { rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_SIGNAL_CTRL, 0x0f); } rockchip_hdmiv2_write_phy(hdmi_dev, 0xf3, 0x22); stat = clk_get_rate(hdmi_dev->pclk_phy) / 100000; rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_TERM_CAL, ((stat >> 8) & 0xff) | 0x80); rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_TERM_CAL_DIV_L, stat & 0xff); if (hdmi_dev->tmdsclk > 340000000) stat = EXT_PHY_AUTO_R100_OHMS; else if (hdmi_dev->tmdsclk > 200000000) stat = EXT_PHY_AUTO_R50_OHMS; else stat = EXT_PHY_AUTO_ROPEN_CIRCUIT; rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_TERM_RESIS_AUTO, stat | 0x20); rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_TERM_CAL, (stat >> 8) & 0xff); if (hdmi_dev->tmdsclk > 200000000) stat = 0; else stat = 0x11; rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_PLL_BW, stat); rockchip_hdmiv2_write_phy(hdmi_dev, EXT_PHY_PPLL_BW, 0x27); if (hdmi_dev->grf_base) regmap_write(hdmi_dev->grf_base, RK322X_GRF_SOC_CON2, RK322X_PLL_POWER_UP); if (hdmi_dev->tmdsclk_ratio_change) msleep(100); else usleep_range(900, 1000); hdmi_msk_reg(hdmi_dev, PHY_CONF0, m_TXPWRON_SIG, v_TXPWRON_SIG(1)); i = 0; while (i++ < PHY_TIMEOUT) { if ((i % 10) == 0) { temp = EXT_PHY_PPLL_POST_DIVIDER; stat = rockchip_hdmiv2_read_phy(hdmi_dev, temp); if (stat & EXT_PHY_PPLL_LOCK_STATUS_MASK) break; usleep_range(1000, 2000); } } if ((stat & EXT_PHY_PPLL_LOCK_STATUS_MASK) == 0) { stat = hdmi_readl(hdmi_dev, MC_LOCKONCLOCK); dev_err(hdmi_dev->hdmi->dev, "PHY PLL not locked: PCLK_ON=%ld,TMDSCLK_ON=%ld\n", (stat & m_PCLK_ON) >> 6, (stat & m_TMDSCLK_ON) >> 5); return -1; } if (hdmi_dev->grf_base) regmap_write(hdmi_dev->grf_base, RK322X_GRF_SOC_CON2, RK322X_PLL_PDATA_EN); return 0; } static int rockchip_hdmiv2_config_phy(struct hdmi_dev *hdmi_dev) { int stat = 0, i = 0; const struct phy_mpll_config_tab *phy_mpll = NULL; if (hdmi_dev->soctype == HDMI_SOC_RK322X) { return ext_phy_config(hdmi_dev); } else if (hdmi_dev->soctype == HDMI_SOC_RK3366) { if (hdmi_dev->pixelclk > 148500000) clk_set_rate(hdmi_dev->pclk_phy, 148500000); else clk_set_rate(hdmi_dev->pclk_phy, hdmi_dev->pixelclk); } else if (hdmi_dev->soctype == HDMI_SOC_RK3399) { clk_set_rate(hdmi_dev->pclk_phy, hdmi_dev->pixelclk); } hdmi_msk_reg(hdmi_dev, PHY_I2CM_DIV, m_PHY_I2CM_FAST_STD, v_PHY_I2CM_FAST_STD(0)); hdmi_msk_reg(hdmi_dev, PHY_MASK, m_PHY_LOCK, v_PHY_LOCK(1)); /* power off PHY */ hdmi_msk_reg(hdmi_dev, PHY_CONF0, m_PDDQ_SIG | m_TXPWRON_SIG | m_SVSRET_SIG, v_PDDQ_SIG(1) | v_TXPWRON_SIG(0) | v_SVSRET_SIG(1)); if (hdmi_dev->tmdsclk_ratio_change && hdmi_dev->hdmi->edid.scdc_present == 1) rockchip_hdmiv2_scdc_set_tmds_rate(hdmi_dev); /* reset PHY */ hdmi_writel(hdmi_dev, MC_PHYRSTZ, v_PHY_RSTZ(1)); usleep_range(1000, 2000); hdmi_writel(hdmi_dev, MC_PHYRSTZ, v_PHY_RSTZ(0)); /* Set slave address as PHY GEN2 address */ hdmi_writel(hdmi_dev, PHY_I2CM_SLAVE, PHY_GEN2_ADDR); /* config the required PHY I2C register */ if (hdmi_dev->soctype == HDMI_SOC_RK3366 && hdmi_dev->pixelclk > 148500000) phy_mpll = get_phy_mpll_tab(148500000, hdmi_dev->tmdsclk, hdmi_dev->pixelrepeat - 1, hdmi_dev->colordepth); else phy_mpll = get_phy_mpll_tab(hdmi_dev->pixelclk, hdmi_dev->tmdsclk, hdmi_dev->pixelrepeat - 1, hdmi_dev->colordepth); if (phy_mpll) { rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_OPMODE_PLLCFG, v_PREP_DIV(phy_mpll->prep_div) | v_TMDS_CNTRL( phy_mpll->tmdsmhl_cntrl) | v_OPMODE(phy_mpll->opmode) | v_FBDIV2_CNTRL( phy_mpll->fbdiv2_cntrl) | v_FBDIV1_CNTRL( phy_mpll->fbdiv1_cntrl) | v_REF_CNTRL(phy_mpll->ref_cntrl) | v_MPLL_N_CNTRL(phy_mpll->n_cntrl)); rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_PLLCURRCTRL, v_MPLL_PROP_CNTRL( phy_mpll->prop_cntrl) | v_MPLL_INT_CNTRL( phy_mpll->int_cntrl)); rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_PLLGMPCTRL, v_MPLL_GMP_CNTRL( phy_mpll->gmp_cntrl)); } if (hdmi_dev->phy_table) { for (i = 0; i < hdmi_dev->phy_table_size; i++) if (hdmi_dev->tmdsclk <= hdmi_dev->phy_table[i].maxfreq) break; } if (i != hdmi_dev->phy_table_size && hdmi_dev->phy_table) { stat = v_OVERRIDE(1) | v_TX_SYMON(1) | v_CLK_SYMON(1) | v_PREEMPHASIS(hdmi_dev->phy_table[i].pre_emphasis) | v_SLOPEBOOST(hdmi_dev->phy_table[i].slopeboost); rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_CLKSYMCTRL, stat); stat = v_SUP_CLKLVL(hdmi_dev->phy_table[i].clk_level) | v_SUP_TXLVL(hdmi_dev->phy_table[i].data0_level); rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_VLEVCTRL, stat); } else { pr_info("%s use default phy settings\n", __func__); rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_CLKSYMCTRL, v_OVERRIDE(1) | v_SLOPEBOOST(0) | v_TX_SYMON(1) | v_CLK_SYMON(1) | v_PREEMPHASIS(0)); if (hdmi_dev->tmdsclk > 340000000) rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_VLEVCTRL, v_SUP_TXLVL(9) | v_SUP_CLKLVL(17)); else if (hdmi_dev->tmdsclk > 165000000) rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_VLEVCTRL, v_SUP_TXLVL(14) | v_SUP_CLKLVL(17)); else rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_VLEVCTRL, v_SUP_TXLVL(18) | v_SUP_CLKLVL(17)); } if (hdmi_dev->tmdsclk > 340000000) rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_TERM_RESIS, v_TX_TERM(R50_OHMS)); else rockchip_hdmiv2_write_phy(hdmi_dev, PHYTX_TERM_RESIS, v_TX_TERM(R100_OHMS)); /* rockchip_hdmiv2_write_phy(hdmi_dev, 0x05, 0x8000); */ if (hdmi_dev->tmdsclk_ratio_change) msleep(100); /* power on PHY */ hdmi_writel(hdmi_dev, PHY_CONF0, 0x2e); /* check if the PHY PLL is locked */ i = 0; while (i++ < PHY_TIMEOUT) { if ((i % 10) == 0) { stat = hdmi_readl(hdmi_dev, PHY_STAT0); if (stat & m_PHY_LOCK) break; usleep_range(1000, 2000); } } if ((stat & m_PHY_LOCK) == 0) { stat = hdmi_readl(hdmi_dev, MC_LOCKONCLOCK); dev_err(hdmi_dev->hdmi->dev, "PHY PLL not locked: PCLK_ON=%ld,TMDSCLK_ON=%ld\n", (stat & m_PCLK_ON) >> 6, (stat & m_TMDSCLK_ON) >> 5); return -1; } hdmi_msk_reg(hdmi_dev, PHY_MASK, m_PHY_LOCK, v_PHY_LOCK(0)); return 0; } static int rockchip_hdmiv2_video_framecomposer(struct hdmi *hdmi_drv, struct hdmi_video *vpara) { struct hdmi_dev *hdmi_dev = hdmi_drv->property->priv; int value, vsync_pol, hsync_pol, de_pol; struct hdmi_video_timing *timing = NULL; struct fb_videomode *mode = NULL; u32 sink_version, tmdsclk; vsync_pol = hdmi_drv->lcdc->cur_screen->pin_vsync; hsync_pol = hdmi_drv->lcdc->cur_screen->pin_hsync; de_pol = (hdmi_drv->lcdc->cur_screen->pin_den == 0) ? 1 : 0; hdmi_msk_reg(hdmi_dev, A_VIDPOLCFG, m_DATAEN_POL | m_VSYNC_POL | m_HSYNC_POL, v_DATAEN_POL(de_pol) | v_VSYNC_POL(vsync_pol) | v_HSYNC_POL(hsync_pol)); timing = (struct hdmi_video_timing *)hdmi_vic2timing(vpara->vic); if (!timing) { dev_err(hdmi_drv->dev, "[%s] not found vic %d\n", __func__, vpara->vic); return -ENOENT; } mode = &timing->mode; if (vpara->color_input == HDMI_COLOR_YCBCR420) tmdsclk = mode->pixclock / 2; else if (vpara->format_3d == HDMI_3D_FRAME_PACKING) tmdsclk = 2 * mode->pixclock; else tmdsclk = mode->pixclock; if (vpara->color_output != HDMI_COLOR_YCBCR422) { switch (vpara->color_output_depth) { case 10: tmdsclk += tmdsclk / 4; break; case 12: tmdsclk += tmdsclk / 2; break; case 16: tmdsclk += tmdsclk; break; case 8: default: break; } } else if (vpara->color_output_depth > 12) { /* YCbCr422 mode only support up to 12bit */ vpara->color_output_depth = 12; } if ((tmdsclk > 594000000) || (tmdsclk > 340000000 && tmdsclk > hdmi_drv->edid.maxtmdsclock)) { if (vpara->format_3d == HDMI_3D_FRAME_PACKING) { pr_err("3d frame packing mode out of max tmdsclk\n"); return -1; } else if (vpara->color_output == HDMI_COLOR_YCBCR444 && hdmi_drv->edid.ycbcr422) { pr_warn("out of max tmdsclk, down to YCbCr422"); vpara->color_output = HDMI_COLOR_YCBCR422; tmdsclk = mode->pixclock; } else { pr_warn("out of max tmds clock, limit to 8bit\n"); vpara->color_output_depth = 8; if (vpara->color_input == HDMI_COLOR_YCBCR420) tmdsclk = mode->pixclock / 2; else tmdsclk = mode->pixclock; } } if ((tmdsclk > 340000000) || (tmdsclk < 340000000 && hdmi_dev->tmdsclk > 340000000)) hdmi_dev->tmdsclk_ratio_change = true; else hdmi_dev->tmdsclk_ratio_change = false; hdmi_dev->tmdsclk = tmdsclk; if (vpara->format_3d == HDMI_3D_FRAME_PACKING) hdmi_dev->pixelclk = 2 * mode->pixclock; else hdmi_dev->pixelclk = mode->pixclock; hdmi_dev->pixelrepeat = timing->pixelrepeat; /* hdmi_dev->colordepth is used for find pll config. * For YCbCr422, tmdsclk is same on all color depth. */ if (vpara->color_output == HDMI_COLOR_YCBCR422) hdmi_dev->colordepth = 8; else hdmi_dev->colordepth = vpara->color_output_depth; pr_info("pixel clk is %lu tmds clk is %u\n", hdmi_dev->pixelclk, hdmi_dev->tmdsclk); /* Start/stop HDCP keepout window generation */ hdmi_msk_reg(hdmi_dev, FC_INVIDCONF, m_FC_HDCP_KEEPOUT, v_FC_HDCP_KEEPOUT(1)); if (hdmi_drv->edid.scdc_present == 1 && !hdmi_drv->uboot) { if (tmdsclk > 340000000 || hdmi_drv->edid.lte_340mcsc_scramble) { /* used for HDMI 2.0 TX */ mutex_lock(&hdmi_dev->ddc_lock); rockchip_hdmiv2_scdc_init(hdmi_dev); sink_version = rockchip_hdmiv2_scdc_get_sink_version(hdmi_dev); pr_info("sink scdc version is %d\n", sink_version); sink_version = hdmi_drv->edid.hf_vsdb_version; rockchip_hdmiv2_scdc_set_source_version(hdmi_dev, sink_version); if (hdmi_drv->edid.rr_capable == 1) rockchip_hdmiv2_scdc_read_request(hdmi_dev, 1); rockchip_hdmiv2_scrambling_enable(hdmi_dev, 1); mutex_unlock(&hdmi_dev->ddc_lock); } else { mutex_lock(&hdmi_dev->ddc_lock); rockchip_hdmiv2_scdc_init(hdmi_dev); rockchip_hdmiv2_scrambling_enable(hdmi_dev, 0); mutex_unlock(&hdmi_dev->ddc_lock); } } else { hdmi_msk_reg(hdmi_dev, FC_SCRAMBLER_CTRL, m_FC_SCRAMBLE_EN, v_FC_SCRAMBLE_EN(0)); } hdmi_msk_reg(hdmi_dev, FC_INVIDCONF, m_FC_VSYNC_POL | m_FC_HSYNC_POL | m_FC_DE_POL | m_FC_HDMI_DVI | m_FC_INTERLACE_MODE, v_FC_VSYNC_POL(vsync_pol) | v_FC_HSYNC_POL(hsync_pol) | v_FC_DE_POL(de_pol) | v_FC_HDMI_DVI(vpara->sink_hdmi) | v_FC_INTERLACE_MODE(mode->vmode)); if ((mode->vmode & FB_VMODE_INTERLACED) && vpara->format_3d != HDMI_3D_FRAME_PACKING) hdmi_msk_reg(hdmi_dev, FC_INVIDCONF, m_FC_VBLANK, v_FC_VBLANK(1)); else hdmi_msk_reg(hdmi_dev, FC_INVIDCONF, m_FC_VBLANK, v_FC_VBLANK(0)); value = mode->xres; if (vpara->color_input == HDMI_COLOR_YCBCR420) value = value / 2; hdmi_writel(hdmi_dev, FC_INHACTIV1, v_FC_HACTIVE1(value >> 8)); hdmi_writel(hdmi_dev, FC_INHACTIV0, (value & 0xff)); if (vpara->format_3d == HDMI_3D_FRAME_PACKING) { if (mode->vmode == 0) value = 2 * mode->yres + mode->upper_margin + mode->lower_margin + mode->vsync_len; else value = 2 * mode->yres + 3 * (mode->upper_margin + mode->lower_margin + mode->vsync_len) + 2; } else { value = mode->yres; } hdmi_writel(hdmi_dev, FC_INVACTIV1, v_FC_VACTIVE1(value >> 8)); hdmi_writel(hdmi_dev, FC_INVACTIV0, (value & 0xff)); value = mode->hsync_len + mode->left_margin + mode->right_margin; if (vpara->color_input == HDMI_COLOR_YCBCR420) value = value / 2; hdmi_writel(hdmi_dev, FC_INHBLANK1, v_FC_HBLANK1(value >> 8)); hdmi_writel(hdmi_dev, FC_INHBLANK0, (value & 0xff)); value = mode->vsync_len + mode->upper_margin + mode->lower_margin; hdmi_writel(hdmi_dev, FC_INVBLANK, (value & 0xff)); value = mode->right_margin; if (vpara->color_input == HDMI_COLOR_YCBCR420) value = value / 2; hdmi_writel(hdmi_dev, FC_HSYNCINDELAY1, v_FC_HSYNCINDEAY1(value >> 8)); hdmi_writel(hdmi_dev, FC_HSYNCINDELAY0, (value & 0xff)); value = mode->lower_margin; hdmi_writel(hdmi_dev, FC_VSYNCINDELAY, (value & 0xff)); value = mode->hsync_len; if (vpara->color_input == HDMI_COLOR_YCBCR420) value = value / 2; hdmi_writel(hdmi_dev, FC_HSYNCINWIDTH1, v_FC_HSYNCWIDTH1(value >> 8)); hdmi_writel(hdmi_dev, FC_HSYNCINWIDTH0, (value & 0xff)); value = mode->vsync_len; hdmi_writel(hdmi_dev, FC_VSYNCINWIDTH, (value & 0xff)); /* Set the control period minimum duration (min. of 12 pixel * clock cycles, refer to HDMI 1.4b specification) */ hdmi_writel(hdmi_dev, FC_CTRLDUR, 12); hdmi_writel(hdmi_dev, FC_EXCTRLDUR, 32); /* spacing < 256^2 * config / tmdsClock, spacing <= 50ms * worst case: tmdsClock == 25MHz => config <= 19 */ hdmi_writel(hdmi_dev, FC_EXCTRLSPAC, (hdmi_dev->tmdsclk / 1000) * 50 / (256 * 512)); hdmi_writel(hdmi_dev, FC_PRCONF, v_FC_PR_FACTOR(timing->pixelrepeat) | v_FC_PR_FACTOR_OUT(timing->pixelrepeat - 1)); return 0; } static int rockchip_hdmiv2_video_packetizer(struct hdmi_dev *hdmi_dev, struct hdmi_video *vpara) { unsigned char color_depth = COLOR_DEPTH_24BIT_DEFAULT; unsigned char output_select = 0; unsigned char remap_size = 0; if (vpara->color_output == HDMI_COLOR_YCBCR422) { switch (vpara->color_output_depth) { case 8: remap_size = YCC422_16BIT; break; case 10: remap_size = YCC422_20BIT; break; case 12: remap_size = YCC422_24BIT; break; default: remap_size = YCC422_16BIT; break; } output_select = OUT_FROM_YCC422_REMAP; /*Config remap size for the different color Depth*/ hdmi_msk_reg(hdmi_dev, VP_REMAP, m_YCC422_SIZE, v_YCC422_SIZE(remap_size)); } else { switch (vpara->color_output_depth) { case 10: color_depth = COLOR_DEPTH_30BIT; output_select = OUT_FROM_PIXEL_PACKING; break; case 12: color_depth = COLOR_DEPTH_36BIT; output_select = OUT_FROM_PIXEL_PACKING; break; case 16: color_depth = COLOR_DEPTH_48BIT; output_select = OUT_FROM_PIXEL_PACKING; break; case 8: default: color_depth = COLOR_DEPTH_24BIT_DEFAULT; output_select = OUT_FROM_8BIT_BYPASS; break; } } /*Config Color Depth*/ hdmi_msk_reg(hdmi_dev, VP_PR_CD, m_COLOR_DEPTH, v_COLOR_DEPTH(color_depth)); /*Config pixel repettion*/ hdmi_msk_reg(hdmi_dev, VP_PR_CD, m_DESIRED_PR_FACTOR, v_DESIRED_PR_FACTOR(hdmi_dev->pixelrepeat - 1)); if (hdmi_dev->pixelrepeat > 1) hdmi_msk_reg(hdmi_dev, VP_CONF, m_PIXEL_REPET_EN | m_BYPASS_SEL, v_PIXEL_REPET_EN(1) | v_BYPASS_SEL(0)); else hdmi_msk_reg(hdmi_dev, VP_CONF, m_PIXEL_REPET_EN | m_BYPASS_SEL, v_PIXEL_REPET_EN(0) | v_BYPASS_SEL(1)); /*config output select*/ if (output_select == OUT_FROM_PIXEL_PACKING) { /* pixel packing */ hdmi_msk_reg(hdmi_dev, VP_CONF, m_BYPASS_EN | m_PIXEL_PACK_EN | m_YCC422_EN | m_OUTPUT_SEL, v_BYPASS_EN(0) | v_PIXEL_PACK_EN(1) | v_YCC422_EN(0) | v_OUTPUT_SEL(output_select)); } else if (output_select == OUT_FROM_YCC422_REMAP) { /* YCC422 */ hdmi_msk_reg(hdmi_dev, VP_CONF, m_BYPASS_EN | m_PIXEL_PACK_EN | m_YCC422_EN | m_OUTPUT_SEL, v_BYPASS_EN(0) | v_PIXEL_PACK_EN(0) | v_YCC422_EN(1) | v_OUTPUT_SEL(output_select)); } else if (output_select == OUT_FROM_8BIT_BYPASS || output_select == 3) { /* bypass */ hdmi_msk_reg(hdmi_dev, VP_CONF, m_BYPASS_EN | m_PIXEL_PACK_EN | m_YCC422_EN | m_OUTPUT_SEL, v_BYPASS_EN(1) | v_PIXEL_PACK_EN(0) | v_YCC422_EN(0) | v_OUTPUT_SEL(output_select)); } #if defined(HDMI_VIDEO_STUFFING) /* YCC422 and pixel packing stuffing*/ hdmi_msk_reg(hdmi_dev, VP_STUFF, m_PR_STUFFING, v_PR_STUFFING(1)); hdmi_msk_reg(hdmi_dev, VP_STUFF, m_YCC422_STUFFING | m_PP_STUFFING, v_YCC422_STUFFING(1) | v_PP_STUFFING(1)); #endif return 0; } static int rockchip_hdmiv2_video_sampler(struct hdmi_dev *hdmi_dev, struct hdmi_video *vpara) { int map_code = 0; if (vpara->color_input == HDMI_COLOR_YCBCR422) { /* YCC422 mapping is discontinued - only map 1 is supported */ switch (vpara->color_output_depth) { case 8: map_code = VIDEO_YCBCR422_8BIT; break; case 10: map_code = VIDEO_YCBCR422_10BIT; break; case 12: map_code = VIDEO_YCBCR422_12BIT; break; default: map_code = VIDEO_YCBCR422_8BIT; break; } } else if (vpara->color_input == HDMI_COLOR_YCBCR420 || vpara->color_input == HDMI_COLOR_YCBCR444) { switch (vpara->color_output_depth) { case 10: map_code = VIDEO_YCBCR444_10BIT; break; case 12: map_code = VIDEO_YCBCR444_12BIT; break; case 16: map_code = VIDEO_YCBCR444_16BIT; break; case 8: default: map_code = VIDEO_YCBCR444_8BIT; break; } } else { switch (vpara->color_output_depth) { case 10: map_code = VIDEO_RGB444_10BIT; break; case 12: map_code = VIDEO_RGB444_12BIT; break; case 16: map_code = VIDEO_RGB444_16BIT; break; case 8: default: map_code = VIDEO_RGB444_8BIT; break; } } /* Set Data enable signal from external * and set video sample input mapping */ hdmi_msk_reg(hdmi_dev, TX_INVID0, m_INTERNAL_DE_GEN | m_VIDEO_MAPPING, v_INTERNAL_DE_GEN(0) | v_VIDEO_MAPPING(map_code)); #if defined(HDMI_VIDEO_STUFFING) hdmi_writel(hdmi_dev, TX_GYDATA0, 0x00); hdmi_writel(hdmi_dev, TX_GYDATA1, 0x00); hdmi_msk_reg(hdmi_dev, TX_INSTUFFING, m_GYDATA_STUFF, v_GYDATA_STUFF(1)); hdmi_writel(hdmi_dev, TX_RCRDATA0, 0x00); hdmi_writel(hdmi_dev, TX_RCRDATA1, 0x00); hdmi_msk_reg(hdmi_dev, TX_INSTUFFING, m_RCRDATA_STUFF, v_RCRDATA_STUFF(1)); hdmi_writel(hdmi_dev, TX_BCBDATA0, 0x00); hdmi_writel(hdmi_dev, TX_BCBDATA1, 0x00); hdmi_msk_reg(hdmi_dev, TX_INSTUFFING, m_BCBDATA_STUFF, v_BCBDATA_STUFF(1)); #endif return 0; } static const char coeff_csc[][24] = { /* G R B Bias * A1 | A2 | A3 | A4 | * B1 | B2 | B3 | B4 | * C1 | C2 | C3 | C4 | */ { /* CSC_BYPASS */ 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, }, { /* CSC_RGB_0_255_TO_RGB_16_235_8BIT */ 0x36, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, /*G*/ 0x00, 0x00, 0x36, 0xf7, 0x00, 0x00, 0x00, 0x40, /*R*/ 0x00, 0x00, 0x00, 0x00, 0x36, 0xf7, 0x00, 0x40, /*B*/ }, { /* CSC_RGB_0_255_TO_RGB_16_235_10BIT */ 0x36, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, /*G*/ 0x00, 0x00, 0x36, 0xf7, 0x00, 0x00, 0x01, 0x00, /*R*/ 0x00, 0x00, 0x00, 0x00, 0x36, 0xf7, 0x01, 0x00, /*B*/ }, { /* CSC_RGB_0_255_TO_ITU601_16_235_8BIT */ 0x20, 0x40, 0x10, 0x80, 0x06, 0x40, 0x00, 0x40, /*Y*/ 0xe8, 0x80, 0x1c, 0x00, 0xfb, 0x80, 0x02, 0x00, /*Cr*/ 0xed, 0x80, 0xf6, 0x80, 0x1c, 0x00, 0x02, 0x00, /*Cb*/ }, { /* CSC_RGB_0_255_TO_ITU601_16_235_10BIT */ 0x20, 0x40, 0x10, 0x80, 0x06, 0x40, 0x01, 0x00, /*Y*/ 0xe8, 0x80, 0x1c, 0x00, 0xfb, 0x80, 0x08, 0x00, /*Cr*/ 0xed, 0x80, 0xf6, 0x80, 0x1c, 0x00, 0x08, 0x00, /*Cb*/ }, { /* CSC_RGB_0_255_TO_ITU709_16_235_8BIT */ 0x27, 0x40, 0x0b, 0xc0, 0x04, 0x00, 0x00, 0x40, /*Y*/ 0xe6, 0x80, 0x1c, 0x00, 0xfd, 0x80, 0x02, 0x00, /*Cr*/ 0xea, 0x40, 0xf9, 0x80, 0x1c, 0x00, 0x02, 0x00, /*Cb*/ }, { /* CSC_RGB_0_255_TO_ITU709_16_235_10BIT */ 0x27, 0x40, 0x0b, 0xc0, 0x04, 0x00, 0x01, 0x00, /*Y*/ 0xe6, 0x80, 0x1c, 0x00, 0xfd, 0x80, 0x08, 0x00, /*Cr*/ 0xea, 0x40, 0xf9, 0x80, 0x1c, 0x00, 0x08, 0x00, /*Cb*/ }, /* Y Cr Cb Bias */ { /* CSC_ITU601_16_235_TO_RGB_0_255_8BIT */ 0x20, 0x00, 0x69, 0x26, 0x74, 0xfd, 0x01, 0x0e, /*G*/ 0x20, 0x00, 0x2c, 0xdd, 0x00, 0x00, 0x7e, 0x9a, /*R*/ 0x20, 0x00, 0x00, 0x00, 0x38, 0xb4, 0x7e, 0x3b, /*B*/ }, { /* CSC_ITU709_16_235_TO_RGB_0_255_8BIT */ 0x20, 0x00, 0x71, 0x06, 0x7a, 0x02, 0x00, 0xa7, /*G*/ 0x20, 0x00, 0x32, 0x64, 0x00, 0x00, 0x7e, 0x6d, /*R*/ 0x20, 0x00, 0x00, 0x00, 0x3b, 0x61, 0x7e, 0x25, /*B*/ }, }; static int rockchip_hdmiv2_video_csc(struct hdmi_dev *hdmi_dev, struct hdmi_video *vpara) { int i, mode, interpolation, decimation, csc_scale = 0; const char *coeff = NULL; unsigned char color_depth = 0; if (vpara->color_input == vpara->color_output) { hdmi_msk_reg(hdmi_dev, MC_FLOWCTRL, m_FEED_THROUGH_OFF, v_FEED_THROUGH_OFF(0)); return 0; } if (vpara->color_input == HDMI_COLOR_YCBCR422 && vpara->color_output != HDMI_COLOR_YCBCR422 && vpara->color_output != HDMI_COLOR_YCBCR420) { interpolation = 1; hdmi_msk_reg(hdmi_dev, CSC_CFG, m_CSC_INTPMODE, v_CSC_INTPMODE(interpolation)); } if ((vpara->color_input == HDMI_COLOR_RGB_0_255 || vpara->color_input == HDMI_COLOR_YCBCR444) && vpara->color_output == HDMI_COLOR_YCBCR422) { decimation = 1; hdmi_msk_reg(hdmi_dev, CSC_CFG, m_CSC_DECIMODE, v_CSC_DECIMODE(decimation)); } mode = CSC_BYPASS; csc_scale = 0; switch (vpara->vic) { case HDMI_720X480I_60HZ_4_3: case HDMI_720X576I_50HZ_4_3: case HDMI_720X480P_60HZ_4_3: case HDMI_720X576P_50HZ_4_3: case HDMI_720X480I_60HZ_16_9: case HDMI_720X576I_50HZ_16_9: case HDMI_720X480P_60HZ_16_9: case HDMI_720X576P_50HZ_16_9: if (vpara->color_input == HDMI_COLOR_RGB_0_255 && vpara->color_output >= HDMI_COLOR_YCBCR444) { mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT; csc_scale = 0; } else if (vpara->color_input >= HDMI_COLOR_YCBCR444 && vpara->color_output == HDMI_COLOR_RGB_0_255) { mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT; csc_scale = 1; } break; default: if (vpara->color_input == HDMI_COLOR_RGB_0_255 && vpara->color_output >= HDMI_COLOR_YCBCR444) { mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT; csc_scale = 0; } else if (vpara->color_input >= HDMI_COLOR_YCBCR444 && vpara->color_output == HDMI_COLOR_RGB_0_255) { mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT; csc_scale = 1; } break; } if ((vpara->color_input == HDMI_COLOR_RGB_0_255) && (vpara->color_output == HDMI_COLOR_RGB_16_235)) { mode = CSC_RGB_0_255_TO_RGB_16_235_8BIT; csc_scale = 0; } if (mode != CSC_BYPASS) { switch (vpara->color_output_depth) { case 10: color_depth = COLOR_DEPTH_30BIT; mode += 1; break; case 12: color_depth = COLOR_DEPTH_36BIT; mode += 2; break; case 16: color_depth = COLOR_DEPTH_48BIT; mode += 3; break; case 8: default: color_depth = COLOR_DEPTH_24BIT; break; } } coeff = coeff_csc[mode]; for (i = 0; i < 24; i++) hdmi_writel(hdmi_dev, CSC_COEF_A1_MSB + i, coeff[i]); hdmi_msk_reg(hdmi_dev, CSC_SCALE, m_CSC_SCALE, v_CSC_SCALE(csc_scale)); /*config CSC_COLOR_DEPTH*/ hdmi_msk_reg(hdmi_dev, CSC_SCALE, m_CSC_COLOR_DEPTH, v_CSC_COLOR_DEPTH(color_depth)); /* enable CSC */ if (mode == CSC_BYPASS) hdmi_msk_reg(hdmi_dev, MC_FLOWCTRL, m_FEED_THROUGH_OFF, v_FEED_THROUGH_OFF(0)); else hdmi_msk_reg(hdmi_dev, MC_FLOWCTRL, m_FEED_THROUGH_OFF, v_FEED_THROUGH_OFF(1)); return 0; } static int hdmi_dev_detect_hotplug(struct hdmi *hdmi) { struct hdmi_dev *hdmi_dev = hdmi->property->priv; u32 value; value = hdmi_readl(hdmi_dev, PHY_STAT0); HDMIDBG(2, "[%s] reg%x value %02x\n", __func__, PHY_STAT0, value); if (value & m_PHY_HPD) return HDMI_HPD_ACTIVATED; return HDMI_HPD_REMOVED; } static int hdmi_dev_read_edid(struct hdmi *hdmi, int block, unsigned char *buff) { struct hdmi_dev *hdmi_dev = hdmi->property->priv; int i = 0, n = 0, index = 0, ret = -1, trytime = 5; int offset = (block % 2) * 0x80; int interrupt = 0; HDMIDBG(2, "[%s] block %d\n", __func__, block); rockchip_hdmiv2_i2cm_reset(hdmi_dev); /* Set DDC I2C CLK which divided from DDC_CLK to 100KHz. */ rockchip_hdmiv2_i2cm_clk_init(hdmi_dev); /* Enable I2C interrupt for reading edid */ rockchip_hdmiv2_i2cm_mask_int(hdmi_dev, 0); hdmi_writel(hdmi_dev, I2CM_SLAVE, DDC_I2C_EDID_ADDR); hdmi_writel(hdmi_dev, I2CM_SEGADDR, DDC_I2C_SEG_ADDR); hdmi_writel(hdmi_dev, I2CM_SEGPTR, block / 2); for (n = 0; n < HDMI_EDID_BLOCK_SIZE / 8; n++) { for (trytime = 0; trytime < 5; trytime++) { hdmi_writel(hdmi_dev, I2CM_ADDRESS, offset + 8 * n); /* enable extend sequential read operation */ if (block == 0) hdmi_msk_reg(hdmi_dev, I2CM_OPERATION, m_I2CM_RD8, v_I2CM_RD8(1)); else hdmi_msk_reg(hdmi_dev, I2CM_OPERATION, m_I2CM_RD8_EXT, v_I2CM_RD8_EXT(1)); i = 20; while (i--) { usleep_range(900, 1000); interrupt = hdmi_readl(hdmi_dev, IH_I2CM_STAT0); if (interrupt) hdmi_writel(hdmi_dev, IH_I2CM_STAT0, interrupt); if (interrupt & (m_SCDC_READREQ | m_I2CM_DONE | m_I2CM_ERROR)) break; } if (interrupt & m_I2CM_DONE) { for (index = 0; index < 8; index++) buff[8 * n + index] = hdmi_readl(hdmi_dev, I2CM_READ_BUFF0 + index); if (n == HDMI_EDID_BLOCK_SIZE / 8 - 1) { ret = 0; goto exit; } break; } else if ((interrupt & m_I2CM_ERROR) || (i == -1)) { dev_err(hdmi->dev, "[%s] edid read %d error\n", __func__, offset + 8 * n); } } if (trytime == 5) { dev_err(hdmi->dev, "[%s] edid read error\n", __func__); break; } } exit: /* Disable I2C interrupt */ rockchip_hdmiv2_i2cm_mask_int(hdmi_dev, 1); return ret; } static void hdmi_dev_config_avi(struct hdmi *hdmi, struct hdmi_video *vpara) { struct hdmi_dev *hdmi_dev = hdmi->property->priv; unsigned char colorimetry, ext_colorimetry = 0, aspect_ratio, y1y0; unsigned char rgb_quan_range = AVI_QUANTIZATION_RANGE_DEFAULT; hdmi_msk_reg(hdmi_dev, FC_DATAUTO3, m_AVI_AUTO, v_AVI_AUTO(0)); hdmi_msk_reg(hdmi_dev, IH_FC_STAT1, m_AVI_INFOFRAME, v_AVI_INFOFRAME(1)); /* Set AVI infoFrame Data byte1 */ if (vpara->color_output == HDMI_COLOR_YCBCR444) y1y0 = AVI_COLOR_MODE_YCBCR444; else if (vpara->color_output == HDMI_COLOR_YCBCR422) y1y0 = AVI_COLOR_MODE_YCBCR422; else if (vpara->color_output == HDMI_COLOR_YCBCR420) y1y0 = AVI_COLOR_MODE_YCBCR420; else y1y0 = AVI_COLOR_MODE_RGB; hdmi_msk_reg(hdmi_dev, FC_AVICONF0, m_FC_ACTIV_FORMAT | m_FC_RGC_YCC, v_FC_RGC_YCC(y1y0) | v_FC_ACTIV_FORMAT(1)); /* Set AVI infoFrame Data byte2 */ switch (vpara->vic) { case HDMI_720X480I_60HZ_4_3: case HDMI_720X576I_50HZ_4_3: case HDMI_720X480P_60HZ_4_3: case HDMI_720X576P_50HZ_4_3: aspect_ratio = AVI_CODED_FRAME_ASPECT_4_3; if (vpara->colorimetry == HDMI_COLORIMETRY_NO_DATA) colorimetry = AVI_COLORIMETRY_SMPTE_170M; break; case HDMI_720X480I_60HZ_16_9: case HDMI_720X576I_50HZ_16_9: case HDMI_720X480P_60HZ_16_9: case HDMI_720X576P_50HZ_16_9: aspect_ratio = AVI_CODED_FRAME_ASPECT_16_9; if (vpara->colorimetry == HDMI_COLORIMETRY_NO_DATA) colorimetry = AVI_COLORIMETRY_SMPTE_170M; break; default: aspect_ratio = AVI_CODED_FRAME_ASPECT_16_9; if (vpara->colorimetry == HDMI_COLORIMETRY_NO_DATA) colorimetry = AVI_COLORIMETRY_ITU709; } if (vpara->colorimetry > HDMI_COLORIMETRY_ITU709) { colorimetry = AVI_COLORIMETRY_EXTENDED; ext_colorimetry = vpara->colorimetry - HDMI_COLORIMETRY_EXTEND_XVYCC_601; } else if (vpara->color_output == HDMI_COLOR_RGB_16_235 || vpara->color_output == HDMI_COLOR_RGB_0_255) { colorimetry = AVI_COLORIMETRY_NO_DATA; } else if (vpara->colorimetry != HDMI_COLORIMETRY_NO_DATA) { colorimetry = vpara->colorimetry; } hdmi_writel(hdmi_dev, FC_AVICONF1, v_FC_COLORIMETRY(colorimetry) | v_FC_PIC_ASPEC_RATIO(aspect_ratio) | v_FC_ACT_ASPEC_RATIO(ACTIVE_ASPECT_RATE_DEFAULT)); /* Set AVI infoFrame Data byte3 */ hdmi_msk_reg(hdmi_dev, FC_AVICONF2, m_FC_EXT_COLORIMETRY | m_FC_QUAN_RANGE, v_FC_EXT_COLORIMETRY(ext_colorimetry) | v_FC_QUAN_RANGE(rgb_quan_range)); /* Set AVI infoFrame Data byte4 */ if ((vpara->vic > 92 && vpara->vic < 96) || (vpara->vic == 98) || (vpara->vic & HDMI_VIDEO_DMT) || (vpara->vic & HDMI_VIDEO_DISCRETE_VR)) hdmi_writel(hdmi_dev, FC_AVIVID, 0); else hdmi_writel(hdmi_dev, FC_AVIVID, vpara->vic & 0xff); /* Set AVI infoFrame Data byte5 */ hdmi_msk_reg(hdmi_dev, FC_AVICONF3, m_FC_YQ | m_FC_CN, v_FC_YQ(YQ_LIMITED_RANGE) | v_FC_CN(CN_GRAPHICS)); hdmi_msk_reg(hdmi_dev, FC_DATAUTO3, m_AVI_AUTO, v_AVI_AUTO(1)); } static int hdmi_dev_config_vsi(struct hdmi *hdmi, unsigned char vic_3d, unsigned char format) { int i = 0, id = 0x000c03; unsigned char data[3] = {0}; struct hdmi_dev *hdmi_dev = hdmi->property->priv; HDMIDBG(2, "[%s] vic %d format %d.\n", __func__, vic_3d, format); hdmi_msk_reg(hdmi_dev, FC_DATAUTO0, m_VSD_AUTO, v_VSD_AUTO(0)); hdmi_writel(hdmi_dev, FC_VSDIEEEID2, id & 0xff); hdmi_writel(hdmi_dev, FC_VSDIEEEID1, (id >> 8) & 0xff); hdmi_writel(hdmi_dev, FC_VSDIEEEID0, (id >> 16) & 0xff); data[0] = format << 5; /* PB4 --HDMI_Video_Format */ switch (format) { case HDMI_VIDEO_FORMAT_4KX2K: data[1] = vic_3d; /* PB5--HDMI_VIC */ data[2] = 0; break; case HDMI_VIDEO_FORMAT_3D: data[1] = vic_3d << 4; /* PB5--3D_Structure field */ data[2] = 0; /* PB6--3D_Ext_Data field */ break; default: data[1] = 0; data[2] = 0; break; } for (i = 0; i < 3; i++) hdmi_writel(hdmi_dev, FC_VSDPAYLOAD0 + i, data[i]); hdmi_writel(hdmi_dev, FC_VSDSIZE, 0x6); hdmi_writel(hdmi_dev, FC_DATAUTO1, 0); hdmi_writel(hdmi_dev, FC_DATAUTO2, 0x11); hdmi_msk_reg(hdmi_dev, FC_DATAUTO0, m_VSD_AUTO, v_VSD_AUTO(1)); return 0; } #define HDR_LSB(n) ((n) & 0xff) #define HDR_MSB(n) (((n) & 0xff00) >> 8) static void hdmi_dev_config_hdr(struct hdmi *hdmi, int eotf, struct hdmi_hdr_metadata *hdr) { struct hdmi_dev *hdmi_dev = hdmi->property->priv; /* hdr is supportted after disignid = 0x21 */ if (!hdmi_dev || hdmi_readl(hdmi_dev, DESIGN_ID) < 0x21) return; hdmi_writel(hdmi_dev, FC_DRM_HB, 1);/*verion = 0x1*/ hdmi_writel(hdmi_dev, (FC_DRM_HB + 1), 26);/*length of following data*/ hdmi_writel(hdmi_dev, FC_DRM_PB, eotf / 2); hdmi_writel(hdmi_dev, FC_DRM_PB + 1, 0); if (hdr) { hdmi_writel(hdmi_dev, FC_DRM_PB + 2, HDR_LSB(hdr->prim_x0)); hdmi_writel(hdmi_dev, FC_DRM_PB + 3, HDR_MSB(hdr->prim_x0)); hdmi_writel(hdmi_dev, FC_DRM_PB + 4, HDR_LSB(hdr->prim_y0)); hdmi_writel(hdmi_dev, FC_DRM_PB + 5, HDR_MSB(hdr->prim_y0)); hdmi_writel(hdmi_dev, FC_DRM_PB + 6, HDR_LSB(hdr->prim_x1)); hdmi_writel(hdmi_dev, FC_DRM_PB + 7, HDR_MSB(hdr->prim_x1)); hdmi_writel(hdmi_dev, FC_DRM_PB + 8, HDR_LSB(hdr->prim_y1)); hdmi_writel(hdmi_dev, FC_DRM_PB + 9, HDR_MSB(hdr->prim_y1)); hdmi_writel(hdmi_dev, FC_DRM_PB + 10, HDR_LSB(hdr->prim_x2)); hdmi_writel(hdmi_dev, FC_DRM_PB + 11, HDR_MSB(hdr->prim_x2)); hdmi_writel(hdmi_dev, FC_DRM_PB + 12, HDR_LSB(hdr->prim_y2)); hdmi_writel(hdmi_dev, FC_DRM_PB + 13, HDR_MSB(hdr->prim_y2)); hdmi_writel(hdmi_dev, FC_DRM_PB + 14, HDR_LSB(hdr->white_px)); hdmi_writel(hdmi_dev, FC_DRM_PB + 15, HDR_MSB(hdr->white_px)); hdmi_writel(hdmi_dev, FC_DRM_PB + 16, HDR_LSB(hdr->white_py)); hdmi_writel(hdmi_dev, FC_DRM_PB + 17, HDR_MSB(hdr->white_py)); hdmi_writel(hdmi_dev, FC_DRM_PB + 18, HDR_LSB(hdr->max_dml)); hdmi_writel(hdmi_dev, FC_DRM_PB + 19, HDR_MSB(hdr->max_dml)); hdmi_writel(hdmi_dev, FC_DRM_PB + 20, HDR_LSB(hdr->min_dml)); hdmi_writel(hdmi_dev, FC_DRM_PB + 21, HDR_MSB(hdr->min_dml)); hdmi_writel(hdmi_dev, FC_DRM_PB + 22, HDR_LSB(hdr->max_cll)); hdmi_writel(hdmi_dev, FC_DRM_PB + 23, HDR_MSB(hdr->max_cll)); hdmi_writel(hdmi_dev, FC_DRM_PB + 24, HDR_LSB(hdr->max_fall)); hdmi_writel(hdmi_dev, FC_DRM_PB + 25, HDR_MSB(hdr->max_fall)); } else { hdmi_writel(hdmi_dev, FC_DRM_PB + 1, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 2, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 3, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 4, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 5, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 6, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 7, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 8, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 9, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 10, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 11, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 12, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 13, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 14, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 15, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 16, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 17, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 18, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 19, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 20, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 21, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 22, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 23, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 24, 0); hdmi_writel(hdmi_dev, FC_DRM_PB + 25, 0); } if (eotf) { hdmi_msk_reg(hdmi_dev, FC_PACK_TXE, m_DRM_TXEN, v_DRM_TXEN(1)); hdmi_msk_reg(hdmi_dev, FC_MASK2, m_DRM_MASK, v_DRM_MASK(0)); hdmi_msk_reg(hdmi_dev, FC_DRM_UP, m_DRM_PUPD, v_DRM_PUPD(1)); } else { hdmi_msk_reg(hdmi_dev, FC_PACK_TXE, m_DRM_TXEN, v_DRM_TXEN(0)); hdmi_msk_reg(hdmi_dev, FC_MASK2, m_DRM_MASK, v_DRM_MASK(1)); hdmi_msk_reg(hdmi_dev, FC_DRM_UP, m_DRM_PUPD, v_DRM_PUPD(1)); } } static int hdmi_dev_config_spd(struct hdmi *hdmi, const char *vendor, const char *product, char deviceinfo) { struct hdmi_dev *hdmi_dev; int i, len; if (!hdmi || !vendor || !product) return -1; hdmi_dev = hdmi->property->priv; hdmi_msk_reg(hdmi_dev, FC_DATAUTO0, m_SPD_AUTO, v_SPD_AUTO(0)); len = strlen(vendor); for (i = 0; i < 8; i++) { if (i < len) hdmi_writel(hdmi_dev, FC_SPDVENDORNAME0 + i, vendor[i]); else hdmi_writel(hdmi_dev, FC_SPDVENDORNAME0 + i, 0); } len = strlen(product); for (i = 0; i < 16; i++) { if (i < len) hdmi_writel(hdmi_dev, FC_SPDPRODUCTNAME0 + i, product[i]); else hdmi_writel(hdmi_dev, FC_SPDPRODUCTNAME0 + i, 0); } hdmi_writel(hdmi_dev, FC_SPDDEVICEINF, deviceinfo); hdmi_msk_reg(hdmi_dev, FC_DATAUTO0, m_SPD_AUTO, v_SPD_AUTO(1)); return 0; } static int hdmi_dev_config_video(struct hdmi *hdmi, struct hdmi_video *vpara) { struct hdmi_dev *hdmi_dev = hdmi->property->priv; HDMIDBG(2, "%s vic %d 3dformat %d color mode %d color depth %d\n", __func__, vpara->vic, vpara->format_3d, vpara->color_output, vpara->color_output_depth); if (hdmi_dev->soctype == HDMI_SOC_RK3288) vpara->color_input = HDMI_COLOR_RGB_0_255; if (!hdmi->uboot) { /* before configure video, we power off phy */ if (hdmi_dev->soctype != HDMI_SOC_RK322X) { hdmi_msk_reg(hdmi_dev, PHY_CONF0, m_PDDQ_SIG | m_TXPWRON_SIG, v_PDDQ_SIG(1) | v_TXPWRON_SIG(0)); } else { hdmi_msk_reg(hdmi_dev, PHY_CONF0, m_ENHPD_RXSENSE_SIG, v_ENHPD_RXSENSE_SIG(1)); regmap_write(hdmi_dev->grf_base, RK322X_GRF_SOC_CON2, RK322X_PLL_POWER_DOWN); } /* force output blue */ if (vpara->color_output == HDMI_COLOR_RGB_0_255) { hdmi_writel(hdmi_dev, FC_DBGTMDS2, 0x00); /*R*/ hdmi_writel(hdmi_dev, FC_DBGTMDS1, 0x00); /*G*/ hdmi_writel(hdmi_dev, FC_DBGTMDS0, 0x00); /*B*/ } else if (vpara->color_output == HDMI_COLOR_RGB_16_235) { hdmi_writel(hdmi_dev, FC_DBGTMDS2, 0x10); /*R*/ hdmi_writel(hdmi_dev, FC_DBGTMDS1, 0x10); /*G*/ hdmi_writel(hdmi_dev, FC_DBGTMDS0, 0x10); /*B*/ } else { hdmi_writel(hdmi_dev, FC_DBGTMDS2, 0x80); /*Cr*/ hdmi_writel(hdmi_dev, FC_DBGTMDS1, 0x10); /*Y*/ hdmi_writel(hdmi_dev, FC_DBGTMDS0, 0x80); /*Cb*/ } hdmi_msk_reg(hdmi_dev, FC_DBGFORCE, m_FC_FORCEVIDEO, v_FC_FORCEVIDEO(1)); hdmi_writel(hdmi_dev, MC_CLKDIS, m_HDCPCLK_DISABLE); } if (rockchip_hdmiv2_video_framecomposer(hdmi, vpara) < 0) return -1; if (rockchip_hdmiv2_video_packetizer(hdmi_dev, vpara) < 0) return -1; /* Color space convert */ if (rockchip_hdmiv2_video_csc(hdmi_dev, vpara) < 0) return -1; if (rockchip_hdmiv2_video_sampler(hdmi_dev, vpara) < 0) return -1; if (vpara->sink_hdmi == OUTPUT_HDMI) { hdmi_dev_config_avi(hdmi, vpara); hdmi_dev_config_spd(hdmi, hdmi_dev->vendor_name, hdmi_dev->product_name, hdmi_dev->deviceinfo); if (vpara->format_3d != HDMI_3D_NONE) { hdmi_dev_config_vsi(hdmi, vpara->format_3d, HDMI_VIDEO_FORMAT_3D); } else if ((vpara->vic > 92 && vpara->vic < 96) || (vpara->vic == 98)) { vpara->vic = (vpara->vic == 98) ? 4 : (96 - vpara->vic); hdmi_dev_config_vsi(hdmi, vpara->vic, HDMI_VIDEO_FORMAT_4KX2K); } else { hdmi_dev_config_vsi(hdmi, vpara->vic, HDMI_VIDEO_FORMAT_NORMAL); } hdmi_dev_config_hdr(hdmi, vpara->eotf, &hdmi->hdr); dev_info(hdmi->dev, "[%s] success output HDMI.\n", __func__); } else { dev_info(hdmi->dev, "[%s] success output DVI.\n", __func__); } if (!hdmi->uboot) rockchip_hdmiv2_config_phy(hdmi_dev); else hdmi_msk_reg(hdmi_dev, PHY_MASK, m_PHY_LOCK, v_PHY_LOCK(0)); return 0; } static void hdmi_dev_config_aai(struct hdmi_dev *hdmi_dev, struct hdmi_audio *audio) { /* Refer to CEA861-E Audio infoFrame * Set both Audio Channel Count and Audio Coding * Type Refer to Stream Head for HDMI */ hdmi_msk_reg(hdmi_dev, FC_AUDICONF0, m_FC_CHN_CNT | m_FC_CODING_TYPE, v_FC_CHN_CNT(audio->channel - 1) | v_FC_CODING_TYPE(0)); /* Set both Audio Sample Size and Sample Frequency * Refer to Stream Head for HDMI */ hdmi_msk_reg(hdmi_dev, FC_AUDICONF1, m_FC_SAMPLE_SIZE | m_FC_SAMPLE_FREQ, v_FC_SAMPLE_SIZE(0) | v_FC_SAMPLE_FREQ(0)); /* Set Channel Allocation */ hdmi_writel(hdmi_dev, FC_AUDICONF2, 0x00); /* Set LFEPBLDOWN-MIX INH and LSV */ hdmi_writel(hdmi_dev, FC_AUDICONF3, 0x00); } static int hdmi_dev_config_audio(struct hdmi *hdmi, struct hdmi_audio *audio) { struct hdmi_dev *hdmi_dev = hdmi->property->priv; int word_length = 0, channel = 0, mclk_fs; unsigned int N = 0, CTS = 0; int rate = 0; char design_id; HDMIDBG(2, "%s\n", __func__); if (audio->channel < 3) channel = I2S_CHANNEL_1_2; else if (audio->channel < 5) channel = I2S_CHANNEL_3_4; else if (audio->channel < 7) channel = I2S_CHANNEL_5_6; else channel = I2S_CHANNEL_7_8; switch (audio->rate) { case HDMI_AUDIO_FS_32000: mclk_fs = FS_128; rate = AUDIO_32K; if (hdmi_dev->tmdsclk >= 594000000) N = N_32K_HIGHCLK; else if (hdmi_dev->tmdsclk >= 297000000) N = N_32K_MIDCLK; else N = N_32K_LOWCLK; /*div a num to avoid the value is exceed 2^32(int)*/ CTS = CALC_CTS(N, hdmi_dev->tmdsclk / 1000, 32); break; case HDMI_AUDIO_FS_44100: mclk_fs = FS_128; rate = AUDIO_441K; if (hdmi_dev->tmdsclk >= 594000000) N = N_441K_HIGHCLK; else if (hdmi_dev->tmdsclk >= 297000000) N = N_441K_MIDCLK; else N = N_441K_LOWCLK; CTS = CALC_CTS(N, hdmi_dev->tmdsclk / 100, 441); break; case HDMI_AUDIO_FS_48000: mclk_fs = FS_128; rate = AUDIO_48K; if (hdmi_dev->tmdsclk >= 594000000) /*FS_153.6*/ N = N_48K_HIGHCLK; else if (hdmi_dev->tmdsclk >= 297000000) N = N_48K_MIDCLK; else N = N_48K_LOWCLK; CTS = CALC_CTS(N, hdmi_dev->tmdsclk / 1000, 48); break; case HDMI_AUDIO_FS_88200: mclk_fs = FS_128; rate = AUDIO_882K; if (hdmi_dev->tmdsclk >= 594000000) N = N_882K_HIGHCLK; else if (hdmi_dev->tmdsclk >= 297000000) N = N_882K_MIDCLK; else N = N_882K_LOWCLK; CTS = CALC_CTS(N, hdmi_dev->tmdsclk / 100, 882); break; case HDMI_AUDIO_FS_96000: mclk_fs = FS_128; rate = AUDIO_96K; if (hdmi_dev->tmdsclk >= 594000000) /*FS_153.6*/ N = N_96K_HIGHCLK; else if (hdmi_dev->tmdsclk >= 297000000) N = N_96K_MIDCLK; else N = N_96K_LOWCLK; CTS = CALC_CTS(N, hdmi_dev->tmdsclk / 1000, 96); break; case HDMI_AUDIO_FS_176400: mclk_fs = FS_128; rate = AUDIO_1764K; if (hdmi_dev->tmdsclk >= 594000000) N = N_1764K_HIGHCLK; else if (hdmi_dev->tmdsclk >= 297000000) N = N_1764K_MIDCLK; else N = N_1764K_LOWCLK; CTS = CALC_CTS(N, hdmi_dev->tmdsclk / 100, 1764); break; case HDMI_AUDIO_FS_192000: mclk_fs = FS_128; rate = AUDIO_192K; if (hdmi_dev->tmdsclk >= 594000000) /*FS_153.6*/ N = N_192K_HIGHCLK; else if (hdmi_dev->tmdsclk >= 297000000) N = N_192K_MIDCLK; else N = N_192K_LOWCLK; CTS = CALC_CTS(N, hdmi_dev->tmdsclk / 1000, 192); break; default: dev_err(hdmi_dev->hdmi->dev, "[%s] not support such sample rate %d\n", __func__, audio->rate); return -ENOENT; } switch (audio->word_length) { case HDMI_AUDIO_WORD_LENGTH_16bit: word_length = I2S_16BIT_SAMPLE; break; case HDMI_AUDIO_WORD_LENGTH_20bit: word_length = I2S_20BIT_SAMPLE; break; case HDMI_AUDIO_WORD_LENGTH_24bit: word_length = I2S_24BIT_SAMPLE; break; default: word_length = I2S_16BIT_SAMPLE; } HDMIDBG(2, "rate = %d, tmdsclk = %u, N = %d, CTS = %d\n", audio->rate, hdmi_dev->tmdsclk, N, CTS); /* more than 2 channels => layout 1 else layout 0 */ hdmi_msk_reg(hdmi_dev, FC_AUDSCONF, m_AUD_PACK_LAYOUT, v_AUD_PACK_LAYOUT((audio->channel > 2) ? 1 : 0)); if (hdmi_dev->audiosrc == HDMI_AUDIO_SRC_SPDIF) { mclk_fs = FS_128; hdmi_msk_reg(hdmi_dev, AUD_CONF0, m_I2S_SEL, v_I2S_SEL(AUDIO_SPDIF_GPA)); hdmi_msk_reg(hdmi_dev, AUD_SPDIF1, m_SET_NLPCM | m_SPDIF_WIDTH, v_SET_NLPCM(PCM_LINEAR) | v_SPDIF_WIDTH(word_length)); /*Mask fifo empty and full int and reset fifo*/ hdmi_msk_reg(hdmi_dev, AUD_SPDIFINT, m_FIFO_EMPTY_MASK | m_FIFO_FULL_MASK, v_FIFO_EMPTY_MASK(1) | v_FIFO_FULL_MASK(1)); hdmi_msk_reg(hdmi_dev, AUD_SPDIF0, m_SW_SAUD_FIFO_RST, v_SW_SAUD_FIFO_RST(1)); } else { /*Mask fifo empty and full int and reset fifo*/ hdmi_msk_reg(hdmi_dev, AUD_INT, m_FIFO_EMPTY_MASK | m_FIFO_FULL_MASK, v_FIFO_EMPTY_MASK(1) | v_FIFO_FULL_MASK(1)); hdmi_msk_reg(hdmi_dev, AUD_CONF0, m_SW_AUD_FIFO_RST, v_SW_AUD_FIFO_RST(1)); hdmi_writel(hdmi_dev, MC_SWRSTZREQ, 0xF7); design_id = hdmi_readl(hdmi_dev, DESIGN_ID); if (design_id >= 0x21) hdmi_writel(hdmi_dev, AUD_CONF2, 0x4); else hdmi_writel(hdmi_dev, AUD_CONF2, 0x0); usleep_range(90, 100); /* * when we try to use hdmi nlpcm mode * we should use set AUD_CONF2 to open this route and set * word_length to 24bit for b.p.c.u.v with 16bit raw data * when the bitstream data up to 8 channel, we should use * the hdmi hbr mode * HBR Mode : Dolby TrueHD * Dolby Atmos * DTS-HDMA * NLPCM Mode : * FS_32000 FS_44100 FS_48000 : Dolby Digital & DTS * FS_176400 FS_192000 : Dolby Digital Plus */ if (audio->type == HDMI_AUDIO_NLPCM) { if (channel == I2S_CHANNEL_7_8) { HDMIDBG(2, "hbr mode.\n"); hdmi_writel(hdmi_dev, AUD_CONF2, 0x1); word_length = I2S_24BIT_SAMPLE; } else if ((audio->rate == HDMI_AUDIO_FS_32000) || (audio->rate == HDMI_AUDIO_FS_44100) || (audio->rate == HDMI_AUDIO_FS_48000) || (audio->rate == HDMI_AUDIO_FS_176400) || (audio->rate == HDMI_AUDIO_FS_192000)) { HDMIDBG(2, "nlpcm mode.\n"); hdmi_writel(hdmi_dev, AUD_CONF2, 0x2); word_length = I2S_24BIT_SAMPLE; } else { hdmi_writel(hdmi_dev, AUD_CONF2, 0x0); } } else { if (design_id >= 0x21) hdmi_writel(hdmi_dev, AUD_CONF2, 0x4); else hdmi_writel(hdmi_dev, AUD_CONF2, 0x0); } hdmi_msk_reg(hdmi_dev, AUD_CONF0, m_I2S_SEL | m_I2S_IN_EN, v_I2S_SEL(AUDIO_I2S) | v_I2S_IN_EN(channel)); hdmi_writel(hdmi_dev, AUD_CONF1, v_I2S_MODE(I2S_STANDARD_MODE) | v_I2S_WIDTH(word_length)); } hdmi_msk_reg(hdmi_dev, AUD_INPUTCLKFS, m_LFS_FACTOR, v_LFS_FACTOR(mclk_fs)); /*Set N value*/ hdmi_msk_reg(hdmi_dev, AUD_N3, m_NCTS_ATOMIC_WR, v_NCTS_ATOMIC_WR(1)); /*Set CTS by manual*/ hdmi_msk_reg(hdmi_dev, AUD_CTS3, m_N_SHIFT | m_CTS_MANUAL | m_AUD_CTS3, v_N_SHIFT(N_SHIFT_1) | v_CTS_MANUAL(1) | v_AUD_CTS3(CTS >> 16)); hdmi_writel(hdmi_dev, AUD_CTS2, (CTS >> 8) & 0xff); hdmi_writel(hdmi_dev, AUD_CTS1, CTS & 0xff); hdmi_msk_reg(hdmi_dev, AUD_N3, m_AUD_N3, v_AUD_N3(N >> 16)); hdmi_writel(hdmi_dev, AUD_N2, (N >> 8) & 0xff); hdmi_writel(hdmi_dev, AUD_N1, N & 0xff); /* set channel status register */ hdmi_msk_reg(hdmi_dev, FC_AUDSCHNLS7, m_AUDIO_SAMPLE_RATE, v_AUDIO_SAMPLE_RATE(rate)); hdmi_writel(hdmi_dev, FC_AUDSCHNLS8, ((~rate) << 4) | 0x2); hdmi_msk_reg(hdmi_dev, AUD_CONF0, m_SW_AUD_FIFO_RST, v_SW_AUD_FIFO_RST(1)); hdmi_dev_config_aai(hdmi_dev, audio); return 0; } static int hdmi_dev_control_output(struct hdmi *hdmi, int enable) { struct hdmi_dev *hdmi_dev = hdmi->property->priv; struct hdmi_video vpara; HDMIDBG(2, "[%s] %d\n", __func__, enable); if (enable == HDMI_AV_UNMUTE) { hdmi_writel(hdmi_dev, FC_DBGFORCE, 0x00); if (hdmi->edid.sink_hdmi == OUTPUT_HDMI) hdmi_msk_reg(hdmi_dev, FC_GCP, m_FC_SET_AVMUTE | m_FC_CLR_AVMUTE, v_FC_SET_AVMUTE(0) | v_FC_CLR_AVMUTE(1)); } else { if (enable & HDMI_VIDEO_MUTE) { hdmi_msk_reg(hdmi_dev, FC_DBGFORCE, m_FC_FORCEVIDEO, v_FC_FORCEVIDEO(1)); if (hdmi->edid.sink_hdmi == OUTPUT_HDMI) { hdmi_msk_reg(hdmi_dev, FC_GCP, m_FC_SET_AVMUTE | m_FC_CLR_AVMUTE, v_FC_SET_AVMUTE(1) | v_FC_CLR_AVMUTE(0)); vpara.vic = hdmi->vic; vpara.color_output = HDMI_COLOR_RGB_0_255; hdmi_dev_config_avi(hdmi, &vpara); while ((!hdmi_readl(hdmi_dev, IH_FC_STAT1)) & m_AVI_INFOFRAME) { usleep_range(900, 1000); } } } /* if (enable & HDMI_AUDIO_MUTE) { * hdmi_msk_reg(hdmi_dev, FC_AUDSCONF, * m_AUD_PACK_SAMPFIT, * v_AUD_PACK_SAMPFIT(0x0F)); * } */ if (enable == (HDMI_VIDEO_MUTE | HDMI_AUDIO_MUTE)) { if (hdmi->ops->hdcp_power_off_cb) hdmi->ops->hdcp_power_off_cb(hdmi); rockchip_hdmiv2_powerdown(hdmi_dev); } } return 0; } static int hdmi_dev_insert(struct hdmi *hdmi) { struct hdmi_dev *hdmi_dev = hdmi->property->priv; HDMIDBG(2, "%s\n", __func__); if (!hdmi->uboot) hdmi_writel(hdmi_dev, MC_CLKDIS, m_HDCPCLK_DISABLE); return HDMI_ERROR_SUCCESS; } static int hdmi_dev_remove(struct hdmi *hdmi) { struct hdmi_dev *hdmi_dev = hdmi->property->priv; HDMIDBG(2, "%s\n", __func__); if (hdmi->ops->hdcp_power_off_cb) hdmi->ops->hdcp_power_off_cb(hdmi); rockchip_hdmiv2_powerdown(hdmi_dev); hdmi_dev->tmdsclk = 0; return HDMI_ERROR_SUCCESS; } static int hdmi_dev_enable(struct hdmi *hdmi) { struct hdmi_dev *hdmi_dev = hdmi->property->priv; HDMIDBG(2, "%s\n", __func__); if (!hdmi_dev->enable) { hdmi_writel(hdmi_dev, IH_MUTE, 0x00); hdmi_dev->enable = 1; } hdmi_submit_work(hdmi, HDMI_HPD_CHANGE, 10, 0); return 0; } static int hdmi_dev_disable(struct hdmi *hdmi) { struct hdmi_dev *hdmi_dev = hdmi->property->priv; HDMIDBG(2, "%s\n", __func__); if (hdmi_dev->enable) { hdmi_dev->enable = 0; hdmi_writel(hdmi_dev, IH_MUTE, 0x1); } return 0; } void rockchip_hdmiv2_dev_init_ops(struct hdmi_ops *ops) { if (ops) { ops->enable = hdmi_dev_enable; ops->disable = hdmi_dev_disable; ops->getstatus = hdmi_dev_detect_hotplug; ops->insert = hdmi_dev_insert; ops->remove = hdmi_dev_remove; ops->getedid = hdmi_dev_read_edid; ops->setvideo = hdmi_dev_config_video; ops->setaudio = hdmi_dev_config_audio; ops->setmute = hdmi_dev_control_output; ops->setavi = hdmi_dev_config_avi; ops->setvsi = hdmi_dev_config_vsi; ops->sethdr = hdmi_dev_config_hdr; } } void rockchip_hdmiv2_dev_initial(struct hdmi_dev *hdmi_dev) { struct hdmi *hdmi = hdmi_dev->hdmi; /*lcdc source select*/ if (hdmi_dev->soctype == HDMI_SOC_RK3288) { regmap_write(hdmi_dev->grf_base, RK3288_GRF_SOC_CON6, HDMI_SEL_LCDC(hdmi->property->videosrc, 4)); /* select GPIO7_C0 as cec pin */ regmap_write(hdmi_dev->grf_base, RK3288_GRF_SOC_CON8, BIT(12) | BIT(28)); } else if (hdmi_dev->soctype == HDMI_SOC_RK3399) { regmap_write(hdmi_dev->grf_base, RK3399_GRF_SOC_CON20, HDMI_SEL_LCDC(hdmi->property->videosrc, 6)); } if (!hdmi->uboot) { pr_info("reset hdmi\n"); if (hdmi_dev->soctype == HDMI_SOC_RK322X) { regmap_write(hdmi_dev->grf_base, RK322X_GRF_SOC_CON2, RK322X_DDC_MASK_EN); regmap_write(hdmi_dev->grf_base, RK322X_GRF_SOC_CON6, RK322X_IO_3V_DOMAIN); } reset_control_assert(hdmi_dev->reset); usleep_range(10, 20); reset_control_deassert(hdmi_dev->reset); rockchip_hdmiv2_powerdown(hdmi_dev); } else { hdmi->hotplug = hdmi_dev_detect_hotplug(hdmi); if (hdmi->hotplug != HDMI_HPD_ACTIVATED) hdmi->uboot = 0; } /*mute unnecessary interrupt, only enable hpd*/ hdmi_writel(hdmi_dev, IH_MUTE_FC_STAT0, 0xff); hdmi_writel(hdmi_dev, IH_MUTE_FC_STAT1, 0xff); hdmi_writel(hdmi_dev, IH_MUTE_FC_STAT2, 0xff); hdmi_writel(hdmi_dev, IH_MUTE_AS_STAT0, 0xff); hdmi_writel(hdmi_dev, IH_MUTE_PHY_STAT0, 0xfc); hdmi_writel(hdmi_dev, IH_MUTE_I2CM_STAT0, 0xff); hdmi_writel(hdmi_dev, IH_MUTE_CEC_STAT0, 0xff); hdmi_writel(hdmi_dev, IH_MUTE_VP_STAT0, 0xff); hdmi_writel(hdmi_dev, IH_MUTE_I2CMPHY_STAT0, 0xff); hdmi_writel(hdmi_dev, IH_MUTE_AHBDMAAUD_STAT0, 0xff); /* disable hdcp interrupt */ hdmi_writel(hdmi_dev, A_APIINTMSK, 0xff); hdmi_writel(hdmi_dev, PHY_MASK, 0xf1); if (hdmi->property->feature & SUPPORT_CEC) rockchip_hdmiv2_cec_init(hdmi); if (hdmi->property->feature & SUPPORT_HDCP) rockchip_hdmiv2_hdcp_init(hdmi); } irqreturn_t rockchip_hdmiv2_dev_irq(int irq, void *priv) { struct hdmi_dev *hdmi_dev = priv; struct hdmi *hdmi = hdmi_dev->hdmi; char phy_pol = hdmi_readl(hdmi_dev, PHY_POL0); char phy_status = hdmi_readl(hdmi_dev, PHY_STAT0); char phy_int0 = hdmi_readl(hdmi_dev, PHY_INI0); /*read interrupt*/ char fc_stat0 = hdmi_readl(hdmi_dev, IH_FC_STAT0); char fc_stat1 = hdmi_readl(hdmi_dev, IH_FC_STAT1); char fc_stat2 = hdmi_readl(hdmi_dev, IH_FC_STAT2); char aud_int = hdmi_readl(hdmi_dev, IH_AS_SATA0); char phy_int = hdmi_readl(hdmi_dev, IH_PHY_STAT0); char vp_stat0 = hdmi_readl(hdmi_dev, IH_VP_STAT0); char cec_int = hdmi_readl(hdmi_dev, IH_CEC_STAT0); char hdcp_int = hdmi_readl(hdmi_dev, A_APIINTSTAT); u8 hdcp2_int = hdmi_readl(hdmi_dev, HDCP2REG_STAT); /*clear interrupt*/ hdmi_writel(hdmi_dev, IH_FC_STAT0, fc_stat0); hdmi_writel(hdmi_dev, IH_FC_STAT1, fc_stat1); hdmi_writel(hdmi_dev, IH_FC_STAT2, fc_stat2); hdmi_writel(hdmi_dev, IH_VP_STAT0, vp_stat0); if (phy_int0 || phy_int) { if ((phy_int0 & m_PHY_LOCK) && (phy_pol & m_PHY_LOCK) == 0) { pr_info("hdmi phy pll unlock\n"); hdmi_submit_work(hdmi, HDMI_SET_VIDEO, 0, 0); } phy_pol = (phy_int0 & (~phy_status)) | ((~phy_int0) & phy_pol); hdmi_writel(hdmi_dev, PHY_POL0, phy_pol); hdmi_writel(hdmi_dev, IH_PHY_STAT0, phy_int); if ((phy_int & m_HPD) || ((phy_int & 0x3c) == 0x3c)) hdmi_submit_work(hdmi, HDMI_HPD_CHANGE, 20, 0); } /* Audio error */ if (aud_int) { hdmi_writel(hdmi_dev, IH_AS_SATA0, aud_int); hdmi_msk_reg(hdmi_dev, AUD_CONF0, m_SW_AUD_FIFO_RST, v_SW_AUD_FIFO_RST(1)); hdmi_writel(hdmi_dev, MC_SWRSTZREQ, 0xF7); } /* CEC */ if (cec_int) { hdmi_writel(hdmi_dev, IH_CEC_STAT0, cec_int); if (hdmi_dev->hdmi->property->feature & SUPPORT_CEC) rockchip_hdmiv2_cec_isr(hdmi_dev, cec_int); } /* HDCP */ if (hdcp_int) { hdmi_writel(hdmi_dev, A_APIINTCLR, hdcp_int); rockchip_hdmiv2_hdcp_isr(hdmi_dev, hdcp_int); } /* HDCP2 */ if (hdcp2_int) { hdmi_writel(hdmi_dev, HDCP2REG_STAT, hdcp2_int); pr_info("hdcp2_int is 0x%02x\n", hdcp2_int); if ((hdcp2_int & m_HDCP2_AUTH_FAIL || hdcp2_int & m_HDCP2_AUTH_LOST) && hdmi_dev->hdcp2_start) { pr_info("hdcp2 failed or lost\n"); hdmi_dev->hdcp2_start(); } } return IRQ_HANDLED; }