diff --git a/drivers/video/drm/Kconfig b/drivers/video/drm/Kconfig index da0b6bd689..015d0ba0fe 100644 --- a/drivers/video/drm/Kconfig +++ b/drivers/video/drm/Kconfig @@ -24,6 +24,14 @@ config DRM_ROCKCHIP_DW_HDMI for the Synopsys DesignWare HDMI driver. If you want to enable HDMI on, you should selet this option. +config DRM_ROCKCHIP_INNO_HDMI + bool "Rockchip specific extensions for INNO HDMI" + depends on DRM_ROCKCHIP + help + This selects support for Rockchip SoC specific extensions + for the Synopsys DesignWare HDMI driver. If you want to + enable HDMI on, you should selet this option. + config ROCKCHIP_INNO_HDMI_PHY bool "Rockchip specific extensions for INNO HDMI PHY" depends on DRM_ROCKCHIP diff --git a/drivers/video/drm/Makefile b/drivers/video/drm/Makefile index 1ee63fe096..7d704d0e1c 100644 --- a/drivers/video/drm/Makefile +++ b/drivers/video/drm/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o obj-$(CONFIG_DRM_ROCKCHIP_DW_MIPI_DSI) += dw_mipi_dsi.o obj-$(CONFIG_DRM_ROCKCHIP_DW_HDMI) += rockchip_dw_hdmi.o dw_hdmi.o obj-$(CONFIG_ROCKCHIP_INNO_HDMI_PHY) += rockchip-inno-hdmi-phy.o +obj-$(CONFIG_DRM_ROCKCHIP_INNO_HDMI) += inno_hdmi.o obj-$(CONFIG_DRM_ROCKCHIP_INNO_MIPI_PHY) += inno_mipi_phy.o obj-$(CONFIG_DRM_ROCKCHIP_INNO_VIDEO_PHY) += inno_video_phy.o obj-$(CONFIG_DRM_ROCKCHIP_INNO_VIDEO_COMBO_PHY) += inno_video_combo_phy.o diff --git a/drivers/video/drm/inno_hdmi.c b/drivers/video/drm/inno_hdmi.c new file mode 100644 index 0000000000..c4f20649ad --- /dev/null +++ b/drivers/video/drm/inno_hdmi.c @@ -0,0 +1,850 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2020 Rockchip Electronics Co., Ltd + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inno_hdmi.h" +#include "rockchip_connector.h" +#include "rockchip_crtc.h" +#include "rockchip_display.h" + +struct inno_hdmi_i2c { + u8 slave_reg; + u8 ddc_addr; + u8 segment_addr; + bool is_regaddr; + bool is_segment; + + unsigned int scl_high_ns; + unsigned int scl_low_ns; +}; + +enum inno_hdmi_dev_type { + RK3036_HDMI, + RK3128_HDMI, +}; + +enum { + CSC_ITU601_16_235_TO_RGB_0_255_8BIT, + CSC_ITU601_0_255_TO_RGB_0_255_8BIT, + CSC_ITU709_16_235_TO_RGB_0_255_8BIT, + CSC_RGB_0_255_TO_ITU601_16_235_8BIT, + CSC_RGB_0_255_TO_ITU709_16_235_8BIT, + CSC_RGB_0_255_TO_RGB_16_235_8BIT, +}; + +static const char coeff_csc[][24] = { + /* + * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]): + * R = 1.164*Y + 1.596*V - 204 + * G = 1.164*Y - 0.391*U - 0.813*V + 154 + * B = 1.164*Y + 2.018*U - 258 + */ + { + 0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc, + 0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a, + 0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02 + }, + /* + * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]): + * R = Y + 1.402*V - 248 + * G = Y - 0.344*U - 0.714*V + 135 + * B = Y + 1.772*U - 227 + */ + { + 0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8, + 0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87, + 0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3 + }, + /* + * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]): + * R = 1.164*Y + 1.793*V - 248 + * G = 1.164*Y - 0.213*U - 0.534*V + 77 + * B = 1.164*Y + 2.115*U - 289 + */ + { + 0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8, + 0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d, + 0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21 + }, + + /* + * RGB2YUV:601 SD mode: + * Cb = -0.291G - 0.148R + 0.439B + 128 + * Y = 0.504G + 0.257R + 0.098B + 16 + * Cr = -0.368G + 0.439R - 0.071B + 128 + */ + { + 0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80, + 0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e, + 0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80 + }, + /* + * RGB2YUV:709 HD mode: + * Cb = - 0.338G - 0.101R + 0.439B + 128 + * Y = 0.614G + 0.183R + 0.062B + 16 + * Cr = - 0.399G + 0.439R - 0.040B + 128 + */ + { + 0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80, + 0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10, + 0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80 + }, + /* + * RGB[0:255]2RGB[16:235]: + * R' = R x (235-16)/255 + 16; + * G' = G x (235-16)/255 + 16; + * B' = B x (235-16)/255 + 16; + */ + { + 0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10, + 0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10 + }, +}; + +struct hdmi_data_info { + int vic; + bool sink_is_hdmi; + bool sink_has_audio; + unsigned int enc_in_format; + unsigned int enc_out_format; + unsigned int colorimetry; +}; + +struct inno_hdmi_phy_config { + unsigned long mpixelclock; + u8 pre_emphasis; /* pre-emphasis value */ + u8 vlev_ctr; /* voltage level control */ +}; + +struct inno_hdmi_plat_data { + enum inno_hdmi_dev_type dev_type; + struct inno_hdmi_phy_config *phy_config; +}; + +struct inno_hdmi { + struct device *dev; + struct drm_device *drm_dev; + struct ddc_adapter adap; + struct hdmi_edid_data edid_data; + struct hdmi_data_info hdmi_data; + + struct clk pclk; + int vic; + void *regs; + void *grf; + + struct inno_hdmi_i2c *i2c; + + unsigned int tmds_rate; + const struct inno_hdmi_plat_data *plat_data; + + unsigned int sample_rate; + unsigned int audio_cts; + unsigned int audio_n; + bool audio_enable; + + struct drm_display_mode previous_mode; +}; + +static struct inno_hdmi_phy_config rk3036_hdmi_phy_config[] = { + /* pixelclk pre-emp vlev */ + { 74250000, 0x3f, 0xbb }, + { 165000000, 0x6f, 0xbb }, + { ~0UL, 0x00, 0x00 } +}; + +static struct inno_hdmi_phy_config rk3128_hdmi_phy_config[] = { + /* pixelclk pre-emp vlev */ + { 74250000, 0x3f, 0xaa }, + { 165000000, 0x5f, 0xaa }, + { ~0UL, 0x00, 0x00 } +}; + +static void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val) +{ + writel(val, hdmi->regs + (offset << 2)); +} + +static u32 hdmi_readb(struct inno_hdmi *hdmi, u16 offset) +{ + return readl(hdmi->regs + (offset << 2)); +} + +static void hdmi_modb(struct inno_hdmi *hdmi, u16 offset, u32 msk, u32 val) +{ + u32 temp = hdmi_readb(hdmi, offset) & ~msk; + + temp |= val & msk; + hdmi_writeb(hdmi, offset, temp); +} + +static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable) +{ + if (enable) + hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON); + else + hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF); +} + +static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode) +{ + const struct inno_hdmi_phy_config *phy_config = + hdmi->plat_data->phy_config; + + switch (mode) { + case NORMAL: + inno_hdmi_sys_power(hdmi, false); + for (; phy_config->mpixelclock != ~0UL; phy_config++) + if (hdmi->tmds_rate <= phy_config->mpixelclock) + break; + if (!phy_config->mpixelclock) + return; + hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, + phy_config->pre_emphasis); + hdmi_writeb(hdmi, HDMI_PHY_DRIVER, phy_config->vlev_ctr); + + hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15); + hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14); + hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10); + + hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f); + hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00); + hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01); + inno_hdmi_sys_power(hdmi, true); + + break; + + case LOWER_PWR: + inno_hdmi_sys_power(hdmi, false); + hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00); + hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00); + hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00); + hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15); + + break; + + default: + dev_err(hdmi->dev, "Unknown power mode %d\n", mode); + } +} + +static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi) +{ + int ddc_bus_freq; + + ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE; + hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF); + hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF); + + /* Clear the EDID interrupt flag and mute the interrupt */ + hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0); + hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY); +} + +static void inno_hdmi_reset(struct inno_hdmi *hdmi) +{ + u32 val; + u32 msk; + + hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL); + udelay(100); + + hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG); + udelay(100); + + msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL; + val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH; + + hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val); + + inno_hdmi_set_pwr_mode(hdmi, NORMAL); +} + +static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc, + union hdmi_infoframe *frame, u32 frame_index, + u32 mask, u32 disable, u32 enable) +{ + if (mask) + hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable); + + hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index); + + if (setup_rc >= 0) { + u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE]; + ssize_t rc, i; + + rc = hdmi_infoframe_pack(frame, packed_frame, + sizeof(packed_frame)); + if (rc < 0) + return rc; + + for (i = 0; i < rc; i++) + hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, + packed_frame[i]); + + if (mask) + hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable); + } + + return setup_rc; +} + +static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi, + struct drm_display_mode *mode) +{ + union hdmi_infoframe frame; + int rc; + + rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi, + mode); + + return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_VSI, + m_PACKET_VSI_EN, v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1)); +} + +static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi, + struct drm_display_mode *mode) +{ + union hdmi_infoframe frame; + int rc; + + rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode, false); + + if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444) + frame.avi.colorspace = HDMI_COLORSPACE_YUV444; + else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422) + frame.avi.colorspace = HDMI_COLORSPACE_YUV422; + else + frame.avi.colorspace = HDMI_COLORSPACE_RGB; + + if (frame.avi.colorspace != HDMI_COLORSPACE_RGB) + frame.avi.colorimetry = hdmi->hdmi_data.colorimetry; + + frame.avi.scan_mode = HDMI_SCAN_MODE_NONE; + + return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI, 0, 0, 0); +} + +static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi) +{ + struct hdmi_data_info *data = &hdmi->hdmi_data; + int c0_c2_change = 0; + int csc_enable = 0; + int csc_mode = 0; + int auto_csc = 0; + int value; + int i; + + /* Input video mode is SDR RGB24bit, data enable signal from external */ + hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL | + v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444)); + + /* Input color hardcode to RGB, and output color hardcode to RGB888 */ + value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) | + v_VIDEO_OUTPUT_COLOR(0) | + v_VIDEO_INPUT_CSP(0); + hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value); + + if (data->enc_in_format == data->enc_out_format) { + if (data->enc_in_format == HDMI_COLORSPACE_RGB || + data->enc_in_format >= HDMI_COLORSPACE_YUV444) { + value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1); + hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value); + + hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, + m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP, + v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) | + v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE)); + return 0; + } + } + + if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) { + if (data->enc_in_format == HDMI_COLORSPACE_RGB && + data->enc_out_format == HDMI_COLORSPACE_YUV444) { + csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT; + auto_csc = AUTO_CSC_DISABLE; + c0_c2_change = C0_C2_CHANGE_DISABLE; + csc_enable = v_CSC_ENABLE; + } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) && + (data->enc_out_format == HDMI_COLORSPACE_RGB)) { + csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT; + auto_csc = AUTO_CSC_ENABLE; + c0_c2_change = C0_C2_CHANGE_DISABLE; + csc_enable = v_CSC_DISABLE; + } + } else { + if (data->enc_in_format == HDMI_COLORSPACE_RGB && + data->enc_out_format == HDMI_COLORSPACE_YUV444) { + csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT; + auto_csc = AUTO_CSC_DISABLE; + c0_c2_change = C0_C2_CHANGE_DISABLE; + csc_enable = v_CSC_ENABLE; + } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) && + (data->enc_out_format == HDMI_COLORSPACE_RGB)) { + csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT; + auto_csc = AUTO_CSC_ENABLE; + c0_c2_change = C0_C2_CHANGE_DISABLE; + csc_enable = v_CSC_DISABLE; + } + } + + for (i = 0; i < 24; i++) + hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i, + coeff_csc[csc_mode][i]); + + value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1); + hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value); + hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC | + m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) | + v_VIDEO_C0_C2_SWAP(c0_c2_change)); + + return 0; +} + +static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi, + struct drm_display_mode *mode) +{ + int value; + + if (hdmi->plat_data->dev_type == RK3036_HDMI) { + value = BIT(20) | BIT(21); + value |= mode->flags & DRM_MODE_FLAG_PHSYNC ? BIT(4) : 0; + value |= mode->flags & DRM_MODE_FLAG_PVSYNC ? BIT(5) : 0; + writel(value, hdmi->grf + 0x148); + } + /* Set detail external video timing polarity and interlace mode */ + value = v_EXTERANL_VIDEO(1); + value |= mode->flags & DRM_MODE_FLAG_PHSYNC ? + v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0); + value |= mode->flags & DRM_MODE_FLAG_PVSYNC ? + v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0); + value |= mode->flags & DRM_MODE_FLAG_INTERLACE ? + v_INETLACE(1) : v_INETLACE(0); + hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value); + + /* Set detail external video timing */ + value = mode->htotal; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF); + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF); + + value = mode->htotal - mode->hdisplay; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF); + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF); + + value = mode->htotal - mode->hsync_start; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF); + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF); + + value = mode->hsync_end - mode->hsync_start; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF); + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF); + + value = mode->vtotal; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF); + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF); + + value = mode->vtotal - mode->vdisplay; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF); + + value = mode->vtotal - mode->vsync_start; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF); + + value = mode->vsync_end - mode->vsync_start; + hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF); + + hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e); + hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c); + hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01); + + return 0; +} + +static int inno_hdmi_setup(struct inno_hdmi *hdmi, + struct drm_display_mode *mode) +{ + hdmi->hdmi_data.vic = drm_match_cea_mode(mode); + + hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB; + hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB; + + if (hdmi->hdmi_data.vic == 6 || hdmi->hdmi_data.vic == 7 || + hdmi->hdmi_data.vic == 21 || hdmi->hdmi_data.vic == 22 || + hdmi->hdmi_data.vic == 2 || hdmi->hdmi_data.vic == 3 || + hdmi->hdmi_data.vic == 17 || hdmi->hdmi_data.vic == 18) + hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601; + else + hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709; + + /* Mute video and audio output */ + hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK, + v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1)); + + /* Set HDMI Mode */ + hdmi_writeb(hdmi, HDMI_HDCP_CTRL, + v_HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi)); + + inno_hdmi_config_video_timing(hdmi, mode); + + inno_hdmi_config_video_csc(hdmi); + + if (hdmi->hdmi_data.sink_is_hdmi) { + inno_hdmi_config_video_avi(hdmi, mode); + inno_hdmi_config_video_vsi(hdmi, mode); + } + + /* + * When IP controller have configured to an accurate video + * timing, then the TMDS clock source would be switched to + * DCLK_LCDC, so we need to init the TMDS rate to mode pixel + * clock rate, and reconfigure the DDC clock. + */ + hdmi->tmds_rate = mode->clock * 1000; + inno_hdmi_i2c_init(hdmi); + /* Unmute video and audio output */ + hdmi_modb(hdmi, HDMI_AV_MUTE, m_VIDEO_BLACK, v_VIDEO_MUTE(0)); + if (hdmi->audio_enable) + hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE, v_AUDIO_MUTE(0)); + + return 0; +} + +static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi, + struct i2c_msg *msgs) +{ + struct inno_hdmi_i2c *i2c = hdmi->i2c; + unsigned int length = msgs->len; + unsigned char *buf = msgs->buf; + int interrupt = 0, i = 20; + + while (i--) { + mdelay(50); + interrupt = 0; + interrupt = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1); + + if (interrupt & m_INT_EDID_READY) + break; + } + + if (!interrupt) { + printf("[%s] i2c read reg[0x%02x] no interrupt\n", + __func__, i2c->slave_reg); + return -EAGAIN; + } + + /* Clear HDMI EDID interrupt flag */ + hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY); + + while (length--) + *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR); + + return 0; +} + +static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, + struct i2c_msg *msgs) +{ + unsigned int length = msgs->len; + + hdmi->i2c->segment_addr = 0; + hdmi->i2c->ddc_addr = 0; + + /* + * The DDC module only support read EDID message, so + * we assume that each word write to this i2c adapter + * should be the offset of EDID word address. + */ + if (length != 1 || + (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR)) { + printf("DDC word write to i2c adapter is not EDID address\n"); + return -EINVAL; + } + + if (msgs->addr == DDC_SEGMENT_ADDR) + hdmi->i2c->segment_addr = msgs->buf[0]; + if (msgs->addr == DDC_ADDR) + hdmi->i2c->ddc_addr = msgs->buf[0]; + + /* Set edid fifo first addr */ + hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00); + + /* Set edid word address 0x00/0x80 */ + hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr); + + /* Set edid segment pointer */ + hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr); + + return 0; +} + +static int inno_hdmi_i2c_xfer(struct ddc_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct inno_hdmi *hdmi = container_of(adap, struct inno_hdmi, adap); + int i, ret = 0; + + /* Clear the EDID interrupt flag and unmute the interrupt */ + hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY); + hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY); + + for (i = 0; i < num; i++) { + dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n", + i + 1, num, msgs[i].len, msgs[i].flags); + + if (msgs[i].flags & I2C_M_RD) + ret = inno_hdmi_i2c_read(hdmi, &msgs[i]); + else + ret = inno_hdmi_i2c_write(hdmi, &msgs[i]); + + if (ret < 0) + break; + } + + if (!ret) + ret = num; + + /* Mute HDMI EDID interrupt */ + hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0); + + return ret; +} + +static int rockchip_inno_hdmi_init(struct display_state *state) +{ + struct connector_state *conn_state = &state->conn_state; + const struct rockchip_connector *connector = conn_state->connector; + struct inno_hdmi *hdmi; + struct drm_display_mode *mode_buf; + ofnode hdmi_node = conn_state->node; + int ret; + + hdmi = calloc(1, sizeof(struct inno_hdmi)); + if (!hdmi) + return -ENOMEM; + + mode_buf = calloc(1, MODE_LEN * sizeof(struct drm_display_mode)); + if (!mode_buf) + return -ENOMEM; + + hdmi->regs = dev_read_addr_ptr(conn_state->dev); + + hdmi->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + if (hdmi->grf <= 0) { + printf("%s: Get syscon grf failed (ret=%p)\n", + __func__, hdmi->grf); + return -ENXIO; + } + + hdmi->i2c = malloc(sizeof(struct inno_hdmi_i2c)); + if (!hdmi->i2c) + return -ENOMEM; + + hdmi->adap.ddc_xfer = inno_hdmi_i2c_xfer; + + /* + * Read high and low time from device tree. If not available use + * the default timing scl clock rate is about 99.6KHz. + */ + hdmi->i2c->scl_high_ns = + ofnode_read_s32_default(hdmi_node, + "ddc-i2c-scl-high-time-ns", 4708); + hdmi->i2c->scl_low_ns = + ofnode_read_s32_default(hdmi_node, + "ddc-i2c-scl-low-time-ns", 4916); + + conn_state->type = DRM_MODE_CONNECTOR_HDMIA; + conn_state->output_mode = ROCKCHIP_OUT_MODE_AAAA; + + hdmi->plat_data = (struct inno_hdmi_plat_data *)connector->data; + hdmi->edid_data.mode_buf = mode_buf; + hdmi->sample_rate = 48000; + + conn_state->private = hdmi; + + inno_hdmi_reset(hdmi); + ret = clk_get_by_name(conn_state->dev, "pclk", &hdmi->pclk); + if (ret < 0) { + dev_err(hdmi->dev, "failed to get pclk: %d\n", ret); + return ret; + } + hdmi->tmds_rate = clk_get_rate(&hdmi->pclk); + inno_hdmi_i2c_init(hdmi); + + /* Unmute hotplug interrupt */ + hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1)); + + return 0; +} + +static int rockchip_inno_hdmi_enable(struct display_state *state) +{ + struct connector_state *conn_state = &state->conn_state; + struct drm_display_mode *mode = &conn_state->mode; + struct inno_hdmi *hdmi = conn_state->private; + + if (!hdmi) + return -EFAULT; + + /* Store the display mode for plugin/DKMS poweron events */ + memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); + + inno_hdmi_setup(hdmi, mode); + inno_hdmi_set_pwr_mode(hdmi, NORMAL); + + return 0; +} + +static void rockchip_inno_hdmi_deinit(struct display_state *state) +{ + struct connector_state *conn_state = &state->conn_state; + struct inno_hdmi *hdmi = conn_state->private; + + if (hdmi->i2c) + free(hdmi->i2c); + if (hdmi) + free(hdmi); +} + +static int rockchip_inno_hdmi_prepare(struct display_state *state) +{ + return 0; +} + +static int rockchip_inno_hdmi_disable(struct display_state *state) +{ + struct connector_state *conn_state = &state->conn_state; + struct inno_hdmi *hdmi = conn_state->private; + + inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR); + return 0; +} + +static int rockchip_inno_hdmi_detect(struct display_state *state) +{ + struct connector_state *conn_state = &state->conn_state; + struct inno_hdmi *hdmi = conn_state->private; + + return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ? + connector_status_connected : connector_status_disconnected; +} + +static int rockchip_inno_hdmi_get_timing(struct display_state *state) +{ + int i, ret; + struct connector_state *conn_state = &state->conn_state; + struct drm_display_mode *mode = &conn_state->mode; + struct inno_hdmi *hdmi = conn_state->private; + struct edid *edid = (struct edid *)conn_state->edid; + const u8 def_modes_vic[6] = {16, 4, 2, 17, 31, 19}; + + if (!hdmi) + return -EFAULT; + + ret = drm_do_get_edid(&hdmi->adap, conn_state->edid); + if (!ret) { + hdmi->hdmi_data.sink_is_hdmi = + drm_detect_hdmi_monitor(edid); + hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid); + ret = drm_add_edid_modes(&hdmi->edid_data, conn_state->edid); + } + if (ret <= 0) { + hdmi->hdmi_data.sink_is_hdmi = true; + hdmi->hdmi_data.sink_has_audio = true; + do_cea_modes(&hdmi->edid_data, def_modes_vic, + sizeof(def_modes_vic)); + hdmi->edid_data.preferred_mode = &hdmi->edid_data.mode_buf[0]; + printf("failed to get edid\n"); + } + drm_rk_filter_whitelist(&hdmi->edid_data); + + if (!drm_mode_prune_invalid(&hdmi->edid_data)) { + printf("can't find valid hdmi mode\n"); + return -EINVAL; + } + + for (i = 0; i < hdmi->edid_data.modes; i++) + hdmi->edid_data.mode_buf[i].vrefresh = + drm_mode_vrefresh(&hdmi->edid_data.mode_buf[i]); + + drm_mode_sort(&hdmi->edid_data); + + *mode = *hdmi->edid_data.preferred_mode; + hdmi->vic = drm_match_cea_mode(mode); + + printf("mode:%dx%d\n", mode->hdisplay, mode->vdisplay); + + conn_state->bus_format = MEDIA_BUS_FMT_RGB888_1X24; + + return 0; +} + +static int rockchip_inno_hdmi_probe(struct udevice *dev) +{ + return 0; +} + +static int rockchip_inno_hdmi_bind(struct udevice *dev) +{ + return 0; +} + +const struct rockchip_connector_funcs rockchip_inno_hdmi_funcs = { + .init = rockchip_inno_hdmi_init, + .deinit = rockchip_inno_hdmi_deinit, + .prepare = rockchip_inno_hdmi_prepare, + .enable = rockchip_inno_hdmi_enable, + .disable = rockchip_inno_hdmi_disable, + .get_timing = rockchip_inno_hdmi_get_timing, + .detect = rockchip_inno_hdmi_detect, +}; + +static const struct inno_hdmi_plat_data rk3036_hdmi_drv_data = { + .dev_type = RK3036_HDMI, + .phy_config = rk3036_hdmi_phy_config, +}; + +static const struct inno_hdmi_plat_data rk3128_hdmi_drv_data = { + .dev_type = RK3128_HDMI, + .phy_config = rk3128_hdmi_phy_config, +}; + +static const struct rockchip_connector rk3036_inno_hdmi_data = { + .funcs = &rockchip_inno_hdmi_funcs, + .data = &rk3036_hdmi_drv_data, +}; + +static const struct rockchip_connector rk3128_inno_hdmi_data = { + .funcs = &rockchip_inno_hdmi_funcs, + .data = &rk3128_hdmi_drv_data, +}; + +static const struct udevice_id rockchip_inno_hdmi_ids[] = { + { + .compatible = "rockchip,rk3036-inno-hdmi", + .data = (ulong)&rk3036_inno_hdmi_data, + }, + { + .compatible = "rockchip,rk3128-inno-hdmi", + .data = (ulong)&rk3128_inno_hdmi_data, + }, {} + +}; + +U_BOOT_DRIVER(rockchip_inno_hdmi) = { + .name = "rockchip_inno_hdmi", + .id = UCLASS_DISPLAY, + .of_match = rockchip_inno_hdmi_ids, + .probe = rockchip_inno_hdmi_probe, + .bind = rockchip_inno_hdmi_bind, +}; diff --git a/drivers/video/drm/inno_hdmi.h b/drivers/video/drm/inno_hdmi.h new file mode 100644 index 0000000000..1e65bea57c --- /dev/null +++ b/drivers/video/drm/inno_hdmi.h @@ -0,0 +1,381 @@ +/* + * SPDX-Licexse-Idextifier: GPL-2.0 + * Copyright (C) Rockchip Electroxics Co.Ltd + * Zhexg Yaxg + * Yakir Yaxg + * + * This software is licexsed uxder the terms of the GNU Gexeral Public + * Licexse versiox 2, as published by the Free Software Fouxdatiox, axd + * may be copied, distributed, axd modified uxder those terms. + * + * This program is distributed ix the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without evex the implied warraxty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Gexeral Public Licexse for more details. + */ + +#ifndef __INNO_HDMI_H__ +#define __INNO_HDMI_H__ + +#define DDC_SEGMENT_ADDR 0x30 + +enum PWR_MODE { + NORMAL, + LOWER_PWR, +}; + +#define HDMI_SCL_RATE (100 * 1000) +#define DDC_BUS_FREQ_L 0x4b +#define DDC_BUS_FREQ_H 0x4c + +#define HDMI_SYS_CTRL 0x00 +#define m_RST_ANALOG BIT(6) +#define v_RST_ANALOG (0 << 6) +#define v_NOT_RST_ANALOG BIT(6) +#define m_RST_DIGITAL BIT(5) +#define v_RST_DIGITAL (0 << 5) +#define v_NOT_RST_DIGITAL BIT(5) +#define m_REG_CLK_INV BIT(4) +#define v_REG_CLK_NOT_INV (0 << 4) +#define v_REG_CLK_INV BIT(4) +#define m_VCLK_INV BIT(3) +#define v_VCLK_NOT_INV (0 << 3) +#define v_VCLK_INV BIT(3) +#define m_REG_CLK_SOURCE BIT(2) +#define v_REG_CLK_SOURCE_TMDS (0 << 2) +#define v_REG_CLK_SOURCE_SYS BIT(2) +#define m_POWER BIT(1) +#define v_PWR_ON (0 << 1) +#define v_PWR_OFF BIT(1) +#define m_INT_POL BIT(0) +#define v_INT_POL_HIGH 1 +#define v_INT_POL_LOW 0 + +#define HDMI_VIDEO_CONTRL1 0x01 +#define m_VIDEO_INPUT_FORMAT (7 << 1) +#define m_DE_SOURCE BIT(0) +#define v_VIDEO_INPUT_FORMAT(x) ((x) << 1) +#define v_DE_EXTERNAL 1 +#define v_DE_INTERNAL 0 +enum { + VIDEO_INPUT_SDR_RGB444 = 0, + VIDEO_INPUT_DDR_RGB444 = 5, + VIDEO_INPUT_DDR_YCBCR422 = 6 +}; + +#define HDMI_VIDEO_CONTRL2 0x02 +#define m_VIDEO_OUTPUT_COLOR (3 << 6) +#define m_VIDEO_INPUT_BITS (3 << 4) +#define m_VIDEO_INPUT_CSP BIT(0) +#define v_VIDEO_OUTPUT_COLOR(x) (((x) & 0x3) << 6) +#define v_VIDEO_INPUT_BITS(x) ((x) << 4) +#define v_VIDEO_INPUT_CSP(x) ((x) << 0) +enum { + VIDEO_INPUT_12BITS = 0, + VIDEO_INPUT_10BITS = 1, + VIDEO_INPUT_REVERT = 2, + VIDEO_INPUT_8BITS = 3, +}; + +#define HDMI_VIDEO_CONTRL 0x03 +#define m_VIDEO_AUTO_CSC BIT(7) +#define v_VIDEO_AUTO_CSC(x) ((x) << 7) +#define m_VIDEO_C0_C2_SWAP BIT(0) +#define v_VIDEO_C0_C2_SWAP(x) ((x) << 0) +enum { + C0_C2_CHANGE_ENABLE = 0, + C0_C2_CHANGE_DISABLE = 1, + AUTO_CSC_DISABLE = 0, + AUTO_CSC_ENABLE = 1, +}; + +#define HDMI_VIDEO_CONTRL3 0x04 +#define m_COLOR_DEPTH_NOT_INDICATED BIT(4) +#define m_SOF BIT(3) +#define m_COLOR_RANGE BIT(2) +#define m_CSC BIT(0) +#define v_COLOR_DEPTH_NOT_INDICATED(x) ((x) << 4) +#define v_SOF_ENABLE (0 << 3) +#define v_SOF_DISABLE BIT(3) +#define v_COLOR_RANGE_FULL BIT(2) +#define v_COLOR_RANGE_LIMITED (0 << 2) +#define v_CSC_ENABLE 1 +#define v_CSC_DISABLE 0 + +#define HDMI_AV_MUTE 0x05 +#define m_AVMUTE_CLEAR BIT(7) +#define m_AVMUTE_ENABLE BIT(6) +#define m_AUDIO_PD BIT(2) +#define m_AUDIO_MUTE BIT(1) +#define m_VIDEO_BLACK BIT(0) +#define v_AVMUTE_CLEAR(x) ((x) << 7) +#define v_AVMUTE_ENABLE(x) ((x) << 6) +#define v_AUDIO_MUTE(x) ((x) << 1) +#define v_AUDIO_PD(x) ((x) << 2) +#define v_VIDEO_MUTE(x) ((x) << 0) + +#define HDMI_VIDEO_TIMING_CTL 0x08 +#define v_HSYNC_POLARITY(x) ((x) << 3) +#define v_VSYNC_POLARITY(x) ((x) << 2) +#define v_INETLACE(x) ((x) << 1) +#define v_EXTERANL_VIDEO(x) ((x) << 0) + +#define HDMI_VIDEO_EXT_HTOTAL_L 0x09 +#define HDMI_VIDEO_EXT_HTOTAL_H 0x0a +#define HDMI_VIDEO_EXT_HBLANK_L 0x0b +#define HDMI_VIDEO_EXT_HBLANK_H 0x0c +#define HDMI_VIDEO_EXT_HDELAY_L 0x0d +#define HDMI_VIDEO_EXT_HDELAY_H 0x0e +#define HDMI_VIDEO_EXT_HDURATION_L 0x0f +#define HDMI_VIDEO_EXT_HDURATION_H 0x10 +#define HDMI_VIDEO_EXT_VTOTAL_L 0x11 +#define HDMI_VIDEO_EXT_VTOTAL_H 0x12 +#define HDMI_VIDEO_EXT_VBLANK 0x13 +#define HDMI_VIDEO_EXT_VDELAY 0x14 +#define HDMI_VIDEO_EXT_VDURATION 0x15 + +#define HDMI_VIDEO_CSC_COEF 0x18 + +#define HDMI_AUDIO_CTRL1 0x35 +enum { + CTS_SOURCE_INTERNAL = 0, + CTS_SOURCE_EXTERNAL = 1, +}; + +#define v_CTS_SOURCE(x) ((x) << 7) + +enum { + DOWNSAMPLE_DISABLE = 0, + DOWNSAMPLE_1_2 = 1, + DOWNSAMPLE_1_4 = 2, +}; + +#define v_DOWN_SAMPLE(x) ((x) << 5) + +enum { + AUDIO_SOURCE_IIS = 0, + AUDIO_SOURCE_SPDIF = 1, +}; + +#define v_AUDIO_SOURCE(x) ((x) << 3) + +#define v_MCLK_ENABLE(x) ((x) << 2) +enum { + MCLK_128FS = 0, + MCLK_256FS = 1, + MCLK_384FS = 2, + MCLK_512FS = 3, +}; + +#define v_MCLK_RATIO(x) (x) + +#define AUDIO_SAMPLE_RATE 0x37 +enum { + AUDIO_32K = 0x3, + AUDIO_441K = 0x0, + AUDIO_48K = 0x2, + AUDIO_882K = 0x8, + AUDIO_96K = 0xa, + AUDIO_1764K = 0xc, + AUDIO_192K = 0xe, +}; + +#define AUDIO_I2S_MODE 0x38 +enum { + I2S_CHANNEL_1_2 = 1, + I2S_CHANNEL_3_4 = 3, + I2S_CHANNEL_5_6 = 7, + I2S_CHANNEL_7_8 = 0xf +}; + +#define v_I2S_CHANNEL(x) ((x) << 2) +enum { + I2S_STANDARD = 0, + I2S_LEFT_JUSTIFIED = 1, + I2S_RIGHT_JUSTIFIED = 2, +}; + +#define v_I2S_MODE(x) (x) + +#define AUDIO_I2S_MAP 0x39 +#define AUDIO_I2S_SWAPS_SPDIF 0x3a +#define v_SPIDF_FREQ(x) (x) + +#define N_32K 0x1000 +#define N_441K 0x1880 +#define N_882K 0x3100 +#define N_1764K 0x6200 +#define N_48K 0x1800 +#define N_96K 0x3000 +#define N_192K 0x6000 + +#define HDMI_AUDIO_CHANNEL_STATUS 0x3e +#define m_AUDIO_STATUS_NLPCM BIT(7) +#define m_AUDIO_STATUS_USE BIT(6) +#define m_AUDIO_STATUS_COPYRIGHT BIT(5) +#define m_AUDIO_STATUS_ADDITION (3 << 2) +#define m_AUDIO_STATUS_CLK_ACCURACY (2 << 0) +#define v_AUDIO_STATUS_NLPCM(x) (((x) & 1) << 7) +#define AUDIO_N_H 0x3f +#define AUDIO_N_M 0x40 +#define AUDIO_N_L 0x41 + +#define HDMI_AUDIO_CTS_H 0x45 +#define HDMI_AUDIO_CTS_M 0x46 +#define HDMI_AUDIO_CTS_L 0x47 + +#define HDMI_DDC_CLK_L 0x4b +#define HDMI_DDC_CLK_H 0x4c + +#define HDMI_EDID_SEGMENT_POINTER 0x4d +#define HDMI_EDID_WORD_ADDR 0x4e +#define HDMI_EDID_FIFO_OFFSET 0x4f +#define HDMI_EDID_FIFO_ADDR 0x50 + +#define HDMI_PACKET_SEND_MANUAL 0x9c +#define HDMI_PACKET_SEND_AUTO 0x9d +#define m_PACKET_GCP_EN BIT(7) +#define m_PACKET_MSI_EN BIT(6) +#define m_PACKET_SDI_EN BIT(5) +#define m_PACKET_VSI_EN BIT(4) +#define v_PACKET_GCP_EN(x) (((x) & 1) << 7) +#define v_PACKET_MSI_EN(x) (((x) & 1) << 6) +#define v_PACKET_SDI_EN(x) (((x) & 1) << 5) +#define v_PACKET_VSI_EN(x) (((x) & 1) << 4) + +#define HDMI_CONTROL_PACKET_BUF_INDEX 0x9f +enum { + INFOFRAME_VSI = 0x05, + INFOFRAME_AVI = 0x06, + INFOFRAME_AAI = 0x08, +}; + +enum drm_coxxector_status { + coxxector_status_discoxxected = 0, + coxxector_status_coxxected = 1, +}; + +#define HDMI_CONTROL_PACKET_ADDR 0xa0 +#define HDMI_MAXIMUM_INFO_FRAME_SIZE 0x11 +enum { + AVI_COLOR_MODE_RGB = 0, + AVI_COLOR_MODE_YCBCR422 = 1, + AVI_COLOR_MODE_YCBCR444 = 2, + AVI_COLORIMETRY_NO_DATA = 0, + + AVI_COLORIMETRY_SMPTE_170M = 1, + AVI_COLORIMETRY_ITU709 = 2, + AVI_COLORIMETRY_EXTENDED = 3, + + AVI_CODED_FRAME_ASPECT_NO_DATA = 0, + AVI_CODED_FRAME_ASPECT_4_3 = 1, + AVI_CODED_FRAME_ASPECT_16_9 = 2, + + ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08, + ACTIVE_ASPECT_RATE_4_3 = 0x09, + ACTIVE_ASPECT_RATE_16_9 = 0x0A, + ACTIVE_ASPECT_RATE_14_9 = 0x0B, +}; + +enum drm_connector_status { + connector_status_disconnected = 0, + connector_status_connected = 1, +}; + +#define HDMI_HDCP_CTRL 0x52 +#define m_HDMI_DVI BIT(1) +#define v_HDMI_DVI(x) ((x) << 1) + +#define HDMI_INTERRUPT_MASK1 0xc0 +#define HDMI_INTERRUPT_STATUS1 0xc1 +#define m_INT_ACTIVE_VSYNC BIT(5) +#define m_INT_EDID_READY BIT(2) + +#define HDMI_INTERRUPT_MASK2 0xc2 +#define HDMI_INTERRUPT_STATUS2 0xc3 +#define m_INT_HDCP_ERR BIT(7) +#define m_INT_BKSV_FLAG BIT(6) +#define m_INT_HDCP_OK BIT(4) + +#define HDMI_STATUS 0xc8 +#define m_HOTPLUG BIT(7) +#define m_MASK_INT_HOTPLUG BIT(5) +#define m_INT_HOTPLUG BIT(1) +#define v_MASK_INT_HOTPLUG(x) (((x) & 0x1) << 5) + +#define HDMI_COLORBAR 0xc9 + +#define HDMI_PHY_SYNC 0xce +#define HDMI_PHY_SYS_CTL 0xe0 +#define m_TMDS_CLK_SOURCE BIT(5) +#define v_TMDS_FROM_PLL (0 << 5) +#define v_TMDS_FROM_GEN BIT(5) +#define m_PHASE_CLK BIT(4) +#define v_DEFAULT_PHASE (0 << 4) +#define v_SYNC_PHASE BIT(4) +#define m_TMDS_CURRENT_PWR BIT(3) +#define v_TURN_ON_CURRENT (0 << 3) +#define v_CAT_OFF_CURRENT BIT(3) +#define m_BANDGAP_PWR BIT(2) +#define v_BANDGAP_PWR_UP (0 << 2) +#define v_BANDGAP_PWR_DOWN BIT(2) +#define m_PLL_PWR BIT(1) +#define v_PLL_PWR_UP (0 << 1) +#define v_PLL_PWR_DOWN BIT(1) +#define m_TMDS_CHG_PWR BIT(0) +#define v_TMDS_CHG_PWR_UP (0 << 0) +#define v_TMDS_CHG_PWR_DOWN BIT(0) + +#define HDMI_PHY_CHG_PWR 0xe1 +#define v_CLK_CHG_PWR(x) (((x) & 1) << 3) +#define v_DATA_CHG_PWR(x) (((x) & 7) << 0) + +#define HDMI_PHY_DRIVER 0xe2 +#define v_CLK_MAIN_DRIVER(x) ((x) << 4) +#define v_DATA_MAIN_DRIVER(x) ((x) << 0) + +#define HDMI_PHY_PRE_EMPHASIS 0xe3 +#define v_PRE_EMPHASIS(x) (((x) & 7) << 4) +#define v_CLK_PRE_DRIVER(x) (((x) & 3) << 2) +#define v_DATA_PRE_DRIVER(x) (((x) & 3) << 0) + +#define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW 0xe7 +#define v_FEEDBACK_DIV_LOW(x) (x) & 0xff +#define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH 0xe8 +#define v_FEEDBACK_DIV_HIGH(x) (x) & 1 + +#define HDMI_PHY_PRE_DIV_RATIO 0xed +#define v_PRE_DIV_RATIO(x) ((x) & 0x1f) + +#define HDMI_CEC_CTRL 0xd0 +#define m_ADJUST_FOR_HISENSE BIT(6) +#define m_REJECT_RX_BROADCAST BIT(5) +#define m_BUSFREETIME_ENABLE BIT(2) +#define m_REJECT_RX BIT(1) +#define m_START_TX BIT(0) + +#define HDMI_CEC_DATA 0xd1 +#define HDMI_CEC_TX_OFFSET 0xd2 +#define HDMI_CEC_RX_OFFSET 0xd3 +#define HDMI_CEC_CLK_H 0xd4 +#define HDMI_CEC_CLK_L 0xd5 +#define HDMI_CEC_TX_LENGTH 0xd6 +#define HDMI_CEC_RX_LENGTH 0xd7 +#define HDMI_CEC_TX_INT_MASK 0xd8 +#define m_TX_DONE BIT(3) +#define m_TX_NOACK BIT(2) +#define m_TX_BROADCAST_REJ BIT(1) +#define m_TX_BUSNOTFREE BIT(0) + +#define HDMI_CEC_RX_INT_MASK 0xd9 +#define m_RX_LA_ERR BIT(4) +#define m_RX_GLITCH BIT(3) +#define m_RX_DONE BIT(0) + +#define HDMI_CEC_TX_INT 0xda +#define HDMI_CEC_RX_INT 0xdb +#define HDMI_CEC_BUSFREETIME_L 0xdc +#define HDMI_CEC_BUSFREETIME_H 0xdd +#define HDMI_CEC_LOGICADDR 0xde + +#endif /* __INNO_HDMI_H__ */