From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 5 May 2019 02:29:42 +0200 Subject: drm/meson: Add support for the Meson8/8b/8m2 TranSwitch HDMI transmitter - WiP WiP Signed-off-by: Martin Blumenstingl --- drivers/gpu/drm/meson/Kconfig | 10 + drivers/gpu/drm/meson/Makefile | 1 + drivers/gpu/drm/meson/meson_transwitch_hdmi.c | 1537 ++++++++++ drivers/gpu/drm/meson/meson_transwitch_hdmi.h | 536 ++++ 4 files changed, 2084 insertions(+) diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig index 111111111111..222222222222 100644 --- a/drivers/gpu/drm/meson/Kconfig +++ b/drivers/gpu/drm/meson/Kconfig @@ -27,3 +27,13 @@ config DRM_MESON_DW_MIPI_DSI default y if DRM_MESON select DRM_DW_MIPI_DSI select GENERIC_PHY_MIPI_DPHY + +config DRM_MESON_TRANSWITCH_HDMI + tristate "Amlogic Meson8/8b/8m2 TranSwitch HDMI 1.4 Controller support" + depends on ARM || COMPILE_TEST + depends on DRM_MESON + default y if DRM_MESON + select DRM_DISPLAY_HDMI_HELPER + select DRM_DISPLAY_HELPER + select REGMAP_MMIO + select SND_SOC_HDMI_CODEC if SND_SOC diff --git a/drivers/gpu/drm/meson/Makefile b/drivers/gpu/drm/meson/Makefile index 111111111111..222222222222 100644 --- a/drivers/gpu/drm/meson/Makefile +++ b/drivers/gpu/drm/meson/Makefile @@ -7,3 +7,4 @@ meson-drm-y += meson_encoder_hdmi.o meson_encoder_dsi.o obj-$(CONFIG_DRM_MESON) += meson-drm.o obj-$(CONFIG_DRM_MESON_DW_HDMI) += meson_dw_hdmi.o obj-$(CONFIG_DRM_MESON_DW_MIPI_DSI) += meson_dw_mipi_dsi.o +obj-$(CONFIG_DRM_MESON_TRANSWITCH_HDMI) += meson_transwitch_hdmi.o diff --git a/drivers/gpu/drm/meson/meson_transwitch_hdmi.c b/drivers/gpu/drm/meson/meson_transwitch_hdmi.c new file mode 100644 index 000000000000..111111111111 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_transwitch_hdmi.c @@ -0,0 +1,1537 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Martin Blumenstingl + * + * All registers and magic values are taken from Amlogic's GPL kernel sources: + * Copyright (C) 2010 Amlogic, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "meson_transwitch_hdmi.h" + +#define HDMI_ADDR_PORT 0x0 +#define HDMI_DATA_PORT 0x4 +#define HDMI_CTRL_PORT 0x8 + #define HDMI_CTRL_PORT_APB3_ERR_EN BIT(15) + +struct meson_txc_hdmi { + struct device *dev; + + struct regmap *regmap; + + struct clk *pclk; + struct clk *sys_clk; + + struct phy *phy; + bool phy_is_on; + + struct mutex codec_mutex; + enum drm_connector_status last_connector_status; + hdmi_codec_plugged_cb codec_plugged_cb; + struct device *codec_dev; + + struct platform_device *hdmi_codec_pdev; + + struct drm_connector *current_connector; + + struct drm_bridge bridge; + struct drm_bridge *next_bridge; +}; + +#define bridge_to_meson_txc_hdmi(x) container_of(x, struct meson_txc_hdmi, bridge) + +static const struct regmap_range meson_txc_hdmi_regmap_ranges[] = { + regmap_reg_range(0x0000, 0x07ff), + regmap_reg_range(0x8000, 0x800c), +}; + +static const struct regmap_access_table meson_txc_hdmi_regmap_access = { + .yes_ranges = meson_txc_hdmi_regmap_ranges, + .n_yes_ranges = ARRAY_SIZE(meson_txc_hdmi_regmap_ranges), +}; + +static int meson_txc_hdmi_reg_read(void *context, unsigned int addr, + unsigned int *data) +{ + void __iomem *base = context; + + writel(addr, base + HDMI_ADDR_PORT); + writel(addr, base + HDMI_ADDR_PORT); + + *data = readl(base + HDMI_DATA_PORT); + + return 0; +} + +static int meson_txc_hdmi_reg_write(void *context, unsigned int addr, + unsigned int data) +{ + void __iomem *base = context; + + writel(addr, base + HDMI_ADDR_PORT); + writel(addr, base + HDMI_ADDR_PORT); + + writel(data, base + HDMI_DATA_PORT); + + return 0; +} + +static const struct regmap_config meson_txc_hdmi_regmap_config = { + .reg_bits = 16, + .val_bits = 16, + .reg_stride = 1, + .reg_read = meson_txc_hdmi_reg_read, + .reg_write = meson_txc_hdmi_reg_write, + .rd_table = &meson_txc_hdmi_regmap_access, + .wr_table = &meson_txc_hdmi_regmap_access, + .max_register = HDMI_OTHER_RX_PACKET_INTR_CLR, + .fast_io = true, +}; + +static void meson_txc_hdmi_write_infoframe(struct regmap *regmap, + unsigned int tx_pkt_reg, u8 *buf, + unsigned int len, bool enable) +{ + unsigned int i; + + /* Write the data bytes by starting at register offset 1 */ + for (i = HDMI_INFOFRAME_HEADER_SIZE; i < len; i++) + regmap_write(regmap, + tx_pkt_reg + i - HDMI_INFOFRAME_HEADER_SIZE + 1, + buf[i]); + + /* Zero all remaining data bytes */ + for (; i < 0x1c; i++) + regmap_write(regmap, tx_pkt_reg + i, 0x00); + + /* Write the header (which we skipped above) */ + regmap_write(regmap, tx_pkt_reg + 0x00, buf[3]); + regmap_write(regmap, tx_pkt_reg + 0x1c, buf[0]); + regmap_write(regmap, tx_pkt_reg + 0x1d, buf[1]); + regmap_write(regmap, tx_pkt_reg + 0x1e, buf[2]); + + regmap_write(regmap, tx_pkt_reg + 0x1f, enable ? 0xff : 0x00); +} + +static void meson_txc_hdmi_disable_infoframe(struct meson_txc_hdmi *priv, + unsigned int tx_pkt_reg) +{ + u8 buf[HDMI_INFOFRAME_HEADER_SIZE] = { 0 }; + + meson_txc_hdmi_write_infoframe(priv->regmap, tx_pkt_reg, buf, + HDMI_INFOFRAME_HEADER_SIZE, false); +} + +static void meson_txc_hdmi_sys5_reset_assert(struct meson_txc_hdmi *priv) +{ + /* A comment in the vendor driver says: bit5,6 is converted */ + regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_2, + TX_SYS5_TX_SOFT_RESET_2_HDMI_CH3_RST_IN | + TX_SYS5_TX_SOFT_RESET_2_HDMI_CH0_RST_IN); + usleep_range(10, 20); + + regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_2, + TX_SYS5_TX_SOFT_RESET_2_HDMI_CH2_RST_IN | + TX_SYS5_TX_SOFT_RESET_2_HDMI_CH1_RST_IN); + usleep_range(10, 20); + + regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_1, + TX_SYS5_TX_SOFT_RESET_1_TX_PIXEL_RSTN | + TX_SYS5_TX_SOFT_RESET_1_TX_TMDS_RSTN | + TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_MASTER_RSTN | + TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_RESAMPLE_RSTN | + TX_SYS5_TX_SOFT_RESET_1_TX_I2S_RESET_RSTN | + TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH2 | + TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH1 | + TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH0); + usleep_range(10, 20); +} + +static void meson_txc_hdmi_sys5_reset_deassert(struct meson_txc_hdmi *priv) +{ + /* Release the resets except tmds_clk */ + regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_1, + TX_SYS5_TX_SOFT_RESET_1_TX_TMDS_RSTN); + usleep_range(10, 20); + + /* Release the tmds_clk reset as well */ + regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_1, 0x0); + usleep_range(10, 20); + + regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_2, + TX_SYS5_TX_SOFT_RESET_2_HDMI_CH2_RST_IN | + TX_SYS5_TX_SOFT_RESET_2_HDMI_CH1_RST_IN | + TX_SYS5_TX_SOFT_RESET_2_HDMI_SR_RST); + usleep_range(10, 20); + + regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_2, + TX_SYS5_TX_SOFT_RESET_2_HDMI_CH2_RST_IN | + TX_SYS5_TX_SOFT_RESET_2_HDMI_CH1_RST_IN); + usleep_range(10, 20); +} + +static void meson_txc_hdmi_config_hdcp_registers(struct meson_txc_hdmi *priv) +{ + regmap_write(priv->regmap, TX_HDCP_CONFIG0, + FIELD_PREP(TX_HDCP_CONFIG0_ROM_ENCRYPT_OFF, 0x3)); + regmap_write(priv->regmap, TX_HDCP_MEM_CONFIG, 0x0); + regmap_write(priv->regmap, TX_HDCP_ENCRYPT_BYTE, 0x0); + + regmap_write(priv->regmap, TX_HDCP_MODE, TX_HDCP_MODE_CLEAR_AVMUTE); + + regmap_write(priv->regmap, TX_HDCP_MODE, TX_HDCP_MODE_ESS_CONFIG); +} + +static u8 meson_txc_hdmi_bus_fmt_to_color_depth(unsigned int bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_UYVY8_1X16: + /* 8 bit */ + return 0x0; + + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_UYVY10_1X20: + /* 10 bit */ + return 0x1; + + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_UYVY12_1X24: + /* 12 bit */ + return 0x2; + + case MEDIA_BUS_FMT_RGB161616_1X48: + case MEDIA_BUS_FMT_YUV16_1X48: + /* 16 bit */ + return 0x3; + + default: + /* unknown, default to 8 bit */ + return 0x0; + } +} + +static u8 meson_txc_hdmi_bus_fmt_to_color_format(unsigned int bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_YUV16_1X48: + /* Documented as YCbCr444 */ + return 0x1; + + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_UYVY12_1X24: + /* Documented as YCbCr422 */ + return 0x3; + + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_RGB161616_1X48: + default: + /* Documented as RGB444 */ + return 0x0; + } +} + +static void meson_txc_hdmi_config_color_space(struct meson_txc_hdmi *priv, + unsigned int input_bus_format, + unsigned int output_bus_format, + enum hdmi_quantization_range quant_range, + enum hdmi_colorimetry colorimetry) +{ + unsigned int regval; + + regmap_write(priv->regmap, TX_VIDEO_DTV_MODE, + FIELD_PREP(TX_VIDEO_DTV_MODE_COLOR_DEPTH, + meson_txc_hdmi_bus_fmt_to_color_depth(output_bus_format))); + + regmap_write(priv->regmap, TX_VIDEO_DTV_OPTION_L, + FIELD_PREP(TX_VIDEO_DTV_OPTION_L_OUTPUT_COLOR_FORMAT, + meson_txc_hdmi_bus_fmt_to_color_format(output_bus_format)) | + FIELD_PREP(TX_VIDEO_DTV_OPTION_L_INPUT_COLOR_FORMAT, + meson_txc_hdmi_bus_fmt_to_color_format(input_bus_format)) | + FIELD_PREP(TX_VIDEO_DTV_OPTION_L_OUTPUT_COLOR_DEPTH, + meson_txc_hdmi_bus_fmt_to_color_depth(output_bus_format)) | + FIELD_PREP(TX_VIDEO_DTV_OPTION_L_INPUT_COLOR_DEPTH, + meson_txc_hdmi_bus_fmt_to_color_depth(input_bus_format))); + + if (quant_range == HDMI_QUANTIZATION_RANGE_LIMITED) + regval = FIELD_PREP(TX_VIDEO_DTV_OPTION_H_OUTPUT_COLOR_RANGE, + TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_16_235) | + FIELD_PREP(TX_VIDEO_DTV_OPTION_H_INPUT_COLOR_RANGE, + TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_16_235); + else + regval = FIELD_PREP(TX_VIDEO_DTV_OPTION_H_OUTPUT_COLOR_RANGE, + TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_0_255) | + FIELD_PREP(TX_VIDEO_DTV_OPTION_H_INPUT_COLOR_RANGE, + TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_0_255); + + regmap_write(priv->regmap, TX_VIDEO_DTV_OPTION_H, regval); + + if (colorimetry == HDMI_COLORIMETRY_ITU_601) { + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_B0, 0x2f); + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_B1, 0x1d); + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_R0, 0x8b); + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_R1, 0x4c); + + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CB0, 0x18); + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CB1, 0x58); + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CR0, 0xd0); + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CR1, 0xb6); + } else { + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_B0, 0x7b); + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_B1, 0x12); + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_R0, 0x6c); + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_R1, 0x36); + + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CB0, 0xf2); + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CB1, 0x2f); + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CR0, 0xd4); + regmap_write(priv->regmap, TX_VIDEO_CSC_COEFF_CR1, 0x77); + } +} + +static void meson_txc_hdmi_config_serializer_clock(struct meson_txc_hdmi *priv, + enum hdmi_colorimetry colorimetry) +{ + /* Serializer Internal clock setting */ + if (colorimetry == HDMI_COLORIMETRY_ITU_601) + regmap_write(priv->regmap, TX_SYS1_PLL, 0x24); + else + regmap_write(priv->regmap, TX_SYS1_PLL, 0x22); + +#if 0 + // TODO: not ported yet + if ((param->VIC==HDMI_1080p60)&&(param->color_depth==COLOR_30BIT)&&(hdmi_rd_reg(0x018)==0x22)) { + regmap_write(priv->regmap, TX_SYS1_PLL, 0x12); + } +#endif +} + +static void meson_txc_hdmi_reconfig_packet_setting(struct meson_txc_hdmi *priv, + u8 cea_mode) +{ + u8 alloc_active2, alloc_eof1, alloc_sof1, alloc_sof2; + + regmap_write(priv->regmap, TX_PACKET_CONTROL_1, + FIELD_PREP(TX_PACKET_CONTROL_1_PACKET_START_LATENCY, 58)); + regmap_write(priv->regmap, TX_PACKET_CONTROL_2, + TX_PACKET_CONTROL_2_HORIZONTAL_GC_PACKET_TRANSPORT_EN); + + switch (cea_mode) { + case 31: + /* 1920x1080p50 */ + alloc_active2 = 0x12; + alloc_eof1 = 0x10; + alloc_sof1 = 0xb6; + alloc_sof2 = 0x11; + break; + case 93: + /* 3840x2160p24 */ + alloc_active2 = 0x12; + alloc_eof1 = 0x47; + alloc_sof1 = 0xf8; + alloc_sof2 = 0x52; + break; + case 94: + /* 3840x2160p25 */ + alloc_active2 = 0x12; + alloc_eof1 = 0x44; + alloc_sof1 = 0xda; + alloc_sof2 = 0x52; + break; + case 95: + /* 3840x2160p30 */ + alloc_active2 = 0x0f; + alloc_eof1 = 0x3a; + alloc_sof1 = 0x60; + alloc_sof2 = 0x52; + break; + case 98: + /* 4096x2160p24 */ + alloc_active2 = 0x12; + alloc_eof1 = 0x47; + alloc_sof1 = 0xf8; + alloc_sof2 = 0x52; + break; + default: + /* Disable the special packet settings only */ + regmap_write(priv->regmap, TX_PACKET_ALLOC_ACTIVE_1, 0x00); + return; + } + + /* + * The vendor driver says: manually configure these register to get + * stable video timings. + */ + regmap_write(priv->regmap, TX_PACKET_ALLOC_ACTIVE_1, 0x01); + regmap_write(priv->regmap, TX_PACKET_ALLOC_ACTIVE_2, alloc_active2); + regmap_write(priv->regmap, TX_PACKET_ALLOC_EOF_1, alloc_eof1); + regmap_write(priv->regmap, TX_PACKET_ALLOC_EOF_2, 0x12); + regmap_write(priv->regmap, TX_CORE_ALLOC_VSYNC_0, 0x01); + regmap_write(priv->regmap, TX_CORE_ALLOC_VSYNC_1, 0x00); + regmap_write(priv->regmap, TX_CORE_ALLOC_VSYNC_2, 0x0a); + regmap_write(priv->regmap, TX_PACKET_ALLOC_SOF_1, alloc_sof1); + regmap_write(priv->regmap, TX_PACKET_ALLOC_SOF_2, alloc_sof2); + regmap_update_bits(priv->regmap, TX_PACKET_CONTROL_1, + TX_PACKET_CONTROL_1_FORCE_PACKET_TIMING, + TX_PACKET_CONTROL_1_FORCE_PACKET_TIMING); +} + +static void meson_txc_hdmi_set_avi_infoframe(struct meson_txc_hdmi *priv, + struct drm_connector *conn, + const struct drm_display_mode *mode, + const struct drm_connector_state *conn_state, + unsigned int output_bus_format, + enum hdmi_quantization_range quant_range, + enum hdmi_colorimetry colorimetry) +{ + u8 buf[HDMI_INFOFRAME_SIZE(AVI)], *video_code; + struct hdmi_avi_infoframe frame; + int ret; + + ret = drm_hdmi_avi_infoframe_from_display_mode(&frame, conn, mode); + if (ret < 0) { + drm_err(priv->bridge.dev, + "Failed to setup AVI infoframe: %d\n", ret); + return; + } + + switch (output_bus_format) { + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_YUV16_1X48: + frame.colorspace = HDMI_COLORSPACE_YUV444; + break; + + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_UYVY12_1X24: + frame.colorspace = HDMI_COLORSPACE_YUV422; + break; + + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_RGB161616_1X48: + default: + frame.colorspace = HDMI_COLORSPACE_RGB; + break; + } + + drm_hdmi_avi_infoframe_colorimetry(&frame, conn_state); + drm_hdmi_avi_infoframe_quant_range(&frame, conn, mode, quant_range); + drm_hdmi_avi_infoframe_bars(&frame, conn_state); + + ret = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf)); + if (ret < 0) { + drm_err(priv->bridge.dev, + "Failed to pack AVI infoframe: %d\n", ret); + return; + } + + video_code = &buf[HDMI_INFOFRAME_HEADER_SIZE + 3]; + if (*video_code > 108) { + regmap_write(priv->regmap, TX_PKT_REG_EXCEPT0_BASE_ADDR, + *video_code); + *video_code = 0x00; + } else { + regmap_write(priv->regmap, TX_PKT_REG_EXCEPT0_BASE_ADDR, + 0x00); + } + + meson_txc_hdmi_write_infoframe(priv->regmap, + TX_PKT_REG_AVI_INFO_BASE_ADDR, buf, + sizeof(buf), true); +} + +static void meson_txc_hdmi_set_vendor_infoframe(struct meson_txc_hdmi *priv, + struct drm_connector *conn, + const struct drm_display_mode *mode) +{ + u8 buf[HDMI_INFOFRAME_HEADER_SIZE + 6]; + struct hdmi_vendor_infoframe frame; + int ret; + + ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame, conn, mode); + if (ret) { + drm_dbg(priv->bridge.dev, + "Failed to setup vendor infoframe: %d\n", ret); + return; + } + + ret = hdmi_vendor_infoframe_pack(&frame, buf, sizeof(buf)); + if (ret < 0) { + drm_err(priv->bridge.dev, + "Failed to pack vendor infoframe: %d\n", ret); + return; + } + + meson_txc_hdmi_write_infoframe(priv->regmap, + TX_PKT_REG_VEND_INFO_BASE_ADDR, buf, + sizeof(buf), true); +} + +static void meson_txc_hdmi_set_spd_infoframe(struct meson_txc_hdmi *priv) +{ + u8 buf[HDMI_INFOFRAME_SIZE(SPD)]; + struct hdmi_spd_infoframe frame; + int ret; + + ret = hdmi_spd_infoframe_init(&frame, "Amlogic", "Meson TXC HDMI"); + if (ret < 0) { + drm_err(priv->bridge.dev, + "Failed to setup SPD infoframe: %d\n", ret); + return; + } + + ret = hdmi_spd_infoframe_pack(&frame, buf, sizeof(buf)); + if (ret < 0) { + drm_err(priv->bridge.dev, + "Failed to pack SDP infoframe: %d\n", ret); + return; + } + + meson_txc_hdmi_write_infoframe(priv->regmap, + TX_PKT_REG_SPD_INFO_BASE_ADDR, buf, + sizeof(buf), true); +} + +static void meson_txc_hdmi_handle_plugged_change(struct meson_txc_hdmi *priv) +{ + bool plugged; + + plugged = priv->last_connector_status == connector_status_connected; + + if (priv->codec_dev && priv->codec_plugged_cb) + priv->codec_plugged_cb(priv->codec_dev, plugged); +} + +static int meson_txc_hdmi_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct meson_txc_hdmi *priv = bridge->driver_private; + + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { + drm_err(bridge->dev, + "DRM_BRIDGE_ATTACH_NO_CONNECTOR flag is not set but needed\n"); + return -EINVAL; + } + + return drm_bridge_attach(bridge->encoder, priv->next_bridge, bridge, + flags); +} + +/* Can return a maximum of 11 possible output formats for a mode/connector */ +#define MAX_OUTPUT_SEL_FORMATS 11 + +static u32 * +meson_txc_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts) +{ + struct drm_connector *conn = conn_state->connector; + struct drm_display_info *info = &conn->display_info; + u8 max_bpc = conn_state->max_requested_bpc; + unsigned int i = 0; + u32 *output_fmts; + + *num_output_fmts = 0; + + output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts), + GFP_KERNEL); + if (!output_fmts) + return NULL; + + /* If we are the only bridge, avoid negotiating with ourselves */ + if (list_is_singular(&bridge->encoder->bridge_chain)) { + *num_output_fmts = 1; + output_fmts[0] = MEDIA_BUS_FMT_FIXED; + + return output_fmts; + } + + /* + * Order bus formats from 16bit to 8bit and from YUV422 to RGB + * if supported. In any case the default RGB888 format is added + */ + + if (max_bpc >= 16 && info->bpc == 16) { + if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) + output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48; + + output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48; + } + + if (max_bpc >= 12 && info->bpc >= 12) { + if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) + output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24; + + if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) + output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36; + + output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36; + } + + if (max_bpc >= 10 && info->bpc >= 10) { + if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) + output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20; + + if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) + output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30; + + output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30; + } + + if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) + output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16; + + if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) + output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24; + + /* Default 8bit RGB fallback */ + output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; + + *num_output_fmts = i; + + return output_fmts; +} + +/* Can return a maximum of 3 possible input formats for an output format */ +#define MAX_INPUT_SEL_FORMATS 3 + +static u32 * +meson_txc_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + u32 *input_fmts; + unsigned int i = 0; + + *num_input_fmts = 0; + + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts), + GFP_KERNEL); + if (!input_fmts) + return NULL; + + switch (output_fmt) { + /* If MEDIA_BUS_FMT_FIXED is tested, return default bus format */ + case MEDIA_BUS_FMT_FIXED: + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; + break; + + /* 8bit */ + case MEDIA_BUS_FMT_RGB888_1X24: + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24; + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16; + break; + case MEDIA_BUS_FMT_YUV8_1X24: + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24; + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16; + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; + break; + case MEDIA_BUS_FMT_UYVY8_1X16: + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16; + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24; + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; + break; + + /* 10bit */ + case MEDIA_BUS_FMT_RGB101010_1X30: + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30; + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30; + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20; + break; + case MEDIA_BUS_FMT_YUV10_1X30: + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30; + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20; + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30; + break; + case MEDIA_BUS_FMT_UYVY10_1X20: + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20; + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30; + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30; + break; + + /* 12bit */ + case MEDIA_BUS_FMT_RGB121212_1X36: + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36; + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36; + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24; + break; + case MEDIA_BUS_FMT_YUV12_1X36: + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36; + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24; + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36; + break; + case MEDIA_BUS_FMT_UYVY12_1X24: + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24; + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36; + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36; + break; + + /* 16bit */ + case MEDIA_BUS_FMT_RGB161616_1X48: + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48; + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48; + break; + case MEDIA_BUS_FMT_YUV16_1X48: + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48; + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48; + break; + } + + *num_input_fmts = i; + + if (*num_input_fmts == 0) { + kfree(input_fmts); + input_fmts = NULL; + } + + return input_fmts; +} + +static void meson_txc_hdmi_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct meson_txc_hdmi *priv = bridge_to_meson_txc_hdmi(bridge); + struct drm_atomic_state *state = old_bridge_state->base.state; + enum hdmi_quantization_range quant_range; + struct drm_connector_state *conn_state; + struct drm_bridge_state *bridge_state; + const struct drm_display_mode *mode; + enum hdmi_colorimetry colorimetry; + struct drm_crtc_state *crtc_state; + struct drm_connector *connector; + unsigned int i; + u8 cea_mode; + + bridge_state = drm_atomic_get_new_bridge_state(state, bridge); + + connector = drm_atomic_get_new_connector_for_encoder(state, + bridge->encoder); + if (WARN_ON(!connector)) + return; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return; + + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (WARN_ON(!crtc_state)) + return; + + priv->current_connector = connector; + + mode = &crtc_state->adjusted_mode; + + cea_mode = drm_match_cea_mode(mode); + + if (connector->display_info.is_hdmi) { + quant_range = drm_default_rgb_quant_range(mode); + + switch (cea_mode) { + case 2 ... 3: + case 6 ... 7: + case 17 ... 18: + case 21 ... 22: + colorimetry = HDMI_COLORIMETRY_ITU_601; + break; + + default: + colorimetry = HDMI_COLORIMETRY_ITU_709; + break; + } + + meson_txc_hdmi_set_avi_infoframe(priv, connector, mode, + conn_state, + bridge_state->output_bus_cfg.format, + quant_range, colorimetry); + meson_txc_hdmi_set_vendor_infoframe(priv, connector, mode); + meson_txc_hdmi_set_spd_infoframe(priv); + } else { + quant_range = HDMI_QUANTIZATION_RANGE_FULL; + colorimetry = HDMI_COLORIMETRY_NONE; + } + + meson_txc_hdmi_sys5_reset_assert(priv); + + meson_txc_hdmi_config_hdcp_registers(priv); + + if (cea_mode == 39) + regmap_write(priv->regmap, TX_VIDEO_DTV_TIMING, 0x0); + else + regmap_write(priv->regmap, TX_VIDEO_DTV_TIMING, + TX_VIDEO_DTV_TIMING_DISABLE_VIC39_CORRECTION); + + regmap_write(priv->regmap, TX_CORE_DATA_CAPTURE_2, + TX_CORE_DATA_CAPTURE_2_INTERNAL_PACKET_ENABLE); + regmap_write(priv->regmap, TX_CORE_DATA_MONITOR_1, + TX_CORE_DATA_MONITOR_1_LANE0 | + FIELD_PREP(TX_CORE_DATA_MONITOR_1_SELECT_LANE0, 0x7)); + regmap_write(priv->regmap, TX_CORE_DATA_MONITOR_2, + FIELD_PREP(TX_CORE_DATA_MONITOR_2_MONITOR_SELECT, 0x2)); + + if (connector->display_info.is_hdmi) + regmap_write(priv->regmap, TX_TMDS_MODE, + TX_TMDS_MODE_FORCED_HDMI | + TX_TMDS_MODE_HDMI_CONFIG); + else + regmap_write(priv->regmap, TX_TMDS_MODE, + TX_TMDS_MODE_FORCED_HDMI); + + regmap_write(priv->regmap, TX_SYS4_CONNECT_SEL_1, 0x0); + + /* + * Set tmds_clk pattern to be "0000011111" before being sent to AFE + * clock channel. + */ + regmap_write(priv->regmap, TX_SYS4_CK_INV_VIDEO, + TX_SYS4_CK_INV_VIDEO_TMDS_CLK_PATTERN); + + regmap_write(priv->regmap, TX_SYS5_FIFO_CONFIG, + TX_SYS5_FIFO_CONFIG_CLK_CHANNEL3_OUTPUT_ENABLE | + TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL2_ENABLE | + TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL1_ENABLE | + TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL0_ENABLE); + + meson_txc_hdmi_config_color_space(priv, + bridge_state->input_bus_cfg.format, + bridge_state->output_bus_cfg.format, + quant_range, colorimetry); + + meson_txc_hdmi_sys5_reset_deassert(priv); + + meson_txc_hdmi_config_serializer_clock(priv, colorimetry); + meson_txc_hdmi_reconfig_packet_setting(priv, cea_mode); + + /* all resets need to be applied twice */ + for (i = 0; i < 2; i++) { + regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_1, + TX_SYS5_TX_SOFT_RESET_1_TX_PIXEL_RSTN | + TX_SYS5_TX_SOFT_RESET_1_TX_TMDS_RSTN | + TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_MASTER_RSTN | + TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_RESAMPLE_RSTN | + TX_SYS5_TX_SOFT_RESET_1_TX_I2S_RESET_RSTN | + TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH2 | + TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH1 | + TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH0); + regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_2, + TX_SYS5_TX_SOFT_RESET_2_HDMI_CH3_RST_IN | + TX_SYS5_TX_SOFT_RESET_2_HDMI_CH2_RST_IN | + TX_SYS5_TX_SOFT_RESET_2_HDMI_CH1_RST_IN | + TX_SYS5_TX_SOFT_RESET_2_HDMI_CH0_RST_IN | + TX_SYS5_TX_SOFT_RESET_2_HDMI_SR_RST | + TX_SYS5_TX_SOFT_RESET_2_TX_DDC_HDCP_RSTN | + TX_SYS5_TX_SOFT_RESET_2_TX_DDC_EDID_RSTN | + TX_SYS5_TX_SOFT_RESET_2_TX_DIG_RESET_N_CH3); + usleep_range(5000, 10000); + regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_1, 0x00); + regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_2, 0x00); + usleep_range(5000, 10000); + } + + if (!priv->phy_is_on) { + int ret; + + ret = phy_power_on(priv->phy); + if (ret) + drm_err(bridge->dev, "Failed to turn on PHY\n"); + else + priv->phy_is_on = true; + } +} + +static void meson_txc_hdmi_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct meson_txc_hdmi *priv = bridge_to_meson_txc_hdmi(bridge); + + priv->current_connector = NULL; + + if (priv->phy_is_on) { + int ret; + + ret = phy_power_off(priv->phy); + if (ret) + drm_err(bridge->dev, "Failed to turn off PHY\n"); + else + priv->phy_is_on = false; + } + + meson_txc_hdmi_disable_infoframe(priv, TX_PKT_REG_AUDIO_INFO_BASE_ADDR); + meson_txc_hdmi_disable_infoframe(priv, TX_PKT_REG_AVI_INFO_BASE_ADDR); + meson_txc_hdmi_disable_infoframe(priv, TX_PKT_REG_EXCEPT0_BASE_ADDR); + meson_txc_hdmi_disable_infoframe(priv, TX_PKT_REG_VEND_INFO_BASE_ADDR); +} + +static enum drm_mode_status +meson_txc_hdmi_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static enum drm_connector_status meson_txc_hdmi_bridge_detect(struct drm_bridge *bridge) +{ + struct meson_txc_hdmi *priv = bridge_to_meson_txc_hdmi(bridge); + enum drm_connector_status status; + unsigned int val; + + regmap_read(priv->regmap, TX_HDCP_ST_EDID_STATUS, &val); + if (val & TX_HDCP_ST_EDID_STATUS_HPD_STATUS) + status = connector_status_connected; + else + status = connector_status_disconnected; + + mutex_lock(&priv->codec_mutex); + if (priv->last_connector_status != status) { + priv->last_connector_status = status; + meson_txc_hdmi_handle_plugged_change(priv); + } + mutex_unlock(&priv->codec_mutex); + + return status; +} + +static int meson_txc_hdmi_get_edid_block(void *data, u8 *buf, unsigned int block, + size_t len) +{ + unsigned int i, regval, start = block * EDID_LENGTH; + struct meson_txc_hdmi *priv = data; + int ret; + + /* Start the DDC transaction */ + regmap_update_bits(priv->regmap, TX_HDCP_EDID_CONFIG, + TX_HDCP_EDID_CONFIG_SYS_TRIGGER_CONFIG, 0); + regmap_update_bits(priv->regmap, TX_HDCP_EDID_CONFIG, + TX_HDCP_EDID_CONFIG_SYS_TRIGGER_CONFIG, + TX_HDCP_EDID_CONFIG_SYS_TRIGGER_CONFIG); + + ret = regmap_read_poll_timeout(priv->regmap, + TX_HDCP_ST_EDID_STATUS, + regval, + (regval & TX_HDCP_ST_EDID_STATUS_EDID_DATA_READY), + 1000, 200000); + + regmap_update_bits(priv->regmap, TX_HDCP_EDID_CONFIG, + TX_HDCP_EDID_CONFIG_SYS_TRIGGER_CONFIG, 0); + + if (ret) + return ret; + + for (i = 0; i < len; i++) { + regmap_read(priv->regmap, TX_RX_EDID_OFFSET + start + i, + ®val); + buf[i] = regval; + } + + return 0; +} + +static const struct drm_edid *meson_txc_hdmi_bridge_edid_read(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct meson_txc_hdmi *priv = bridge_to_meson_txc_hdmi(bridge); + const struct drm_edid *drm_edid; + + drm_edid = drm_edid_read_custom(connector, + meson_txc_hdmi_get_edid_block, priv); + if (!drm_edid) { + drm_dbg(priv->bridge.dev, "Failed to get EDID\n"); + return NULL; + } + + return drm_edid; +} + +static const struct drm_bridge_funcs meson_txc_hdmi_bridge_funcs = { + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .attach = meson_txc_hdmi_bridge_attach, + .atomic_get_output_bus_fmts = meson_txc_hdmi_bridge_atomic_get_output_bus_fmts, + .atomic_get_input_bus_fmts = meson_txc_hdmi_bridge_atomic_get_input_bus_fmts, + .atomic_enable = meson_txc_hdmi_bridge_atomic_enable, + .atomic_disable = meson_txc_hdmi_bridge_atomic_disable, + .mode_valid = meson_txc_hdmi_bridge_mode_valid, + .detect = meson_txc_hdmi_bridge_detect, + .edid_read = meson_txc_hdmi_bridge_edid_read, +}; + +static int meson_txc_hdmi_hw_init(struct meson_txc_hdmi *priv) +{ + unsigned long ddc_i2c_bus_clk_hz = 500 * 1000; + unsigned long sys_clk_hz = 24 * 1000 * 1000; + int ret; + + ret = phy_init(priv->phy); + if (ret) { + dev_err(priv->dev, "Failed to initialize the PHY: %d\n", ret); + return ret; + } + + ret = clk_set_rate(priv->sys_clk, sys_clk_hz); + if (ret) { + dev_err(priv->dev, "Failed to set HDMI system clock to 24MHz\n"); + goto err_phy_exit; + } + + ret = clk_prepare_enable(priv->sys_clk); + if (ret) { + dev_err(priv->dev, "Failed to enable the sys clk\n"); + goto err_phy_exit; + } + + regmap_update_bits(priv->regmap, HDMI_OTHER_CTRL1, + HDMI_OTHER_CTRL1_POWER_ON, + HDMI_OTHER_CTRL1_POWER_ON); + + regmap_write(priv->regmap, TX_HDMI_PHY_CONFIG0, + TX_HDMI_PHY_CONFIG0_HDMI_COMMON_B7_B0); + + regmap_write(priv->regmap, TX_HDCP_MODE, 0x40); + + /* + * The vendor driver comments that this is a setting for "Band-gap and + * main-bias". 0x1d = power-up, 0x00 = power-down. + */ + regmap_write(priv->regmap, TX_SYS1_AFE_TEST, 0x1d); + + meson_txc_hdmi_config_serializer_clock(priv, HDMI_COLORIMETRY_NONE); + + /* + * The vendor driver has a comment with the following information for + * the magic value: + * bit[2:0]=011: CK channel output TMDS CLOCK + * bit[2:0]=101, ck channel output PHYCLCK + */ + regmap_write(priv->regmap, TX_SYS1_AFE_CONNECT, 0xfb); + + /* Termination resistor calib value */ + regmap_write(priv->regmap, TX_CORE_CALIB_VALUE, 0x0f); + + /* HPD glitch filter */ + regmap_write(priv->regmap, TX_HDCP_HPD_FILTER_L, 0xa0); + regmap_write(priv->regmap, TX_HDCP_HPD_FILTER_H, 0xa0); + + /* Disable MEM power-down */ + regmap_write(priv->regmap, TX_MEM_PD_REG0, 0x0); + + regmap_write(priv->regmap, TX_HDCP_CONFIG3, + FIELD_PREP(TX_HDCP_CONFIG3_DDC_I2C_BUS_CLOCK_TIME_DIVIDER, + (sys_clk_hz / ddc_i2c_bus_clk_hz) - 1)); + + /* Enable software controlled DDC transaction */ + regmap_write(priv->regmap, TX_HDCP_EDID_CONFIG, + TX_HDCP_EDID_CONFIG_FORCED_MEM_COPY_DONE | + TX_HDCP_EDID_CONFIG_MEM_COPY_DONE_CONFIG); + regmap_write(priv->regmap, TX_CORE_EDID_CONFIG_MORE, + TX_CORE_EDID_CONFIG_MORE_SYS_TRIGGER_CONFIG_SEMI_MANU); + + /* mask (= disable) all interrupts */ + regmap_write(priv->regmap, HDMI_OTHER_INTR_MASKN, 0x0); + + /* clear any pending interrupt */ + regmap_write(priv->regmap, HDMI_OTHER_INTR_STAT_CLR, + HDMI_OTHER_INTR_STAT_CLR_EDID_RISING | + HDMI_OTHER_INTR_STAT_CLR_HPD_FALLING | + HDMI_OTHER_INTR_STAT_CLR_HPD_RISING); + + return 0; + +err_phy_exit: + phy_exit(priv->phy); + return 0; +} + +static void meson_txc_hdmi_hw_exit(struct meson_txc_hdmi *priv) +{ + int ret; + + /* mask (= disable) all interrupts */ + regmap_write(priv->regmap, HDMI_OTHER_INTR_MASKN, + HDMI_OTHER_INTR_MASKN_TX_EDID_INT_RISE | + HDMI_OTHER_INTR_MASKN_TX_HPD_INT_FALL | + HDMI_OTHER_INTR_MASKN_TX_HPD_INT_RISE); + + regmap_update_bits(priv->regmap, HDMI_OTHER_CTRL1, + HDMI_OTHER_CTRL1_POWER_ON, 0); + + clk_disable_unprepare(priv->sys_clk); + + ret = phy_exit(priv->phy); + if (ret) + dev_err(priv->dev, "Failed to exit the PHY: %d\n", ret); +} + +static u32 meson_txc_hdmi_hdmi_codec_calc_audio_n(struct hdmi_codec_params *hparms) +{ + u32 audio_n; + + if ((hparms->sample_rate % 44100) == 0) + audio_n = (128 * hparms->sample_rate) / 900; + else + audio_n = (128 * hparms->sample_rate) / 1000; + + if (hparms->cea.coding_type == HDMI_AUDIO_CODING_TYPE_EAC3 || + hparms->cea.coding_type == HDMI_AUDIO_CODING_TYPE_DTS_HD) + audio_n *= 4; + + return audio_n; +} + +static u8 meson_txc_hdmi_hdmi_codec_coding_type(struct hdmi_codec_params *hparms) +{ + switch (hparms->cea.coding_type) { + case HDMI_AUDIO_CODING_TYPE_MLP: + return TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_HBR_AUDIO_PACKET; + case HDMI_AUDIO_CODING_TYPE_DSD: + return TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_ONE_BIT_AUDIO; + case HDMI_AUDIO_CODING_TYPE_DST: + return TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_DST_AUDIO_PACKET; + default: + return TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_AUDIO_SAMPLE_PACKET; + } +} + +static int meson_txc_hdmi_hdmi_codec_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *fmt, + struct hdmi_codec_params *hparms) +{ + struct meson_txc_hdmi *priv = dev_get_drvdata(dev); + u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)]; + u16 audio_tx_format; + u32 audio_n; + int len, i; + + if (hparms->cea.coding_type == HDMI_AUDIO_CODING_TYPE_MLP) { + /* + * TODO: fixed CTS is not supported yet, it needs special + * TX_SYS1_ACR_N_* settings + */ + return -EINVAL; + } + + switch (hparms->sample_width) { + case 16: + audio_tx_format = FIELD_PREP(TX_AUDIO_FORMAT_BIT_WIDTH_MASK, + TX_AUDIO_FORMAT_BIT_WIDTH_16); + break; + + case 20: + audio_tx_format = FIELD_PREP(TX_AUDIO_FORMAT_BIT_WIDTH_MASK, + TX_AUDIO_FORMAT_BIT_WIDTH_20); + break; + + case 24: + audio_tx_format = FIELD_PREP(TX_AUDIO_FORMAT_BIT_WIDTH_MASK, + TX_AUDIO_FORMAT_BIT_WIDTH_24); + break; + + default: + return -EINVAL; + } + + switch (fmt->fmt) { + case HDMI_I2S: + regmap_update_bits(priv->regmap, HDMI_OTHER_CTRL1, + HDMI_OTHER_CTRL1_HDMI_AUDIO_CLOCK_ON, + HDMI_OTHER_CTRL1_HDMI_AUDIO_CLOCK_ON); + + audio_tx_format |= TX_AUDIO_FORMAT_SPDIF_OR_I2S | + TX_AUDIO_FORMAT_I2S_ONE_BIT_OR_I2S | + FIELD_PREP(TX_AUDIO_FORMAT_I2S_FORMAT, 0x2); + + if (hparms->channels > 2) + audio_tx_format |= TX_AUDIO_FORMAT_I2S_2_OR_8_CH; + + regmap_write(priv->regmap, TX_AUDIO_FORMAT, audio_tx_format); + + regmap_write(priv->regmap, TX_AUDIO_I2S, TX_AUDIO_I2S_ENABLE); + regmap_write(priv->regmap, TX_AUDIO_SPDIF, 0x0); + break; + + case HDMI_SPDIF: + regmap_update_bits(priv->regmap, HDMI_OTHER_CTRL1, + HDMI_OTHER_CTRL1_HDMI_AUDIO_CLOCK_ON, 0x0); + + if (hparms->cea.coding_type == HDMI_AUDIO_CODING_TYPE_STREAM) + audio_tx_format |= TX_AUDIO_FORMAT_SPDIF_CHANNEL_STATUS_FROM_DATA_OR_REG; + + regmap_write(priv->regmap, TX_AUDIO_FORMAT, audio_tx_format); + + regmap_write(priv->regmap, TX_AUDIO_I2S, 0x0); + regmap_write(priv->regmap, TX_AUDIO_SPDIF, TX_AUDIO_SPDIF_ENABLE); + break; + + default: + return -EINVAL; + } + + if (hparms->channels > 2) + regmap_write(priv->regmap, TX_AUDIO_HEADER, + TX_AUDIO_HEADER_AUDIO_SAMPLE_PACKET_HEADER_LAYOUT1); + else + regmap_write(priv->regmap, TX_AUDIO_HEADER, 0x0); + + regmap_write(priv->regmap, TX_AUDIO_SAMPLE, + FIELD_PREP(TX_AUDIO_SAMPLE_CHANNEL_VALID, + BIT(hparms->channels) - 1)); + + audio_n = meson_txc_hdmi_hdmi_codec_calc_audio_n(hparms); + + regmap_write(priv->regmap, TX_SYS1_ACR_N_0, + FIELD_PREP(TX_SYS1_ACR_N_0_N_BYTE0, + (audio_n >> 0) & 0xff)); + regmap_write(priv->regmap, TX_SYS1_ACR_N_1, + FIELD_PREP(TX_SYS1_ACR_N_1_N_BYTE1, + (audio_n >> 8) & 0xff)); + regmap_update_bits(priv->regmap, TX_SYS1_ACR_N_2, + TX_SYS1_ACR_N_2_N_UPPER_NIBBLE, + FIELD_PREP(TX_SYS1_ACR_N_2_N_UPPER_NIBBLE, + (audio_n >> 16) & 0xf)); + + regmap_write(priv->regmap, TX_SYS0_ACR_CTS_0, 0x0); + regmap_write(priv->regmap, TX_SYS0_ACR_CTS_1, 0x0); + regmap_write(priv->regmap, TX_SYS0_ACR_CTS_2, + TX_SYS0_ACR_CTS_2_FORCE_ARC_STABLE); + + regmap_write(priv->regmap, TX_AUDIO_CONTROL, + TX_AUDIO_CONTROL_AUTO_AUDIO_FIFO_CLEAR | + FIELD_PREP(TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_MASK, + meson_txc_hdmi_hdmi_codec_coding_type(hparms)) | + TX_AUDIO_CONTROL_AUDIO_SAMPLE_PACKET_FLAT); + + len = hdmi_audio_infoframe_pack(&hparms->cea, buf, sizeof(buf)); + if (len < 0) + return len; + + meson_txc_hdmi_write_infoframe(priv->regmap, + TX_PKT_REG_AUDIO_INFO_BASE_ADDR, + buf, len, true); + + for (i = 0; i < ARRAY_SIZE(hparms->iec.status); i++) { + unsigned char sub1, sub2; + + sub1 = sub2 = hparms->iec.status[i]; + + if (i == 2) { + sub1 |= FIELD_PREP(IEC958_AES2_CON_CHANNEL, 1); + sub2 |= FIELD_PREP(IEC958_AES2_CON_CHANNEL, 2); + } + + regmap_write(priv->regmap, TX_IEC60958_SUB1_OFFSET + i, sub1); + regmap_write(priv->regmap, TX_IEC60958_SUB2_OFFSET + i, sub2); + } + + return 0; +} + +static int meson_txc_hdmi_hdmi_codec_audio_startup(struct device *dev, + void *data) +{ + struct meson_txc_hdmi *priv = dev_get_drvdata(dev); + + regmap_update_bits(priv->regmap, TX_PACKET_CONTROL_2, + TX_PACKET_CONTROL_2_AUDIO_REQUEST_DISABLE, 0x0); + + /* reset audio master and sample */ + regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_1, + TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_RESAMPLE_RSTN | + TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_MASTER_RSTN); + regmap_write(priv->regmap, TX_SYS5_TX_SOFT_RESET_1, 0x0); + + regmap_write(priv->regmap, TX_AUDIO_CONTROL_MORE, + TX_AUDIO_CONTROL_MORE_ENABLE); + + regmap_write(priv->regmap, TX_AUDIO_FIFO, + FIELD_PREP(TX_AUDIO_FIFO_FIFO_DEPTH_MASK, + TX_AUDIO_FIFO_FIFO_DEPTH_512) | + FIELD_PREP(TX_AUDIO_FIFO_CRITICAL_THRESHOLD_MASK, + TX_AUDIO_FIFO_CRITICAL_THRESHOLD_DEPTH_DIV16) | + FIELD_PREP(TX_AUDIO_FIFO_NORMAL_THRESHOLD_MASK, + TX_AUDIO_FIFO_NORMAL_THRESHOLD_DEPTH_DIV8)); + + regmap_write(priv->regmap, TX_AUDIO_LIPSYNC, 0x0); + + regmap_write(priv->regmap, TX_SYS1_ACR_N_2, + FIELD_PREP(TX_SYS1_ACR_N_2_N_MEAS_TOLERANCE, 0x3)); + + return 0; +} + +static void meson_txc_hdmi_hdmi_codec_audio_shutdown(struct device *dev, + void *data) +{ + struct meson_txc_hdmi *priv = dev_get_drvdata(dev); + + meson_txc_hdmi_disable_infoframe(priv, TX_PKT_REG_AUDIO_INFO_BASE_ADDR); + + regmap_write(priv->regmap, TX_AUDIO_CONTROL_MORE, 0x0); + regmap_update_bits(priv->regmap, HDMI_OTHER_CTRL1, + HDMI_OTHER_CTRL1_HDMI_AUDIO_CLOCK_ON, 0x0); + + regmap_update_bits(priv->regmap, TX_PACKET_CONTROL_2, + TX_PACKET_CONTROL_2_AUDIO_REQUEST_DISABLE, + TX_PACKET_CONTROL_2_AUDIO_REQUEST_DISABLE); +} + +static int meson_txc_hdmi_hdmi_codec_mute_stream(struct device *dev, + void *data, bool enable, + int direction) +{ + struct meson_txc_hdmi *priv = dev_get_drvdata(dev); + + regmap_write(priv->regmap, TX_AUDIO_PACK, + enable ? 0 : TX_AUDIO_PACK_AUDIO_SAMPLE_PACKETS_ENABLE); + + return 0; +} + +static int meson_txc_hdmi_hdmi_codec_get_eld(struct device *dev, void *data, + uint8_t *buf, size_t len) +{ + struct meson_txc_hdmi *priv = dev_get_drvdata(dev); + + if (priv->current_connector) + memcpy(buf, priv->current_connector->eld, + min_t(size_t, MAX_ELD_BYTES, len)); + else + memset(buf, 0, len); + + return 0; +} + +static int meson_txc_hdmi_hdmi_codec_get_dai_id(struct snd_soc_component *component, + struct device_node *endpoint) +{ + struct of_endpoint of_ep; + int ret; + + ret = of_graph_parse_endpoint(endpoint, &of_ep); + if (ret < 0) + return ret; + + /* + * HDMI sound should be located as reg = <2> + * Then, it is sound port 0 + */ + if (of_ep.port == 2) + return 0; + + return -EINVAL; +} + +static int meson_txc_hdmi_hdmi_codec_hook_plugged_cb(struct device *dev, + void *data, + hdmi_codec_plugged_cb fn, + struct device *codec_dev) +{ + struct meson_txc_hdmi *priv = dev_get_drvdata(dev); + + mutex_lock(&priv->codec_mutex); + priv->codec_plugged_cb = fn; + priv->codec_dev = codec_dev; + meson_txc_hdmi_handle_plugged_change(priv); + mutex_unlock(&priv->codec_mutex); + + return 0; +} + +static struct hdmi_codec_ops meson_txc_hdmi_hdmi_codec_ops = { + .hw_params = meson_txc_hdmi_hdmi_codec_hw_params, + .audio_startup = meson_txc_hdmi_hdmi_codec_audio_startup, + .audio_shutdown = meson_txc_hdmi_hdmi_codec_audio_shutdown, + .mute_stream = meson_txc_hdmi_hdmi_codec_mute_stream, + .get_eld = meson_txc_hdmi_hdmi_codec_get_eld, + .get_dai_id = meson_txc_hdmi_hdmi_codec_get_dai_id, + .hook_plugged_cb = meson_txc_hdmi_hdmi_codec_hook_plugged_cb, +}; + +static const struct hdmi_codec_pdata meson_txc_hdmi_codec_pdata = { + .ops = &meson_txc_hdmi_hdmi_codec_ops, + .i2s = 1, + .spdif = 1, + .max_i2s_channels = 8, +}; + +static int meson_txc_hdmi_codec_init(struct meson_txc_hdmi *priv) +{ + priv->hdmi_codec_pdev = platform_device_register_data(priv->dev, + HDMI_CODEC_DRV_NAME, + PLATFORM_DEVID_AUTO, + &meson_txc_hdmi_codec_pdata, + sizeof(meson_txc_hdmi_codec_pdata)); + return PTR_ERR_OR_ZERO(priv->hdmi_codec_pdev); +} + +static int meson_txc_hdmi_probe(struct platform_device *pdev) +{ + struct device_node *endpoint, *remote; + struct device *dev = &pdev->dev; + struct meson_txc_hdmi *priv; + void __iomem *base; + u32 regval; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + + mutex_init(&priv->codec_mutex); + + platform_set_drvdata(pdev, priv); + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init(dev, NULL, base, + &meson_txc_hdmi_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), + "Failed to get the pclk\n"); + + priv->sys_clk = devm_clk_get(dev, "sys"); + if (IS_ERR(priv->sys_clk)) + return dev_err_probe(dev, PTR_ERR(priv->sys_clk), + "Failed to get the sys clock\n"); + + priv->phy = devm_phy_get(dev, "hdmi"); + if (IS_ERR(priv->phy)) + return dev_err_probe(dev, PTR_ERR(priv->phy), + "Failed to get the HDMI PHY\n"); + + endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1); + if (!endpoint) + return dev_err_probe(dev, -ENODEV, + "Missing endpoint in port@1\n"); + + remote = of_graph_get_remote_port_parent(endpoint); + of_node_put(endpoint); + if (!remote) + return dev_err_probe(dev, -ENODEV, + "Endpoint in port@1 unconnected\n"); + + if (!of_device_is_available(remote)) { + of_node_put(remote); + return dev_err_probe(dev, -ENODEV, + "port@1 remote device is disabled\n"); + } + + priv->next_bridge = of_drm_find_bridge(remote); + of_node_put(remote); + if (!priv->next_bridge) + return -EPROBE_DEFER; + + ret = clk_prepare_enable(priv->pclk); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable the pclk\n"); + + regval = readl(base + HDMI_CTRL_PORT); + regval |= HDMI_CTRL_PORT_APB3_ERR_EN; + writel(regval, base + HDMI_CTRL_PORT); + + ret = meson_txc_hdmi_hw_init(priv); + if (ret) + goto err_disable_clk; + + ret = meson_txc_hdmi_codec_init(priv); + if (ret) + goto err_hw_exit; + + priv->bridge.driver_private = priv; + priv->bridge.funcs = &meson_txc_hdmi_bridge_funcs; + priv->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID; + priv->bridge.of_node = dev->of_node; + priv->bridge.interlace_allowed = true; + + drm_bridge_add(&priv->bridge); + + return 0; + +err_hw_exit: + meson_txc_hdmi_hw_exit(priv); +err_disable_clk: + clk_disable_unprepare(priv->pclk); + return ret; +} + +static void meson_txc_hdmi_remove(struct platform_device *pdev) +{ + struct meson_txc_hdmi *priv = platform_get_drvdata(pdev); + + platform_device_unregister(priv->hdmi_codec_pdev); + + drm_bridge_remove(&priv->bridge); + + meson_txc_hdmi_hw_exit(priv); + + clk_disable_unprepare(priv->pclk); +} + +static const struct of_device_id meson_txc_hdmi_of_table[] = { + { .compatible = "amlogic,meson8-hdmi-tx" }, + { .compatible = "amlogic,meson8b-hdmi-tx" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, meson_txc_hdmi_of_table); + +static struct platform_driver meson_txc_hdmi_platform_driver = { + .probe = meson_txc_hdmi_probe, + .remove_new = meson_txc_hdmi_remove, + .driver = { + .name = "meson-transwitch-hdmi", + .of_match_table = meson_txc_hdmi_of_table, + }, +}; +module_platform_driver(meson_txc_hdmi_platform_driver); + +MODULE_AUTHOR("Martin Blumenstingl "); +MODULE_DESCRIPTION("Amlogic Meson8 and Meson8b TranSwitch HDMI 1.4 TX driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/meson/meson_transwitch_hdmi.h b/drivers/gpu/drm/meson/meson_transwitch_hdmi.h new file mode 100644 index 000000000000..111111111111 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_transwitch_hdmi.h @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Martin Blumenstingl + * + * All registers and magic values are taken from Amlogic's GPL kernel sources: + * Copyright (C) 2010 Amlogic, Inc. + */ + +#include +#include + +#ifndef __MESON_TRANSWITCH_HDMI_H__ +#define __MESON_TRANSWITCH_HDMI_H__ + +/* HDMI TX register */ + +// System config 0 +#define TX_SYS0_AFE_SIGNAL 0x0000 +#define TX_SYS0_AFE_LOOP 0x0001 +#define TX_SYS0_ACR_CTS_0 0x0002 + #define TX_SYS0_ACR_CTS_0_AUDIO_CTS_BYTE0 GENMASK(7, 0) +#define TX_SYS0_ACR_CTS_1 0x0003 + #define TX_SYS0_ACR_CTS_1_AUDIO_CTS_BYTE1 GENMASK(7, 0) +#define TX_SYS0_ACR_CTS_2 0x0004 + #define TX_SYS0_ACR_CTS_2_FORCE_ARC_STABLE BIT(5) +#define TX_SYS0_BIST_CONTROL 0x0005 + #define TX_SYS0_BIST_CONTROL_AFE_BIST_ENABLE BIT(7) + #define TX_SYS0_BIST_CONTROL_TMDS_SHIFT_PATTERN_SELECT BIT(6) + #define TX_SYS0_BIST_CONTROL_TMDS_PRBS_PATTERN_SELECT GENMASK(5, 4) + #define TX_SYS0_BIST_CONTROL_TMDS_REPEAT_BIST_PATTERN GENMASK(2, 0) + +#define TX_SYS0_BIST_DATA_0 0x0006 +#define TX_SYS0_BIST_DATA_1 0x0007 +#define TX_SYS0_BIST_DATA_2 0x0008 +#define TX_SYS0_BIST_DATA_3 0x0009 +#define TX_SYS0_BIST_DATA_4 0x000A +#define TX_SYS0_BIST_DATA_5 0x000B +#define TX_SYS0_BIST_DATA_6 0x000C +#define TX_SYS0_BIST_DATA_7 0x000D +#define TX_SYS0_BIST_DATA_8 0x000E +#define TX_SYS0_BIST_DATA_9 0x000F + +// system config 1 +#define TX_HDMI_PHY_CONFIG0 0x0010 + #define TX_HDMI_PHY_CONFIG0_HDMI_COMMON_B7_B0 GENMASK(7, 0) +#define TX_HDMI_PHY_CONFIG1 0x0010 + #define TX_HDMI_PHY_CONFIG1_HDMI_COMMON_B11_B8 GENMASK(3, 0) + #define TX_HDMI_PHY_CONFIG1_HDMI_CTL_REG_B3_B0 GENMASK(7, 4) +#define TX_HDMI_PHY_CONFIG2 0x0012 + #define TX_HDMI_PHY_CONFIG_HDMI_CTL_REG_B11_B4 GENMASK(7, 0) +#define TX_HDMI_PHY_CONFIG3 0x0013 + #define TX_HDMI_PHY_CONFIG3_HDMI_L2H_CTL GENMASK(3, 0) + #define TX_HDMI_PHY_CONFIG3_HDMI_MDR_PU GENMASK(7, 4) +#define TX_HDMI_PHY_CONFIG4 0x0014 + #define TX_HDMI_PHY_CONFIG4_HDMI_LF_PD BIT(0) + #define TX_HDMI_PHY_CONFIG4_HDMI_PHY_CLK_EN BIT(1) + #define TX_HDMI_PHY_CONFIG4_HDMI_MODE GENMASK(3, 2) + #define TX_HDMI_PHY_CONFIG4_HDMI_MODE_NORMAL 0x0 + #define TX_HDMI_PHY_CONFIG4_HDMI_MODE_CLK_CH3_EQUAL_CH0 0x1 + #define TX_HDMI_PHY_CONFIG4_HDMI_MODE_ALTERNATE_HIGH_LOW 0x2 + #define TX_HDMI_PHY_CONFIG4_HDMI_MODE_ALTERNATE_LOW_HIGH 0x3 + #define TX_HDMI_PHY_CONFIG4_HDMI_PREM_CTL GENMASK(7, 4) +#define TX_HDMI_PHY_CONFIG5 0x0015 + #define TX_HDMI_PHY_CONFIG5_HDMI_VCM_CTL GENMASK(7, 5) + #define TX_HDMI_PHY_CONFIG5_HDMI_PREFCTL GENMASK(2, 0) +#define TX_HDMI_PHY_CONFIG6 0x0016 + #define TX_HDMI_PHY_CONFIG6_HDMI_RTERM_CTL GENMASK(3, 0) + #define TX_HDMI_PHY_CONFIG6_HDMI_SWING_CTL GENMASK(7, 4) +#define TX_SYS1_AFE_TEST 0x0017 +#define TX_SYS1_PLL 0x0018 +#define TX_SYS1_TUNE 0x0019 +#define TX_SYS1_AFE_CONNECT 0x001A + +#define TX_SYS1_ACR_N_0 0x001C + #define TX_SYS1_ACR_N_0_N_BYTE0 GENMASK(7, 0) +#define TX_SYS1_ACR_N_1 0x001D + #define TX_SYS1_ACR_N_1_N_BYTE1 GENMASK(7, 0) +#define TX_SYS1_ACR_N_2 0x001E + #define TX_SYS1_ACR_N_2_N_MEAS_TOLERANCE GENMASK(7, 4) + #define TX_SYS1_ACR_N_2_N_UPPER_NIBBLE GENMASK(3, 0) +#define TX_SYS1_PRBS_DATA 0x001F + #define TX_SYS1_PRBS_DATA_PRBS_MODE GENMASK(1, 0) + #define TX_SYS1_PRBS_DATA_PRBS_MODE_11 0x0 + #define TX_SYS1_PRBS_DATA_PRBS_MODE_15 0x1 + #define TX_SYS1_PRBS_DATA_PRBS_MODE_7 0x2 + #define TX_SYS1_PRBS_DATA_PRBS_MODE_31 0x3 + +// HDCP CONFIG +#define TX_HDCP_ECC_CONFIG 0x0024 +#define TX_HDCP_CRC_CONFIG 0x0025 +#define TX_HDCP_EDID_CONFIG 0x0026 + #define TX_HDCP_EDID_CONFIG_FORCED_SYS_TRIGGER BIT(7) + #define TX_HDCP_EDID_CONFIG_SYS_TRIGGER_CONFIG BIT(6) + #define TX_HDCP_EDID_CONFIG_MEM_ACC_SEQ_MODE BIT(5) + #define TX_HDCP_EDID_CONFIG_MEM_ACC_SEQ_START BIT(4) + #define TX_HDCP_EDID_CONFIG_FORCED_MEM_COPY_DONE BIT(3) + #define TX_HDCP_EDID_CONFIG_MEM_COPY_DONE_CONFIG BIT(2) + #define TX_HDCP_EDID_CONFIG_SYS_TRIGGER_CONFIG_SEMI_MANU BIT(1) + +#define TX_HDCP_MEM_CONFIG 0x0027 + #define TX_HDCP_MEM_CONFIG_READ_DECRYPT BIT(3) + +#define TX_HDCP_HPD_FILTER_L 0x0028 +#define TX_HDCP_HPD_FILTER_H 0x0029 +#define TX_HDCP_ENCRYPT_BYTE 0x002A +#define TX_HDCP_CONFIG0 0x002B + #define TX_HDCP_CONFIG0_ROM_ENCRYPT_OFF GENMASK(4, 3) + +#define TX_HDCP_CONFIG1 0x002C +#define TX_HDCP_CONFIG2 0x002D +#define TX_HDCP_CONFIG3 0x002E + #define TX_HDCP_CONFIG3_DDC_I2C_BUS_CLOCK_TIME_DIVIDER GENMASK(7, 0) + +#define TX_HDCP_MODE 0x002F + #define TX_HDCP_MODE_CP_DESIRED BIT(7) + #define TX_HDCP_MODE_ESS_CONFIG BIT(6) + #define TX_HDCP_MODE_SET_AVMUTE BIT(5) + #define TX_HDCP_MODE_CLEAR_AVMUTE BIT(4) + #define TX_HDCP_MODE_HDCP_1_1 BIT(3) + #define TX_HDCP_MODE_VSYNC_HSYNC_FORCED_POLARITY_SELECT BIT(2) + #define TX_HDCP_MODE_FORCED_VSYNC_POLARITY BIT(1) + #define TX_HDCP_MODE_FORCED_HSYNC_POLARITY BIT(0) + +// Video config, part 1 +#define TX_VIDEO_ACTIVE_PIXELS_0 0x0030 +#define TX_VIDEO_ACTIVE_PIXELS_1 0x0031 +#define TX_VIDEO_FRONT_PIXELS 0x0032 +#define TX_VIDEO_HSYNC_PIXELS 0x0033 +#define TX_VIDEO_BACK_PIXELS 0x0034 +#define TX_VIDEO_ACTIVE_LINES_0 0x0035 +#define TX_VIDEO_ACTIVE_LINES_1 0x0036 +#define TX_VIDEO_EOF_LINES 0x0037 +#define TX_VIDEO_VSYNC_LINES 0x0038 +#define TX_VIDEO_SOF_LINES 0x0039 +#define TX_VIDEO_DTV_TIMING 0x003A + #define TX_VIDEO_DTV_TIMING_FORCE_DTV_TIMING_AUTO BIT(7) + #define TX_VIDEO_DTV_TIMING_FORCE_VIDEO_SCAN BIT(6) + #define TX_VIDEO_DTV_TIMING_FORCE_VIDEO_FIELD BIT(5) + #define TX_VIDEO_DTV_TIMING_DISABLE_VIC39_CORRECTION BIT(4) + +#define TX_VIDEO_DTV_MODE 0x003B + #define TX_VIDEO_DTV_MODE_FORCED_DEFAULT_PHASE BIT(7) + #define TX_VIDEO_DTV_MODE_COLOR_DEPTH GENMASK(1, 0) + +#define TX_VIDEO_DTV_FORMAT0 0x003C +#define TX_VIDEO_DTV_FORMAT1 0x003D +#define TX_VIDEO_PIXEL_PACK 0x003F +// video config, part 2 +#define TX_VIDEO_CSC_COEFF_B0 0x0040 +#define TX_VIDEO_CSC_COEFF_B1 0x0041 +#define TX_VIDEO_CSC_COEFF_R0 0x0042 +#define TX_VIDEO_CSC_COEFF_R1 0x0043 +#define TX_VIDEO_CSC_COEFF_CB0 0x0044 +#define TX_VIDEO_CSC_COEFF_CB1 0x0045 +#define TX_VIDEO_CSC_COEFF_CR0 0x0046 +#define TX_VIDEO_CSC_COEFF_CR1 0x0047 +#define TX_VIDEO_DTV_OPTION_L 0x0048 + #define TX_VIDEO_DTV_OPTION_L_OUTPUT_COLOR_FORMAT GENMASK(7, 6) + #define TX_VIDEO_DTV_OPTION_L_INPUT_COLOR_FORMAT GENMASK(5, 4) + #define TX_VIDEO_DTV_OPTION_L_OUTPUT_COLOR_DEPTH GENMASK(3, 2) + #define TX_VIDEO_DTV_OPTION_L_INPUT_COLOR_DEPTH GENMASK(1, 0) + +#define TX_VIDEO_DTV_OPTION_H 0x0049 + #define TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_16_235 0x0 + #define TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_16_240 0x1 + #define TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_1_254 0x2 + #define TX_VIDEO_DTV_OPTION_H_COLOR_RANGE_0_255 0x3 + #define TX_VIDEO_DTV_OPTION_H_OUTPUT_COLOR_RANGE GENMASK(3, 2) + #define TX_VIDEO_DTV_OPTION_H_INPUT_COLOR_RANGE GENMASK(1, 0) + +#define TX_VIDEO_DTV_FILTER 0x004A +#define TX_VIDEO_DTV_DITHER 0x004B +#define TX_VIDEO_DTV_DEDITHER 0x004C +#define TX_VIDEO_PROC_CONFIG0 0x004E +#define TX_VIDEO_PROC_CONFIG1 0x004F + +// Audio config +#define TX_AUDIO_FORMAT 0x0058 + #define TX_AUDIO_FORMAT_SPDIF_OR_I2S BIT(7) + #define TX_AUDIO_FORMAT_I2S_2_OR_8_CH BIT(6) + #define TX_AUDIO_FORMAT_I2S_FORMAT GENMASK(5, 4) + #define TX_AUDIO_FORMAT_BIT_WIDTH_MASK GENMASK(3, 2) + #define TX_AUDIO_FORMAT_BIT_WIDTH_16 0x1 + #define TX_AUDIO_FORMAT_BIT_WIDTH_20 0x2 + #define TX_AUDIO_FORMAT_BIT_WIDTH_24 0x3 + #define TX_AUDIO_FORMAT_WS_POLARITY BIT(1) + #define TX_AUDIO_FORMAT_I2S_ONE_BIT_OR_I2S BIT(0) + #define TX_AUDIO_FORMAT_SPDIF_CHANNEL_STATUS_FROM_DATA_OR_REG BIT(0) + +#define TX_AUDIO_SPDIF 0x0059 + #define TX_AUDIO_SPDIF_ENABLE BIT(0) +#define TX_AUDIO_I2S 0x005A + #define TX_AUDIO_I2S_ENABLE BIT(0) +#define TX_AUDIO_FIFO 0x005B + #define TX_AUDIO_FIFO_FIFO_DEPTH_MASK GENMASK(7, 4) + #define TX_AUDIO_FIFO_FIFO_DEPTH_512 0x4 + #define TX_AUDIO_FIFO_CRITICAL_THRESHOLD_MASK GENMASK(3, 2) + #define TX_AUDIO_FIFO_CRITICAL_THRESHOLD_DEPTH_DIV16 0x2 + #define TX_AUDIO_FIFO_NORMAL_THRESHOLD_MASK GENMASK(1, 0) + #define TX_AUDIO_FIFO_NORMAL_THRESHOLD_DEPTH_DIV8 0x1 +#define TX_AUDIO_LIPSYNC 0x005C + #define TX_AUDIO_LIPSYNC_NORMALIZED_LIPSYNC_PARAM GENMASK(7, 0) +#define TX_AUDIO_CONTROL 0x005D + #define TX_AUDIO_CONTROL_FORCED_AUDIO_FIFO_CLEAR BIT(7) + #define TX_AUDIO_CONTROL_AUTO_AUDIO_FIFO_CLEAR BIT(6) + #define TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_MASK GENMASK(5, 4) + #define TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_AUDIO_SAMPLE_PACKET 0x0 + #define TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_ONE_BIT_AUDIO 0x1 + #define TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_HBR_AUDIO_PACKET 0x2 + #define TX_AUDIO_CONTROL_AUDIO_PACKET_TYPE_DST_AUDIO_PACKET 0x3 + #define TX_AUDIO_CONTROL_AUDIO_SAMPLE_PACKET_VALID BIT(2) + #define TX_AUDIO_CONTROL_AUDIO_SAMPLE_PACKET_USER BIT(1) + #define TX_AUDIO_CONTROL_AUDIO_SAMPLE_PACKET_FLAT BIT(0) +#define TX_AUDIO_HEADER 0x005E + #define TX_AUDIO_HEADER_AUDIO_SAMPLE_PACKET_HEADER_LAYOUT1 BIT(7) + #define TX_AUDIO_HEADER_SET_NORMAL_DOUBLE_IN_DST_PACKET_HEADER BIT(6) +#define TX_AUDIO_SAMPLE 0x005F + #define TX_AUDIO_SAMPLE_CHANNEL_VALID GENMASK(7, 0) +#define TX_AUDIO_VALID 0x0060 +#define TX_AUDIO_USER 0x0061 +#define TX_AUDIO_PACK 0x0062 + #define TX_AUDIO_PACK_AUDIO_SAMPLE_PACKETS_ENABLE BIT(0) +#define TX_AUDIO_CONTROL_MORE 0x0064 + #define TX_AUDIO_CONTROL_MORE_ENABLE BIT(0) + +// tmds config +#define TX_TMDS_MODE 0x0068 + #define TX_TMDS_MODE_FORCED_HDMI BIT(7) + #define TX_TMDS_MODE_HDMI_CONFIG BIT(6) + #define TX_TMDS_MODE_BIT_SWAP BIT(3) + #define TX_TMDS_MODE_CHANNEL_SWAP GENMASK(2, 0) + +#define TX_TMDS_CONFIG0 0x006C +#define TX_TMDS_CONFIG1 0x006D + +// packet config +#define TX_PACKET_ALLOC_ACTIVE_1 0x0078 +#define TX_PACKET_ALLOC_ACTIVE_2 0x0079 +#define TX_PACKET_ALLOC_EOF_1 0x007A +#define TX_PACKET_ALLOC_EOF_2 0x007B +#define TX_PACKET_ALLOC_SOF_1 0x007C +#define TX_PACKET_ALLOC_SOF_2 0x007D +#define TX_PACKET_CONTROL_1 0x007E + #define TX_PACKET_CONTROL_1_FORCE_PACKET_TIMING BIT(7) + #define TX_PACKET_CONTROL_1_PACKET_ALLOC_MODE BIT(6) + #define TX_PACKET_CONTROL_1_PACKET_START_LATENCY GENMASK(5, 0) + +#define TX_PACKET_CONTROL_2 0x007F + #define TX_PACKET_CONTROL_2_AUDIO_REQUEST_DISABLE BIT(3) + #define TX_PACKET_CONTROL_2_HORIZONTAL_GC_PACKET_TRANSPORT_EN BIT(1) + +#define TX_CORE_EDID_CONFIG_MORE 0x0080 + #define TX_CORE_EDID_CONFIG_MORE_KEEP_EDID_ERROR BIT(1) + #define TX_CORE_EDID_CONFIG_MORE_SYS_TRIGGER_CONFIG_SEMI_MANU BIT(0) + +#define TX_CORE_ALLOC_VSYNC_0 0x0081 +#define TX_CORE_ALLOC_VSYNC_1 0x0082 +#define TX_CORE_ALLOC_VSYNC_2 0x0083 +#define TX_MEM_PD_REG0 0x0084 + +// core config +#define TX_CORE_DATA_CAPTURE_1 0x00F0 +#define TX_CORE_DATA_CAPTURE_2 0x00F1 + #define TX_CORE_DATA_CAPTURE_2_AUDIO_SOURCE_SELECT GENMASK(7, 6) + #define TX_CORE_DATA_CAPTURE_2_EXTERNAL_PACKET_ENABLE BIT(5) + #define TX_CORE_DATA_CAPTURE_2_INTERNAL_PACKET_ENABLE BIT(4) + #define TX_CORE_DATA_CAPTURE_2_AFE_FIFO_SRC_LANE1 GENMASK(3, 2) + #define TX_CORE_DATA_CAPTURE_2_AFE_FIFO_SRC_LANE0 GENMASK(1, 0) + +#define TX_CORE_DATA_MONITOR_1 0x00F2 + #define TX_CORE_DATA_MONITOR_1_LANE1 BIT(7) + #define TX_CORE_DATA_MONITOR_1_SELECT_LANE1 GENMASK(6, 4) + #define TX_CORE_DATA_MONITOR_1_LANE0 BIT(3) + #define TX_CORE_DATA_MONITOR_1_SELECT_LANE0 GENMASK(2, 0) + +#define TX_CORE_DATA_MONITOR_2 0x00F3 + #define TX_CORE_DATA_MONITOR_2_MONITOR_SELECT GENMASK(2, 0) + +#define TX_CORE_CALIB_MODE 0x00F4 +#define TX_CORE_CALIB_SAMPLE_DELAY 0x00F5 +#define TX_CORE_CALIB_VALUE_AUTO 0x00F6 +#define TX_CORE_CALIB_VALUE 0x00F7 + +// system config 4 +#define TX_SYS4_TX_CKI_DDR 0x00A0 +#define TX_SYS4_TX_CKO_DDR 0x00A1 +#define TX_SYS4_RX_CKI_DDR 0x00A2 +#define TX_SYS4_RX_CKO_DDR 0x00A3 +#define TX_SYS4_CONNECT_SEL_0 0x00A4 +#define TX_SYS4_CONNECT_SEL_1 0x00A5 + #define TX_SYS4_CONNECT_SEL_1_TX_CONNECT_SEL_UPPER_CHANNEL_DATA BIT(6) + +#define TX_SYS4_CONNECT_SEL_2 0x00A6 +#define TX_SYS4_CONNECT_SEL_3 0x00A7 +#define TX_SYS4_CK_INV_VIDEO 0x00A8 + #define TX_SYS4_CK_INV_VIDEO_TMDS_CLK_PATTERN BIT(4) +#define TX_SYS4_CK_INV_AUDIO 0x00A9 +#define TX_SYS4_CK_INV_AFE 0x00AA +#define TX_SYS4_CK_INV_CH01 0x00AB +#define TX_SYS4_CK_INV_CH2 0x00AC +#define TX_SYS4_CK_CEC 0x00AD +#define TX_SYS4_CK_SOURCE_1 0x00AE +#define TX_SYS4_CK_SOURCE_2 0x00AF + +#define TX_IEC60958_SUB1_OFFSET 0x00B0 +#define TX_IEC60958_SUB2_OFFSET 0x00C8 + +// system config 5 +#define TX_SYS5_TX_SOFT_RESET_1 0x00E0 + #define TX_SYS5_TX_SOFT_RESET_1_TX_PIXEL_RSTN BIT(7) + #define TX_SYS5_TX_SOFT_RESET_1_TX_TMDS_RSTN BIT(6) + #define TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_MASTER_RSTN BIT(5) + #define TX_SYS5_TX_SOFT_RESET_1_TX_AUDIO_RESAMPLE_RSTN BIT(4) + #define TX_SYS5_TX_SOFT_RESET_1_TX_I2S_RESET_RSTN BIT(3) + #define TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH2 BIT(2) + #define TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH1 BIT(1) + #define TX_SYS5_TX_SOFT_RESET_1_TX_DIG_RESET_N_CH0 BIT(0) + +#define TX_SYS5_TX_SOFT_RESET_2 0x00E1 + #define TX_SYS5_TX_SOFT_RESET_2_HDMI_CH3_RST_IN BIT(7) + #define TX_SYS5_TX_SOFT_RESET_2_HDMI_CH2_RST_IN BIT(6) + #define TX_SYS5_TX_SOFT_RESET_2_HDMI_CH1_RST_IN BIT(5) + #define TX_SYS5_TX_SOFT_RESET_2_HDMI_CH0_RST_IN BIT(4) + #define TX_SYS5_TX_SOFT_RESET_2_HDMI_SR_RST BIT(3) + #define TX_SYS5_TX_SOFT_RESET_2_TX_DDC_HDCP_RSTN BIT(2) + #define TX_SYS5_TX_SOFT_RESET_2_TX_DDC_EDID_RSTN BIT(1) + #define TX_SYS5_TX_SOFT_RESET_2_TX_DIG_RESET_N_CH3 BIT(0) + +#define TX_SYS5_RX_SOFT_RESET_1 0x00E2 +#define TX_SYS5_RX_SOFT_RESET_2 0x00E3 +#define TX_SYS5_RX_SOFT_RESET_3 0x00E4 +#define TX_SYS5_SSTL_BIDIR_IN 0x00E5 +#define TX_SYS5_SSTL_IN 0x00E6 +#define TX_SYS5_SSTL_DIFF_IN 0x00E7 +#define TX_SYS5_FIFO_CONFIG 0x00E8 + #define TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL2_BYPASS BIT(6) + #define TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL1_BYPASS BIT(5) + #define TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL0_BYPASS BIT(4) + #define TX_SYS5_FIFO_CONFIG_CLK_CHANNEL3_OUTPUT_ENABLE BIT(3) + #define TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL2_ENABLE BIT(2) + #define TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL1_ENABLE BIT(1) + #define TX_SYS5_FIFO_CONFIG_AFE_FIFO_CHANNEL0_ENABLE BIT(0) + +#define TX_SYS5_FIFO_SAMP01_CFG 0x00E9 +#define TX_SYS5_FIFO_SAMP23_CFG 0x00EA +#define TX_SYS5_CONNECT_FIFO_CFG 0x00EB +#define TX_SYS5_IO_CALIB_CONTROL 0x00EC +#define TX_SYS5_SSTL_BIDIR_OUT 0x00ED +#define TX_SYS5_SSTL_OUT 0x00EE +#define TX_SYS5_SSTL_DIFF_OUT 0x00EF + +// HDCP shadow register +#define TX_HDCP_SHW_BKSV_0 0x0100 +#define TX_HDCP_SHW_BKSV_1 0x0101 +#define TX_HDCP_SHW_BKSV_2 0x0102 +#define TX_HDCP_SHW_BKSV_3 0x0103 +#define TX_HDCP_SHW_BKSV_4 0x0104 +#define TX_HDCP_SHW_RI1_0 0x0108 +#define TX_HDCP_SHW_RI1_1 0x0109 +#define TX_HDCP_SHW_PJ1 0x010A +#define TX_HDCP_SHW_AKSV_0 0x0110 +#define TX_HDCP_SHW_AKSV_1 0x0111 +#define TX_HDCP_SHW_AKSV_2 0x0112 +#define TX_HDCP_SHW_AKSV_3 0x0113 +#define TX_HDCP_SHW_AKSV_4 0x0114 +#define TX_HDCP_SHW_AINFO 0x0115 +#define TX_HDCP_SHW_AN_0 0x0118 +#define TX_HDCP_SHW_AN_1 0x0119 +#define TX_HDCP_SHW_AN_2 0x011A +#define TX_HDCP_SHW_AN_3 0x011B +#define TX_HDCP_SHW_AN_4 0x011C +#define TX_HDCP_SHW_AN_5 0x011D +#define TX_HDCP_SHW_AN_6 0x011E +#define TX_HDCP_SHW_AN_7 0x011F +#define TX_HDCP_SHW_V1_H0_0 0x0120 +#define TX_HDCP_SHW_V1_H0_1 0x0121 +#define TX_HDCP_SHW_V1_H0_2 0x0122 +#define TX_HDCP_SHW_V1_H0_3 0x0123 +#define TX_HDCP_SHW_V1_H1_0 0x0124 +#define TX_HDCP_SHW_V1_H1_1 0x0125 +#define TX_HDCP_SHW_V1_H1_2 0x0126 +#define TX_HDCP_SHW_V1_H1_3 0x0127 +#define TX_HDCP_SHW_V1_H2_0 0x0128 +#define TX_HDCP_SHW_V1_H2_1 0x0129 +#define TX_HDCP_SHW_V1_H2_2 0x012A +#define TX_HDCP_SHW_V1_H2_3 0x012B +#define TX_HDCP_SHW_V1_H3_0 0x012C +#define TX_HDCP_SHW_V1_H3_1 0x012D +#define TX_HDCP_SHW_V1_H3_2 0x012E +#define TX_HDCP_SHW_V1_H3_3 0x012F +#define TX_HDCP_SHW_V1_H4_0 0x0130 +#define TX_HDCP_SHW_V1_H4_1 0x0131 +#define TX_HDCP_SHW_V1_H4_2 0x0132 +#define TX_HDCP_SHW_V1_H4_3 0x0133 +#define TX_HDCP_SHW_BCAPS 0x0140 +#define TX_HDCP_SHW_BSTATUS_0 0x0141 +#define TX_HDCP_SHW_BSTATUS_1 0x0142 +#define TX_HDCP_SHW_KSV_FIFO 0x0143 + +// system status 0 +#define TX_SYSST0_CONNECT_FIFO 0x0180 +#define TX_SYSST0_PLL_MONITOR 0x0181 +#define TX_SYSST0_AFE_FIFO 0x0182 +#define TX_SYSST0_ROM_STATUS 0x018F + +// hdcp status +#define TX_HDCP_ST_AUTHENTICATION 0x0190 +#define TX_HDCP_ST_FRAME_COUNT 0x0191 +#define TX_HDCP_ST_STATUS_0 0x0192 +#define TX_HDCP_ST_STATUS_1 0x0193 +#define TX_HDCP_ST_STATUS_2 0x0194 +#define TX_HDCP_ST_STATUS_3 0x0195 +#define TX_HDCP_ST_EDID_STATUS 0x0196 + #define TX_HDCP_ST_EDID_STATUS_SYSTEM_STATUS GENMASK(7, 6) + #define TX_HDCP_ST_EDID_STATUS_SYSTEM_STATUS_NO_SINK_ATTACHED 0x0 + #define TX_HDCP_ST_EDID_STATUS_SYSTEM_STATUS_READING_EDID 0x1 + #define TX_HDCP_ST_EDID_STATUS_SYSTEM_STATUS_DVI_MODE 0x2 + #define TX_HDCP_ST_EDID_STATUS_SYSTEM_STATUS_HDMI_MODE 0x3 + #define TX_HDCP_ST_EDID_STATUS_EDID_DATA_READY BIT(4) + #define TX_HDCP_ST_EDID_STATUS_HPD_STATUS BIT(1) + +#define TX_HDCP_ST_MEM_STATUS 0x0197 +#define TX_HDCP_ST_ST_MODE 0x019F + +// video status +#define TX_VIDEO_ST_ACTIVE_PIXELS_1 0x01A0 +#define TX_VIDEO_ST_ACTIVE_PIXELS_2 0x01A1 +#define TX_VIDEO_ST_FRONT_PIXELS 0x01A2 +#define TX_VIDEO_ST_HSYNC_PIXELS 0x01A3 +#define TX_VIDEO_ST_BACK_PIXELS 0x01A4 +#define TX_VIDEO_ST_ACTIVE_LINES_1 0x01A5 +#define TX_VIDEO_ST_ACTIVE_LINES_2 0x01A6 +#define TX_VIDEO_ST_EOF_LINES 0x01A7 +#define TX_VIDEO_ST_VSYNC_LINES 0x01A8 +#define TX_VIDEO_ST_SOF_LINES 0x01A9 +#define TX_VIDEO_ST_DTV_TIMING 0x01AA +#define TX_VIDEO_ST_DTV_MODE 0x01AB +// audio status +#define TX_VIDEO_ST_AUDIO_STATUS 0x01AC +#define TX_AFE_STATUS_0 0x01AE +#define TX_AFE_STATUS_1 0x01AF + +#define TX_IEC60958_ST_SUB1_OFFSET 0x01B0 +#define TX_IEC60958_ST_SUB2_OFFSET 0x01C8 + +// system status 1 +#define TX_SYSST1_CALIB_BIT_RESULT_0 0x01E0 +#define TX_SYSST1_CALIB_BIT_RESULT_1 0x01E1 +//HDMI_STATUS_OUT[7:0] +#define TX_HDMI_PHY_READBACK_0 0x01E2 +//HDMI_COMP_OUT[4] +//HDMI_STATUS_OUT[11:8] +#define TX_HDMI_PHY_READBACK_1 0x01E3 +#define TX_SYSST1_CALIB_BIT_RESULT_4 0x01E4 +#define TX_SYSST1_CALIB_BIT_RESULT_5 0x01E5 +#define TX_SYSST1_CALIB_BIT_RESULT_6 0x01E6 +#define TX_SYSST1_CALIB_BIT_RESULT_7 0x01E7 +#define TX_SYSST1_CALIB_BUS_RESULT_0 0x01E8 +#define TX_SYSST1_CALIB_BUS_RESULT_1 0x01E9 +#define TX_SYSST1_CALIB_BUS_RESULT_2 0x01EA +#define TX_SYSST1_CALIB_BUS_RESULT_3 0x01EB +#define TX_SYSST1_CALIB_BUS_RESULT_4 0x01EC +#define TX_SYSST1_CALIB_BUS_RESULT_5 0x01ED +#define TX_SYSST1_CALIB_BUS_RESULT_6 0x01EE +#define TX_SYSST1_CALIB_BUS_RESULT_7 0x01EF + +// Packet status +#define TX_PACKET_ST_REQUEST_STATUS_1 0x01F0 +#define TX_PACKET_ST_REQUEST_STATUS_2 0x01F1 +#define TX_PACKET_ST_REQUEST_MISSED_1 0x01F2 +#define TX_PACKET_ST_REQUEST_MISSED_2 0x01F3 +#define TX_PACKET_ST_ENCODE_STATUS_0 0x01F4 +#define TX_PACKET_ST_ENCODE_STATUS_1 0x01F5 +#define TX_PACKET_ST_ENCODE_STATUS_2 0x01F6 +#define TX_PACKET_ST_TIMER_STATUS 0x01F7 + +// tmds status +#define TX_TMDS_ST_CLOCK_METER_1 0x01F8 +#define TX_TMDS_ST_CLOCK_METER_2 0x01F9 +#define TX_TMDS_ST_CLOCK_METER_3 0x01FA +#define TX_TMDS_ST_TMDS_STATUS_1 0x01FC +#define TX_TMDS_ST_TMDS_STATUS_2 0x01FD +#define TX_TMDS_ST_TMDS_STATUS_3 0x01FE +#define TX_TMDS_ST_TMDS_STATUS_4 0x01FF + +// Packet register +#define TX_PKT_REG_SPD_INFO_BASE_ADDR 0x0200 +#define TX_PKT_REG_VEND_INFO_BASE_ADDR 0x0220 +#define TX_PKT_REG_MPEG_INFO_BASE_ADDR 0x0240 +#define TX_PKT_REG_AVI_INFO_BASE_ADDR 0x0260 +#define TX_PKT_REG_AUDIO_INFO_BASE_ADDR 0x0280 +#define TX_PKT_REG_ACP_INFO_BASE_ADDR 0x02A0 +#define TX_PKT_REG_ISRC1_BASE_ADDR 0x02C0 +#define TX_PKT_REG_ISRC2_BASE_ADDR 0x02E0 +#define TX_PKT_REG_EXCEPT0_BASE_ADDR 0x0300 +#define TX_PKT_REG_EXCEPT1_BASE_ADDR 0x0320 +#define TX_PKT_REG_EXCEPT2_BASE_ADDR 0x0340 +#define TX_PKT_REG_EXCEPT3_BASE_ADDR 0x0360 +#define TX_PKT_REG_EXCEPT4_BASE_ADDR 0x0380 +#define TX_PKT_REG_GAMUT_P0_BASE_ADDR 0x03A0 +#define TX_PKT_REG_GAMUT_P1_1_BASE_ADDR 0x03C0 +#define TX_PKT_REG_GAMUT_P1_2_BASE_ADDR 0x03E0 + +#define TX_RX_EDID_OFFSET 0x0600 + +/* HDMI OTHER registers */ + +#define HDMI_OTHER_CTRL0 0x8000 +#define HDMI_OTHER_CTRL1 0x8001 + #define HDMI_OTHER_CTRL1_POWER_ON BIT(15) + #define HDMI_OTHER_CTRL1_HDMI_AUDIO_CLOCK_ON BIT(13) + +#define HDMI_OTHER_STATUS0 0x8002 +#define HDMI_OTHER_CTRL2 0x8003 +#define HDMI_OTHER_INTR_MASKN 0x8004 + #define HDMI_OTHER_INTR_MASKN_TX_EDID_INT_RISE BIT(2) + #define HDMI_OTHER_INTR_MASKN_TX_HPD_INT_FALL BIT(1) + #define HDMI_OTHER_INTR_MASKN_TX_HPD_INT_RISE BIT(0) + +#define HDMI_OTHER_INTR_STAT 0x8005 + #define HDMI_OTHER_INTR_STAT_EDID_RISING BIT(2) + #define HDMI_OTHER_INTR_STAT_HPD_FALLING BIT(1) + #define HDMI_OTHER_INTR_STAT_HPD_RISING BIT(0) + +#define HDMI_OTHER_INTR_STAT_CLR 0x8006 + #define HDMI_OTHER_INTR_STAT_CLR_EDID_RISING BIT(2) + #define HDMI_OTHER_INTR_STAT_CLR_HPD_FALLING BIT(1) + #define HDMI_OTHER_INTR_STAT_CLR_HPD_RISING BIT(0) + +#define HDMI_OTHER_AVI_INTR_MASKN0 0x8008 +#define HDMI_OTHER_AVI_INTR_MASKN1 0x8009 +#define HDMI_OTHER_RX_AINFO_INTR_MASKN0 0x800a +#define HDMI_OTHER_RX_AINFO_INTR_MASKN1 0x800b +#define HDMI_OTHER_RX_PACKET_INTR_CLR 0x800c + +#endif /* __MESON_TRANSWITCH_HDMI_H__ */ -- Armbian