diff options
Diffstat (limited to 'drivers/gpu/drm/bridge')
33 files changed, 4316 insertions, 239 deletions
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 44ad70939663..007e5a282f67 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -74,6 +74,14 @@ config DRM_DISPLAY_CONNECTOR on ARM-based platforms. Saying Y here when this driver is not needed will not cause any issue. +config DRM_ITE_IT6505 + tristate "ITE IT6505 DisplayPort bridge" + depends on OF + select DRM_KMS_HELPER + select EXTCON + help + ITE IT6505 DisplayPort bridge chip driver. + config DRM_LONTIUM_LT8912B tristate "Lontium LT8912B DSI/HDMI bridge" depends on OF @@ -183,6 +191,7 @@ config DRM_PARADE_PS8640 tristate "Parade PS8640 MIPI DSI to eDP Converter" depends on OF select DRM_DP_AUX_BUS + select DRM_DP_HELPER select DRM_KMS_HELPER select DRM_MIPI_DSI select DRM_PANEL @@ -253,6 +262,7 @@ config DRM_TOSHIBA_TC358764 config DRM_TOSHIBA_TC358767 tristate "Toshiba TC358767 eDP bridge" depends on OF + select DRM_DP_HELPER select DRM_KMS_HELPER select REGMAP_I2C select DRM_PANEL @@ -272,6 +282,7 @@ config DRM_TOSHIBA_TC358768 config DRM_TOSHIBA_TC358775 tristate "Toshiba TC358775 DSI/LVDS bridge" depends on OF + select DRM_DP_HELPER select DRM_KMS_HELPER select REGMAP_I2C select DRM_PANEL @@ -299,6 +310,7 @@ config DRM_TI_SN65DSI83 config DRM_TI_SN65DSI86 tristate "TI SN65DSI86 DSI to eDP bridge" depends on OF + select DRM_DP_HELPER select DRM_KMS_HELPER select REGMAP_I2C select DRM_PANEL diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index f2c73683cfcb..425844c30495 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o +obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o obj-$(CONFIG_DRM_LONTIUM_LT9611) += lontium-lt9611.o obj-$(CONFIG_DRM_LONTIUM_LT9611UXC) += lontium-lt9611uxc.o diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index 592ecfcf00ca..6a882891d91c 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h @@ -169,6 +169,7 @@ #define ADV7511_PACKET_ENABLE_SPARE2 BIT(1) #define ADV7511_PACKET_ENABLE_SPARE1 BIT(0) +#define ADV7535_REG_POWER2_HPD_OVERRIDE BIT(6) #define ADV7511_REG_POWER2_HPD_SRC_MASK 0xc0 #define ADV7511_REG_POWER2_HPD_SRC_BOTH 0x00 #define ADV7511_REG_POWER2_HPD_SRC_HPD 0x40 diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index f8e5da148599..005bf18682ff 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -223,7 +223,7 @@ static void adv7511_set_config_csc(struct adv7511 *adv7511, config.csc_coefficents = adv7511_csc_ycbcr_to_rgb; if ((connector->display_info.color_formats & - DRM_COLOR_FORMAT_YCRCB422) && + DRM_COLOR_FORMAT_YCBCR422) && config.hdmi_mode) { config.csc_enable = false; config.avi_infoframe.colorspace = @@ -351,11 +351,17 @@ static void __adv7511_power_on(struct adv7511 *adv7511) * from standby or are enabled. When the HPD goes low the adv7511 is * reset and the outputs are disabled which might cause the monitor to * go to standby again. To avoid this we ignore the HPD pin for the - * first few seconds after enabling the output. + * first few seconds after enabling the output. On the other hand + * adv7535 require to enable HPD Override bit for proper HPD. */ - regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, - ADV7511_REG_POWER2_HPD_SRC_MASK, - ADV7511_REG_POWER2_HPD_SRC_NONE); + if (adv7511->type == ADV7535) + regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, + ADV7535_REG_POWER2_HPD_OVERRIDE, + ADV7535_REG_POWER2_HPD_OVERRIDE); + else + regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, + ADV7511_REG_POWER2_HPD_SRC_MASK, + ADV7511_REG_POWER2_HPD_SRC_NONE); } static void adv7511_power_on(struct adv7511 *adv7511) @@ -375,6 +381,10 @@ static void adv7511_power_on(struct adv7511 *adv7511) static void __adv7511_power_off(struct adv7511 *adv7511) { /* TODO: setup additional power down modes */ + if (adv7511->type == ADV7535) + regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, + ADV7535_REG_POWER2_HPD_OVERRIDE, 0); + regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, ADV7511_POWER_POWER_DOWN, ADV7511_POWER_POWER_DOWN); @@ -672,9 +682,14 @@ adv7511_detect(struct adv7511 *adv7511, struct drm_connector *connector) status = connector_status_disconnected; } else { /* Renable HPD sensing */ - regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, - ADV7511_REG_POWER2_HPD_SRC_MASK, - ADV7511_REG_POWER2_HPD_SRC_BOTH); + if (adv7511->type == ADV7535) + regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, + ADV7535_REG_POWER2_HPD_OVERRIDE, + ADV7535_REG_POWER2_HPD_OVERRIDE); + else + regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, + ADV7511_REG_POWER2_HPD_SRC_MASK, + ADV7511_REG_POWER2_HPD_SRC_BOTH); } adv7511->status = status; diff --git a/drivers/gpu/drm/bridge/adv7511/adv7533.c b/drivers/gpu/drm/bridge/adv7511/adv7533.c index eb7579dec40a..ef6270806d1d 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7533.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7533.c @@ -29,7 +29,7 @@ static void adv7511_dsi_config_timing_gen(struct adv7511 *adv) struct mipi_dsi_device *dsi = adv->dsi; struct drm_display_mode *mode = &adv->curr_mode; unsigned int hsw, hfp, hbp, vsw, vfp, vbp; - u8 clock_div_by_lanes[] = { 6, 4, 3 }; /* 2, 3, 4 lanes */ + static const u8 clock_div_by_lanes[] = { 6, 4, 3 }; /* 2, 3, 4 lanes */ hsw = mode->hsync_end - mode->hsync_start; hfp = mode->hsync_start - mode->hdisplay; diff --git a/drivers/gpu/drm/bridge/analogix/Kconfig b/drivers/gpu/drm/bridge/analogix/Kconfig index 2ef6eb2b786c..cc0aa6572d98 100644 --- a/drivers/gpu/drm/bridge/analogix/Kconfig +++ b/drivers/gpu/drm/bridge/analogix/Kconfig @@ -3,6 +3,7 @@ config DRM_ANALOGIX_ANX6345 tristate "Analogix ANX6345 bridge" depends on OF select DRM_ANALOGIX_DP + select DRM_DP_HELPER select DRM_KMS_HELPER select REGMAP_I2C help @@ -14,6 +15,7 @@ config DRM_ANALOGIX_ANX6345 config DRM_ANALOGIX_ANX78XX tristate "Analogix ANX78XX bridge" select DRM_ANALOGIX_DP + select DRM_DP_HELPER select DRM_KMS_HELPER select REGMAP_I2C help @@ -30,6 +32,8 @@ config DRM_ANALOGIX_ANX7625 tristate "Analogix Anx7625 MIPI to DP interface support" depends on DRM depends on OF + select DRM_DP_AUX_BUS + select DRM_DP_HELPER select DRM_MIPI_DSI help ANX7625 is an ultra-low power 4K mobile HD transmitter diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c index e33cd077595a..94e56a2e91f2 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c @@ -22,7 +22,7 @@ #include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> -#include <drm/drm_dp_helper.h> +#include <drm/dp/drm_dp_helper.h> #include <drm/drm_edid.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c index 5e6a0ed39199..2768b41c48e9 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c @@ -21,7 +21,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_crtc.h> -#include <drm/drm_dp_helper.h> +#include <drm/dp/drm_dp_helper.h> #include <drm/drm_edid.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> diff --git a/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c index fe40bab21530..e8297168bfef 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c @@ -8,7 +8,7 @@ #include <linux/regmap.h> #include <drm/drm.h> -#include <drm/drm_dp_helper.h> +#include <drm/dp/drm_dp_helper.h> #include <drm/drm_print.h> #include "analogix-i2c-dptx.h" diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index b7d2e4449cfa..eb590fb8e8d0 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1537,9 +1537,9 @@ static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge, video->color_depth = COLOR_8; break; } - if (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB444) + if (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR444) video->color_space = COLOR_YCBCR444; - else if (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422) + else if (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR422) video->color_space = COLOR_YCBCR422; else video->color_space = COLOR_RGB; diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h index c051502d7fbf..32665203a6ae 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h @@ -10,7 +10,7 @@ #define _ANALOGIX_DP_CORE_H #include <drm/drm_crtc.h> -#include <drm/drm_dp_helper.h> +#include <drm/dp/drm_dp_helper.h> #define DP_TIMEOUT_LOOP_COUNT 100 #define MAX_CR_LOOP 5 diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 2346dbcc505f..31ecf5626f1d 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -24,8 +24,10 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_crtc_helper.h> -#include <drm/drm_dp_helper.h> +#include <drm/dp/drm_dp_aux_bus.h> +#include <drm/dp/drm_dp_helper.h> #include <drm/drm_edid.h> +#include <drm/drm_hdcp.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> @@ -128,6 +130,23 @@ static int anx7625_reg_write(struct anx7625_data *ctx, return ret; } +static int anx7625_reg_block_write(struct anx7625_data *ctx, + struct i2c_client *client, + u8 reg_addr, u8 len, u8 *buf) +{ + int ret; + struct device *dev = &client->dev; + + i2c_access_workaround(ctx, client); + + ret = i2c_smbus_write_i2c_block_data(client, reg_addr, len, buf); + if (ret < 0) + dev_err(dev, "write i2c block failed id=%x\n:%x", + client->addr, reg_addr); + + return ret; +} + static int anx7625_write_or(struct anx7625_data *ctx, struct i2c_client *client, u8 offset, u8 mask) @@ -213,6 +232,76 @@ static int wait_aux_op_finish(struct anx7625_data *ctx) return 0; } +static int anx7625_aux_trans(struct anx7625_data *ctx, u8 op, u32 address, + u8 len, u8 *buf) +{ + struct device *dev = &ctx->client->dev; + int ret; + u8 addrh, addrm, addrl; + u8 cmd; + bool is_write = !(op & DP_AUX_I2C_READ); + + if (len > DP_AUX_MAX_PAYLOAD_BYTES) { + dev_err(dev, "exceed aux buffer len.\n"); + return -EINVAL; + } + + if (!len) + return len; + + addrl = address & 0xFF; + addrm = (address >> 8) & 0xFF; + addrh = (address >> 16) & 0xFF; + + if (!is_write) + op &= ~DP_AUX_I2C_MOT; + cmd = DPCD_CMD(len, op); + + /* Set command and length */ + ret = anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + AP_AUX_COMMAND, cmd); + + /* Set aux access address */ + ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + AP_AUX_ADDR_7_0, addrl); + ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + AP_AUX_ADDR_15_8, addrm); + ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + AP_AUX_ADDR_19_16, addrh); + + if (is_write) + ret |= anx7625_reg_block_write(ctx, ctx->i2c.rx_p0_client, + AP_AUX_BUFF_START, len, buf); + /* Enable aux access */ + ret |= anx7625_write_or(ctx, ctx->i2c.rx_p0_client, + AP_AUX_CTRL_STATUS, AP_AUX_CTRL_OP_EN); + + if (ret < 0) { + dev_err(dev, "cannot access aux related register.\n"); + return -EIO; + } + + ret = wait_aux_op_finish(ctx); + if (ret < 0) { + dev_err(dev, "aux IO error: wait aux op finish.\n"); + return ret; + } + + /* Write done */ + if (is_write) + return len; + + /* Read done, read out dpcd data */ + ret = anx7625_reg_block_read(ctx, ctx->i2c.rx_p0_client, + AP_AUX_BUFF_START, len, buf); + if (ret < 0) { + dev_err(dev, "read dpcd register failed\n"); + return -EIO; + } + + return len; +} + static int anx7625_video_mute_control(struct anx7625_data *ctx, u8 status) { @@ -669,6 +758,165 @@ static int anx7625_dpi_config(struct anx7625_data *ctx) return ret; } +static int anx7625_read_flash_status(struct anx7625_data *ctx) +{ + return anx7625_reg_read(ctx, ctx->i2c.rx_p0_client, R_RAM_CTRL); +} + +static int anx7625_hdcp_key_probe(struct anx7625_data *ctx) +{ + int ret, val; + struct device *dev = &ctx->client->dev; + u8 ident[FLASH_BUF_LEN]; + + ret = anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + FLASH_ADDR_HIGH, 0x91); + ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + FLASH_ADDR_LOW, 0xA0); + if (ret < 0) { + dev_err(dev, "IO error : set key flash address.\n"); + return ret; + } + + ret = anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + FLASH_LEN_HIGH, (FLASH_BUF_LEN - 1) >> 8); + ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + FLASH_LEN_LOW, (FLASH_BUF_LEN - 1) & 0xFF); + if (ret < 0) { + dev_err(dev, "IO error : set key flash len.\n"); + return ret; + } + + ret = anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + R_FLASH_RW_CTRL, FLASH_READ); + ret |= readx_poll_timeout(anx7625_read_flash_status, + ctx, val, + ((val & FLASH_DONE) || (val < 0)), + 2000, + 2000 * 150); + if (ret) { + dev_err(dev, "flash read access fail!\n"); + return -EIO; + } + + ret = anx7625_reg_block_read(ctx, ctx->i2c.rx_p0_client, + FLASH_BUF_BASE_ADDR, + FLASH_BUF_LEN, ident); + if (ret < 0) { + dev_err(dev, "read flash data fail!\n"); + return -EIO; + } + + if (ident[29] == 0xFF && ident[30] == 0xFF && ident[31] == 0xFF) + return -EINVAL; + + return 0; +} + +static int anx7625_hdcp_key_load(struct anx7625_data *ctx) +{ + int ret; + struct device *dev = &ctx->client->dev; + + /* Select HDCP 1.4 KEY */ + ret = anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + R_BOOT_RETRY, 0x12); + ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + FLASH_ADDR_HIGH, HDCP14KEY_START_ADDR >> 8); + ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + FLASH_ADDR_LOW, HDCP14KEY_START_ADDR & 0xFF); + ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + R_RAM_LEN_H, HDCP14KEY_SIZE >> 12); + ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + R_RAM_LEN_L, HDCP14KEY_SIZE >> 4); + + ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + R_RAM_ADDR_H, 0); + ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + R_RAM_ADDR_L, 0); + /* Enable HDCP 1.4 KEY load */ + ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, + R_RAM_CTRL, DECRYPT_EN | LOAD_START); + dev_dbg(dev, "load HDCP 1.4 key done\n"); + return ret; +} + +static int anx7625_hdcp_disable(struct anx7625_data *ctx) +{ + int ret; + struct device *dev = &ctx->client->dev; + + dev_dbg(dev, "disable HDCP 1.4\n"); + + /* Disable HDCP */ + ret = anx7625_write_and(ctx, ctx->i2c.rx_p1_client, 0xee, 0x9f); + /* Try auth flag */ + ret |= anx7625_write_or(ctx, ctx->i2c.rx_p1_client, 0xec, 0x10); + /* Interrupt for DRM */ + ret |= anx7625_write_or(ctx, ctx->i2c.rx_p1_client, 0xff, 0x01); + if (ret < 0) + dev_err(dev, "fail to disable HDCP\n"); + + return anx7625_write_and(ctx, ctx->i2c.tx_p0_client, + TX_HDCP_CTRL0, ~HARD_AUTH_EN & 0xFF); +} + +static int anx7625_hdcp_enable(struct anx7625_data *ctx) +{ + u8 bcap; + int ret; + struct device *dev = &ctx->client->dev; + + ret = anx7625_hdcp_key_probe(ctx); + if (ret) { + dev_dbg(dev, "no key found, not to do hdcp\n"); + return ret; + } + + /* Read downstream capability */ + anx7625_aux_trans(ctx, DP_AUX_NATIVE_READ, 0x68028, 1, &bcap); + if (!(bcap & 0x01)) { + pr_warn("downstream not support HDCP 1.4, cap(%x).\n", bcap); + return 0; + } + + dev_dbg(dev, "enable HDCP 1.4\n"); + + /* First clear HDCP state */ + ret = anx7625_reg_write(ctx, ctx->i2c.tx_p0_client, + TX_HDCP_CTRL0, + KSVLIST_VLD | BKSV_SRM_PASS | RE_AUTHEN); + usleep_range(1000, 1100); + /* Second clear HDCP state */ + ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p0_client, + TX_HDCP_CTRL0, + KSVLIST_VLD | BKSV_SRM_PASS | RE_AUTHEN); + + /* Set time for waiting KSVR */ + ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p0_client, + SP_TX_WAIT_KSVR_TIME, 0xc8); + /* Set time for waiting R0 */ + ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p0_client, + SP_TX_WAIT_R0_TIME, 0xb0); + ret |= anx7625_hdcp_key_load(ctx); + if (ret) { + pr_warn("prepare HDCP key failed.\n"); + return ret; + } + + ret = anx7625_write_or(ctx, ctx->i2c.rx_p1_client, 0xee, 0x20); + + /* Try auth flag */ + ret |= anx7625_write_or(ctx, ctx->i2c.rx_p1_client, 0xec, 0x10); + /* Interrupt for DRM */ + ret |= anx7625_write_or(ctx, ctx->i2c.rx_p1_client, 0xff, 0x01); + if (ret < 0) + dev_err(dev, "fail to enable HDCP\n"); + + return anx7625_write_or(ctx, ctx->i2c.tx_p0_client, + TX_HDCP_CTRL0, HARD_AUTH_EN); +} + static void anx7625_dp_start(struct anx7625_data *ctx) { int ret; @@ -679,6 +927,9 @@ static void anx7625_dp_start(struct anx7625_data *ctx) return; } + /* Disable HDCP */ + anx7625_write_and(ctx, ctx->i2c.rx_p1_client, 0xee, 0x9f); + if (ctx->pdata.is_dpi) ret = anx7625_dpi_config(ctx); else @@ -686,12 +937,17 @@ static void anx7625_dp_start(struct anx7625_data *ctx) if (ret < 0) DRM_DEV_ERROR(dev, "MIPI phy setup error.\n"); + + ctx->hdcp_cp = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + + ctx->dp_en = 1; } static void anx7625_dp_stop(struct anx7625_data *ctx) { struct device *dev = &ctx->client->dev; int ret; + u8 data; DRM_DEV_DEBUG_DRIVER(dev, "stop dp output\n"); @@ -703,8 +959,17 @@ static void anx7625_dp_stop(struct anx7625_data *ctx) ret |= anx7625_write_and(ctx, ctx->i2c.tx_p2_client, 0x08, 0x7f); ret |= anx7625_video_mute_control(ctx, 1); + + dev_dbg(dev, "notify downstream enter into standby\n"); + /* Downstream monitor enter into standby mode */ + data = 2; + ret |= anx7625_aux_trans(ctx, DP_AUX_NATIVE_WRITE, 0x000600, 1, &data); if (ret < 0) DRM_DEV_ERROR(dev, "IO error : mute video fail\n"); + + ctx->hdcp_cp = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + + ctx->dp_en = 0; } static int sp_tx_rst_aux(struct anx7625_data *ctx) @@ -846,7 +1111,8 @@ static int segments_edid_read(struct anx7625_data *ctx, static int sp_tx_edid_read(struct anx7625_data *ctx, u8 *pedid_blocks_buf) { - u8 offset, edid_pos; + u8 offset; + int edid_pos; int count, blocks_num; u8 pblock_buf[MAX_DPCD_BUFFER_SIZE]; u8 i, j; @@ -1098,9 +1364,18 @@ static void anx7625_init_gpio(struct anx7625_data *platform) /* Gpio for chip power enable */ platform->pdata.gpio_p_on = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR_OR_NULL(platform->pdata.gpio_p_on)) { + DRM_DEV_DEBUG_DRIVER(dev, "no enable gpio found\n"); + platform->pdata.gpio_p_on = NULL; + } + /* Gpio for chip reset */ platform->pdata.gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR_OR_NULL(platform->pdata.gpio_reset)) { + DRM_DEV_DEBUG_DRIVER(dev, "no reset gpio found\n"); + platform->pdata.gpio_reset = NULL; + } if (platform->pdata.gpio_p_on && platform->pdata.gpio_reset) { platform->pdata.low_power_mode = 1; @@ -1388,11 +1663,56 @@ static int anx7625_parse_dt(struct device *dev, return 0; } +static bool anx7625_of_panel_on_aux_bus(struct device *dev) +{ + struct device_node *bus, *panel; + + bus = of_get_child_by_name(dev->of_node, "aux-bus"); + if (!bus) + return false; + + panel = of_get_child_by_name(bus, "panel"); + of_node_put(bus); + if (!panel) + return false; + of_node_put(panel); + + return true; +} + static inline struct anx7625_data *bridge_to_anx7625(struct drm_bridge *bridge) { return container_of(bridge, struct anx7625_data, bridge); } +static ssize_t anx7625_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + struct anx7625_data *ctx = container_of(aux, struct anx7625_data, aux); + struct device *dev = &ctx->client->dev; + u8 request = msg->request & ~DP_AUX_I2C_MOT; + int ret = 0; + + pm_runtime_get_sync(dev); + msg->reply = 0; + switch (request) { + case DP_AUX_NATIVE_WRITE: + case DP_AUX_I2C_WRITE: + case DP_AUX_NATIVE_READ: + case DP_AUX_I2C_READ: + break; + default: + ret = -EINVAL; + } + if (!ret) + ret = anx7625_aux_trans(ctx, msg->request, msg->address, + msg->size, msg->buffer); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + static struct edid *anx7625_get_edid(struct anx7625_data *ctx) { struct device *dev = &ctx->client->dev; @@ -1601,9 +1921,27 @@ static int anx7625_audio_hook_plugged_cb(struct device *dev, void *data, return 0; } +static int anx7625_audio_get_eld(struct device *dev, void *data, + u8 *buf, size_t len) +{ + struct anx7625_data *ctx = dev_get_drvdata(dev); + + if (!ctx->connector) { + dev_err(dev, "connector not initial\n"); + return -EINVAL; + } + + dev_dbg(dev, "audio copy eld\n"); + memcpy(buf, ctx->connector->eld, + min(sizeof(ctx->connector->eld), len)); + + return 0; +} + static const struct hdmi_codec_ops anx7625_codec_ops = { .hw_params = anx7625_audio_hw_params, .audio_shutdown = anx7625_audio_shutdown, + .get_eld = anx7625_audio_get_eld, .get_dai_id = anx7625_hdmi_i2s_get_dai_id, .hook_plugged_cb = anx7625_audio_hook_plugged_cb, }; @@ -1660,7 +1998,7 @@ static int anx7625_attach_dsi(struct anx7625_data *ctx) host = of_find_mipi_dsi_host_by_node(ctx->pdata.mipi_host_node); if (!host) { DRM_DEV_ERROR(dev, "fail to find dsi host.\n"); - return -EINVAL; + return -EPROBE_DEFER; } dsi = devm_mipi_dsi_device_register_full(dev, host, &info); @@ -1688,6 +2026,83 @@ static int anx7625_attach_dsi(struct anx7625_data *ctx) return 0; } +static void hdcp_check_work_func(struct work_struct *work) +{ + u8 status; + struct delayed_work *dwork; + struct anx7625_data *ctx; + struct device *dev; + struct drm_device *drm_dev; + + dwork = to_delayed_work(work); + ctx = container_of(dwork, struct anx7625_data, hdcp_work); + dev = &ctx->client->dev; + + if (!ctx->connector) { + dev_err(dev, "HDCP connector is null!"); + return; + } + + drm_dev = ctx->connector->dev; + drm_modeset_lock(&drm_dev->mode_config.connection_mutex, NULL); + mutex_lock(&ctx->hdcp_wq_lock); + + status = anx7625_reg_read(ctx, ctx->i2c.tx_p0_client, 0); + dev_dbg(dev, "sink HDCP status check: %.02x\n", status); + if (status & BIT(1)) { + ctx->hdcp_cp = DRM_MODE_CONTENT_PROTECTION_ENABLED; + drm_hdcp_update_content_protection(ctx->connector, + ctx->hdcp_cp); + dev_dbg(dev, "update CP to ENABLE\n"); + } + + mutex_unlock(&ctx->hdcp_wq_lock); + drm_modeset_unlock(&drm_dev->mode_config.connection_mutex); +} + +static int anx7625_connector_atomic_check(struct anx7625_data *ctx, + struct drm_connector_state *state) +{ + struct device *dev = &ctx->client->dev; + int cp; + + dev_dbg(dev, "hdcp state check\n"); + cp = state->content_protection; + + if (cp == ctx->hdcp_cp) + return 0; + + if (cp == DRM_MODE_CONTENT_PROTECTION_DESIRED) { + if (ctx->dp_en) { + dev_dbg(dev, "enable HDCP\n"); + anx7625_hdcp_enable(ctx); + + queue_delayed_work(ctx->hdcp_workqueue, + &ctx->hdcp_work, + msecs_to_jiffies(2000)); + } + } + + if (cp == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { + if (ctx->hdcp_cp != DRM_MODE_CONTENT_PROTECTION_ENABLED) { + dev_err(dev, "current CP is not ENABLED\n"); + return -EINVAL; + } + anx7625_hdcp_disable(ctx); + ctx->hdcp_cp = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + drm_hdcp_update_content_protection(ctx->connector, + ctx->hdcp_cp); + dev_dbg(dev, "update CP to UNDESIRE\n"); + } + + if (cp == DRM_MODE_CONTENT_PROTECTION_ENABLED) { + dev_err(dev, "Userspace illegal set to PROTECTION ENABLE\n"); + return -EINVAL; + } + + return 0; +} + static int anx7625_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) { @@ -1704,6 +2119,13 @@ static int anx7625_bridge_attach(struct drm_bridge *bridge, return -ENODEV; } + ctx->aux.drm_dev = bridge->dev; + err = drm_dp_aux_register(&ctx->aux); + if (err) { + dev_err(dev, "failed to register aux channel: %d\n", err); + return err; + } + if (ctx->pdata.panel_bridge) { err = drm_bridge_attach(bridge->encoder, ctx->pdata.panel_bridge, @@ -1717,6 +2139,13 @@ static int anx7625_bridge_attach(struct drm_bridge *bridge, return 0; } +static void anx7625_bridge_detach(struct drm_bridge *bridge) +{ + struct anx7625_data *ctx = bridge_to_anx7625(bridge); + + drm_dp_aux_unregister(&ctx->aux); +} + static enum drm_mode_status anx7625_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, @@ -1902,25 +2331,57 @@ static bool anx7625_bridge_mode_fixup(struct drm_bridge *bridge, return true; } -static void anx7625_bridge_enable(struct drm_bridge *bridge) +static int anx7625_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct anx7625_data *ctx = bridge_to_anx7625(bridge); + struct device *dev = &ctx->client->dev; + + dev_dbg(dev, "drm bridge atomic check\n"); + + anx7625_bridge_mode_fixup(bridge, &crtc_state->mode, + &crtc_state->adjusted_mode); + + return anx7625_connector_atomic_check(ctx, conn_state); +} + +static void anx7625_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *state) { struct anx7625_data *ctx = bridge_to_anx7625(bridge); struct device *dev = &ctx->client->dev; + struct drm_connector *connector; + + dev_dbg(dev, "drm atomic enable\n"); + + if (!bridge->encoder) { + dev_err(dev, "Parent encoder object not found"); + return; + } + + connector = drm_atomic_get_new_connector_for_encoder(state->base.state, + bridge->encoder); + if (!connector) + return; - DRM_DEV_DEBUG_DRIVER(dev, "drm enable\n"); + ctx->connector = connector; pm_runtime_get_sync(dev); anx7625_dp_start(ctx); } -static void anx7625_bridge_disable(struct drm_bridge *bridge) +static void anx7625_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old) { struct anx7625_data *ctx = bridge_to_anx7625(bridge); struct device *dev = &ctx->client->dev; - DRM_DEV_DEBUG_DRIVER(dev, "drm disable\n"); + dev_dbg(dev, "drm atomic disable\n"); + ctx->connector = NULL; anx7625_dp_stop(ctx); pm_runtime_put_sync(dev); @@ -1950,11 +2411,15 @@ static struct edid *anx7625_bridge_get_edid(struct drm_bridge *bridge, static const struct drm_bridge_funcs anx7625_bridge_funcs = { .attach = anx7625_bridge_attach, - .disable = anx7625_bridge_disable, + .detach = anx7625_bridge_detach, .mode_valid = anx7625_bridge_mode_valid, .mode_set = anx7625_bridge_mode_set, - .mode_fixup = anx7625_bridge_mode_fixup, - .enable = anx7625_bridge_enable, + .atomic_check = anx7625_bridge_atomic_check, + .atomic_enable = anx7625_bridge_atomic_enable, + .atomic_disable = anx7625_bridge_atomic_disable, + .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, .detect = anx7625_bridge_detect, .get_edid = anx7625_bridge_get_edid, }; @@ -1962,40 +2427,54 @@ static const struct drm_bridge_funcs anx7625_bridge_funcs = { static int anx7625_register_i2c_dummy_clients(struct anx7625_data *ctx, struct i2c_client *client) { + int err = 0; + ctx->i2c.tx_p0_client = i2c_new_dummy_device(client->adapter, TX_P0_ADDR >> 1); - if (!ctx->i2c.tx_p0_client) - return -ENOMEM; + if (IS_ERR(ctx->i2c.tx_p0_client)) + return PTR_ERR(ctx->i2c.tx_p0_client); ctx->i2c.tx_p1_client = i2c_new_dummy_device(client->adapter, TX_P1_ADDR >> 1); - if (!ctx->i2c.tx_p1_client) + if (IS_ERR(ctx->i2c.tx_p1_client)) { + err = PTR_ERR(ctx->i2c.tx_p1_client); goto free_tx_p0; + } ctx->i2c.tx_p2_client = i2c_new_dummy_device(client->adapter, TX_P2_ADDR >> 1); - if (!ctx->i2c.tx_p2_client) + if (IS_ERR(ctx->i2c.tx_p2_client)) { + err = PTR_ERR(ctx->i2c.tx_p2_client); goto free_tx_p1; + } ctx->i2c.rx_p0_client = i2c_new_dummy_device(client->adapter, RX_P0_ADDR >> 1); - if (!ctx->i2c.rx_p0_client) + if (IS_ERR(ctx->i2c.rx_p0_client)) { + err = PTR_ERR(ctx->i2c.rx_p0_client); goto free_tx_p2; + } ctx->i2c.rx_p1_client = i2c_new_dummy_device(client->adapter, RX_P1_ADDR >> 1); - if (!ctx->i2c.rx_p1_client) + if (IS_ERR(ctx->i2c.rx_p1_client)) { + err = PTR_ERR(ctx->i2c.rx_p1_client); goto free_rx_p0; + } ctx->i2c.rx_p2_client = i2c_new_dummy_device(client->adapter, RX_P2_ADDR >> 1); - if (!ctx->i2c.rx_p2_client) + if (IS_ERR(ctx->i2c.rx_p2_client)) { + err = PTR_ERR(ctx->i2c.rx_p2_client); goto free_rx_p1; + } ctx->i2c.tcpc_client = i2c_new_dummy_device(client->adapter, TCPC_INTERFACE_ADDR >> 1); - if (!ctx->i2c.tcpc_client) + if (IS_ERR(ctx->i2c.tcpc_client)) { + err = PTR_ERR(ctx->i2c.tcpc_client); goto free_rx_p2; + } return 0; @@ -2012,7 +2491,7 @@ free_tx_p1: free_tx_p0: i2c_unregister_device(ctx->i2c.tx_p0_client); - return -ENOMEM; + return err; } static void anx7625_unregister_i2c_dummy_clients(struct anx7625_data *ctx) @@ -2090,6 +2569,12 @@ static const struct dev_pm_ops anx7625_pm_ops = { anx7625_runtime_pm_resume, NULL) }; +static void anx7625_runtime_disable(void *data) +{ + pm_runtime_dont_use_autosuspend(data); + pm_runtime_disable(data); +} + static int anx7625_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -2104,7 +2589,7 @@ static int anx7625_i2c_probe(struct i2c_client *client, return -ENODEV; } - platform = kzalloc(sizeof(*platform), GFP_KERNEL); + platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL); if (!platform) { DRM_DEV_ERROR(dev, "fail to allocate driver data\n"); return -ENOMEM; @@ -2112,13 +2597,6 @@ static int anx7625_i2c_probe(struct i2c_client *client, pdata = &platform->pdata; - ret = anx7625_parse_dt(dev, pdata); - if (ret) { - if (ret != -EPROBE_DEFER) - DRM_DEV_ERROR(dev, "fail to parse DT : %d\n", ret); - goto free_platform; - } - platform->client = client; i2c_set_clientdata(client, platform); @@ -2134,6 +2612,15 @@ static int anx7625_i2c_probe(struct i2c_client *client, anx7625_init_gpio(platform); mutex_init(&platform->lock); + mutex_init(&platform->hdcp_wq_lock); + + INIT_DELAYED_WORK(&platform->hdcp_work, hdcp_check_work_func); + platform->hdcp_workqueue = create_workqueue("hdcp workqueue"); + if (!platform->hdcp_workqueue) { + dev_err(dev, "fail to create work queue\n"); + ret = -ENOMEM; + return ret; + } platform->pdata.intp_irq = client->irq; if (platform->pdata.intp_irq) { @@ -2143,7 +2630,7 @@ static int anx7625_i2c_probe(struct i2c_client *client, if (!platform->workqueue) { DRM_DEV_ERROR(dev, "fail to create work queue\n"); ret = -ENOMEM; - goto free_platform; + goto free_hdcp_wq; } ret = devm_request_threaded_irq(dev, platform->pdata.intp_irq, @@ -2157,6 +2644,19 @@ static int anx7625_i2c_probe(struct i2c_client *client, } } + platform->aux.name = "anx7625-aux"; + platform->aux.dev = dev; + platform->aux.transfer = anx7625_aux_transfer; + drm_dp_aux_init(&platform->aux); + devm_of_dp_aux_populate_ep_devices(&platform->aux); + + ret = anx7625_parse_dt(dev, pdata); + if (ret) { + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(dev, "fail to parse DT : %d\n", ret); + return ret; + } + if (anx7625_register_i2c_dummy_clients(platform, client) != 0) { ret = -ENOMEM; DRM_DEV_ERROR(dev, "fail to reserve I2C bus.\n"); @@ -2164,6 +2664,12 @@ static int anx7625_i2c_probe(struct i2c_client *client, } pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_suspend_ignore_children(dev, true); + ret = devm_add_action_or_reset(dev, anx7625_runtime_disable, dev); + if (ret) + return ret; if (!platform->pdata.low_power_mode) { anx7625_disable_pd_protocol(platform); @@ -2176,7 +2682,8 @@ static int anx7625_i2c_probe(struct i2c_client *client, platform->bridge.funcs = &anx7625_bridge_funcs; platform->bridge.of_node = client->dev.of_node; - platform->bridge.ops = DRM_BRIDGE_OP_EDID; + if (!anx7625_of_panel_on_aux_bus(&client->dev)) + platform->bridge.ops |= DRM_BRIDGE_OP_EDID; if (!platform->pdata.panel_bridge) platform->bridge.ops |= DRM_BRIDGE_OP_HPD | DRM_BRIDGE_OP_DETECT; @@ -2213,8 +2720,9 @@ free_wq: if (platform->workqueue) destroy_workqueue(platform->workqueue); -free_platform: - kfree(platform); +free_hdcp_wq: + if (platform->hdcp_workqueue) + destroy_workqueue(platform->hdcp_workqueue); return ret; } @@ -2228,6 +2736,12 @@ static int anx7625_i2c_remove(struct i2c_client *client) if (platform->pdata.intp_irq) destroy_workqueue(platform->workqueue); + if (platform->hdcp_workqueue) { + cancel_delayed_work(&platform->hdcp_work); + flush_workqueue(platform->hdcp_workqueue); + destroy_workqueue(platform->hdcp_workqueue); + } + if (!platform->pdata.low_power_mode) pm_runtime_put_sync_suspend(&client->dev); @@ -2236,7 +2750,6 @@ static int anx7625_i2c_remove(struct i2c_client *client) if (platform->pdata.audio_en) anx7625_unregister_audio(platform); - kfree(platform); return 0; } diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.h b/drivers/gpu/drm/bridge/analogix/anx7625.h index 3d79b6fb13c8..edbbfe410a56 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.h +++ b/drivers/gpu/drm/bridge/analogix/anx7625.h @@ -59,10 +59,23 @@ /***************************************************************/ /* Register definition of device address 0x70 */ -#define I2C_ADDR_70_DPTX 0x70 - -#define SP_TX_LINK_BW_SET_REG 0xA0 -#define SP_TX_LANE_COUNT_SET_REG 0xA1 +#define TX_HDCP_CTRL0 0x01 +#define STORE_AN BIT(7) +#define RX_REPEATER BIT(6) +#define RE_AUTHEN BIT(5) +#define SW_AUTH_OK BIT(4) +#define HARD_AUTH_EN BIT(3) +#define ENC_EN BIT(2) +#define BKSV_SRM_PASS BIT(1) +#define KSVLIST_VLD BIT(0) + +#define SP_TX_WAIT_R0_TIME 0x40 +#define SP_TX_WAIT_KSVR_TIME 0x42 +#define SP_TX_SYS_CTRL1_REG 0x80 +#define HDCP2TX_FW_EN BIT(4) + +#define SP_TX_LINK_BW_SET_REG 0xA0 +#define SP_TX_LANE_COUNT_SET_REG 0xA1 #define M_VID_0 0xC0 #define M_VID_1 0xC1 @@ -71,6 +84,12 @@ #define N_VID_1 0xC4 #define N_VID_2 0xC5 +#define KEY_START_ADDR 0x9000 +#define KEY_RESERVED 416 + +#define HDCP14KEY_START_ADDR (KEY_START_ADDR + KEY_RESERVED) +#define HDCP14KEY_SIZE 624 + /***************************************************************/ /* Register definition of device address 0x72 */ #define AUX_RST 0x04 @@ -155,9 +174,43 @@ #define I2C_ADDR_7E_FLASH_CONTROLLER 0x7E +#define R_BOOT_RETRY 0x00 +#define R_RAM_ADDR_H 0x01 +#define R_RAM_ADDR_L 0x02 +#define R_RAM_LEN_H 0x03 +#define R_RAM_LEN_L 0x04 #define FLASH_LOAD_STA 0x05 #define FLASH_LOAD_STA_CHK BIT(7) +#define R_RAM_CTRL 0x05 +/* bit positions */ +#define FLASH_DONE BIT(7) +#define BOOT_LOAD_DONE BIT(6) +#define CRC_OK BIT(5) +#define LOAD_DONE BIT(4) +#define O_RW_DONE BIT(3) +#define FUSE_BUSY BIT(2) +#define DECRYPT_EN BIT(1) +#define LOAD_START BIT(0) + +#define FLASH_ADDR_HIGH 0x0F +#define FLASH_ADDR_LOW 0x10 +#define FLASH_LEN_HIGH 0x31 +#define FLASH_LEN_LOW 0x32 +#define R_FLASH_RW_CTRL 0x33 +/* bit positions */ +#define READ_DELAY_SELECT BIT(7) +#define GENERAL_INSTRUCTION_EN BIT(6) +#define FLASH_ERASE_EN BIT(5) +#define RDID_READ_EN BIT(4) +#define REMS_READ_EN BIT(3) +#define WRITE_STATUS_EN BIT(2) +#define FLASH_READ BIT(1) +#define FLASH_WRITE BIT(0) + +#define FLASH_BUF_BASE_ADDR 0x60 +#define FLASH_BUF_LEN 0x20 + #define XTAL_FRQ_SEL 0x3F /* bit field positions */ #define XTAL_FRQ_SEL_POS 5 @@ -184,10 +237,13 @@ #define AP_AUX_CTRL_ADDRONLY 0x20 #define AP_AUX_BUFF_START 0x15 -#define PIXEL_CLOCK_L 0x25 -#define PIXEL_CLOCK_H 0x26 +#define PIXEL_CLOCK_L 0x25 +#define PIXEL_CLOCK_H 0x26 + +#define AP_AUX_COMMAND 0x27 /* com+len */ +#define LENGTH_SHIFT 4 +#define DPCD_CMD(len, cmd) ((((len) - 1) << LENGTH_SHIFT) | (cmd)) -#define AP_AUX_COMMAND 0x27 /* com+len */ /* Bit 0&1: 3D video structure */ /* 0x01: frame packing, 0x02:Line alternative, 0x03:Side-by-side(full) */ #define AP_AV_STATUS 0x28 @@ -392,22 +448,31 @@ struct anx7625_data { struct platform_device *audio_pdev; int hpd_status; int hpd_high_cnt; + int dp_en; + int hdcp_cp; /* Lock for work queue */ struct mutex lock; struct i2c_client *client; struct anx7625_i2c_client i2c; struct i2c_client *last_client; + struct timer_list hdcp_timer; struct s_edid_data slimport_edid_p; struct device *codec_dev; hdmi_codec_plugged_cb plugged_cb; struct work_struct work; struct workqueue_struct *workqueue; + struct delayed_work hdcp_work; + struct workqueue_struct *hdcp_workqueue; + /* Lock for hdcp work queue */ + struct mutex hdcp_wq_lock; char edid_block; struct display_timing dt; u8 display_timing_valid; struct drm_bridge bridge; u8 bridge_attached; + struct drm_connector *connector; struct mipi_dsi_device *dsi; + struct drm_dp_aux aux; }; #endif /* __ANX7625_H__ */ diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig b/drivers/gpu/drm/bridge/cadence/Kconfig index ef8c230e0f62..de697bade05e 100644 --- a/drivers/gpu/drm/bridge/cadence/Kconfig +++ b/drivers/gpu/drm/bridge/cadence/Kconfig @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config DRM_CDNS_MHDP8546 tristate "Cadence DPI/DP bridge" + select DRM_DP_HELPER select DRM_KMS_HELPER select DRM_PANEL_BRIDGE depends on OF diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index 5530fbf64f1e..ac18e15aa167 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -41,7 +41,7 @@ #include <drm/drm_bridge.h> #include <drm/drm_connector.h> #include <drm/drm_crtc_helper.h> -#include <drm/drm_dp_helper.h> +#include <drm/dp/drm_dp_helper.h> #include <drm/drm_hdcp.h> #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_print.h> @@ -1553,13 +1553,13 @@ static u32 cdns_mhdp_get_bpp(struct cdns_mhdp_display_fmt *fmt) switch (fmt->color_format) { case DRM_COLOR_FORMAT_RGB444: - case DRM_COLOR_FORMAT_YCRCB444: + case DRM_COLOR_FORMAT_YCBCR444: bpp = fmt->bpc * 3; break; - case DRM_COLOR_FORMAT_YCRCB422: + case DRM_COLOR_FORMAT_YCBCR422: bpp = fmt->bpc * 2; break; - case DRM_COLOR_FORMAT_YCRCB420: + case DRM_COLOR_FORMAT_YCBCR420: bpp = fmt->bpc * 3 / 2; break; default: @@ -1767,8 +1767,8 @@ static void cdns_mhdp_configure_video(struct cdns_mhdp_device *mhdp, * If YCBCR supported and stream not SD, use ITU709 * Need to handle ITU version with YCBCR420 when supported */ - if ((pxlfmt == DRM_COLOR_FORMAT_YCRCB444 || - pxlfmt == DRM_COLOR_FORMAT_YCRCB422) && mode->crtc_vdisplay >= 720) + if ((pxlfmt == DRM_COLOR_FORMAT_YCBCR444 || + pxlfmt == DRM_COLOR_FORMAT_YCBCR422) && mode->crtc_vdisplay >= 720) misc0 = DP_YCBCR_COEFFICIENTS_ITU709; bpp = cdns_mhdp_get_bpp(&mhdp->display_fmt); @@ -1778,15 +1778,15 @@ static void cdns_mhdp_configure_video(struct cdns_mhdp_device *mhdp, pxl_repr = CDNS_DP_FRAMER_RGB << CDNS_DP_FRAMER_PXL_FORMAT; misc0 |= DP_COLOR_FORMAT_RGB; break; - case DRM_COLOR_FORMAT_YCRCB444: + case DRM_COLOR_FORMAT_YCBCR444: pxl_repr = CDNS_DP_FRAMER_YCBCR444 << CDNS_DP_FRAMER_PXL_FORMAT; misc0 |= DP_COLOR_FORMAT_YCbCr444 | DP_TEST_DYNAMIC_RANGE_CEA; break; - case DRM_COLOR_FORMAT_YCRCB422: + case DRM_COLOR_FORMAT_YCBCR422: pxl_repr = CDNS_DP_FRAMER_YCBCR422 << CDNS_DP_FRAMER_PXL_FORMAT; misc0 |= DP_COLOR_FORMAT_YCbCr422 | DP_TEST_DYNAMIC_RANGE_CEA; break; - case DRM_COLOR_FORMAT_YCRCB420: + case DRM_COLOR_FORMAT_YCBCR420: pxl_repr = CDNS_DP_FRAMER_YCBCR420 << CDNS_DP_FRAMER_PXL_FORMAT; break; default: @@ -1882,7 +1882,7 @@ static void cdns_mhdp_configure_video(struct cdns_mhdp_device *mhdp, if (mhdp->display_fmt.y_only) misc1 |= CDNS_DP_TEST_COLOR_FORMAT_RAW_Y_ONLY; /* Use VSC SDP for Y420 */ - if (pxlfmt == DRM_COLOR_FORMAT_YCRCB420) + if (pxlfmt == DRM_COLOR_FORMAT_YCBCR420) misc1 = CDNS_DP_TEST_VSC_SDP; cdns_mhdp_reg_write(mhdp, CDNS_DP_MSA_MISC(stream_id), diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h index c74439d0b1a7..fc77f987c835 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h @@ -17,7 +17,7 @@ #include <drm/drm_bridge.h> #include <drm/drm_connector.h> -#include <drm/drm_dp_helper.h> +#include <drm/dp/drm_dp_helper.h> struct clk; struct device; diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c index d8a15c459b42..829e1a144656 100644 --- a/drivers/gpu/drm/bridge/cdns-dsi.c +++ b/drivers/gpu/drm/bridge/cdns-dsi.c @@ -1284,6 +1284,7 @@ static const struct of_device_id cdns_dsi_of_match[] = { { .compatible = "cdns,dsi" }, { }, }; +MODULE_DEVICE_TABLE(of, cdns_dsi_of_match); static struct platform_driver cdns_dsi_platform_driver = { .probe = cdns_dsi_drm_probe, diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c index a6151db95586..d9b7f48b99fb 100644 --- a/drivers/gpu/drm/bridge/chipone-icn6211.c +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -4,6 +4,7 @@ * Author: Jagan Teki <jagan@amarulasolutions.com> */ +#include <drm/drm_atomic_helper.h> #include <drm/drm_of.h> #include <drm/drm_print.h> #include <drm/drm_mipi_dsi.h> @@ -30,6 +31,7 @@ struct chipone { struct device *dev; struct drm_bridge bridge; + struct drm_display_mode mode; struct drm_bridge *panel_bridge; struct gpio_desc *enable_gpio; struct regulator *vdd1; @@ -42,11 +44,6 @@ static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge) return container_of(bridge, struct chipone, bridge); } -static struct drm_display_mode *bridge_to_mode(struct drm_bridge *bridge) -{ - return &bridge->encoder->crtc->state->adjusted_mode; -} - static inline int chipone_dsi_write(struct chipone *icn, const void *seq, size_t len) { @@ -61,10 +58,11 @@ static inline int chipone_dsi_write(struct chipone *icn, const void *seq, chipone_dsi_write(icn, d, ARRAY_SIZE(d)); \ } -static void chipone_enable(struct drm_bridge *bridge) +static void chipone_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { struct chipone *icn = bridge_to_chipone(bridge); - struct drm_display_mode *mode = bridge_to_mode(bridge); + struct drm_display_mode *mode = &icn->mode; ICN6211_DSI(icn, 0x7a, 0xc1); @@ -114,7 +112,8 @@ static void chipone_enable(struct drm_bridge *bridge) usleep_range(10000, 11000); } -static void chipone_pre_enable(struct drm_bridge *bridge) +static void chipone_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { struct chipone *icn = bridge_to_chipone(bridge); int ret; @@ -145,7 +144,8 @@ static void chipone_pre_enable(struct drm_bridge *bridge) usleep_range(10000, 11000); } -static void chipone_post_disable(struct drm_bridge *bridge) +static void chipone_atomic_post_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { struct chipone *icn = bridge_to_chipone(bridge); @@ -161,6 +161,15 @@ static void chipone_post_disable(struct drm_bridge *bridge) gpiod_set_value(icn->enable_gpio, 0); } +static void chipone_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct chipone *icn = bridge_to_chipone(bridge); + + drm_mode_copy(&icn->mode, adjusted_mode); +} + static int chipone_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) { struct chipone *icn = bridge_to_chipone(bridge); @@ -169,16 +178,19 @@ static int chipone_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flag } static const struct drm_bridge_funcs chipone_bridge_funcs = { - .attach = chipone_attach, - .post_disable = chipone_post_disable, - .pre_enable = chipone_pre_enable, - .enable = chipone_enable, + .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, + .atomic_pre_enable = chipone_atomic_pre_enable, + .atomic_enable = chipone_atomic_enable, + .atomic_post_disable = chipone_atomic_post_disable, + .mode_set = chipone_mode_set, + .attach = chipone_attach, }; static int chipone_parse_dt(struct chipone *icn) { struct device *dev = icn->dev; - struct drm_panel *panel; int ret; icn->vdd1 = devm_regulator_get_optional(dev, "vdd1"); @@ -214,11 +226,7 @@ static int chipone_parse_dt(struct chipone *icn) return PTR_ERR(icn->enable_gpio); } - ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL); - if (ret) - return ret; - - icn->panel_bridge = devm_drm_panel_bridge_add(dev, panel); + icn->panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); if (IS_ERR(icn->panel_bridge)) return PTR_ERR(icn->panel_bridge); diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c new file mode 100644 index 000000000000..f2f101220ade --- /dev/null +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -0,0 +1,3352 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/extcon.h> +#include <linux/fs.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <linux/wait.h> + +#include <crypto/hash.h> + +#include <drm/dp/drm_dp_helper.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_hdcp.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> + +#include <sound/hdmi-codec.h> + +#define REG_IC_VER 0x04 + +#define REG_RESET_CTRL 0x05 +#define VIDEO_RESET BIT(0) +#define AUDIO_RESET BIT(1) +#define ALL_LOGIC_RESET BIT(2) +#define AUX_RESET BIT(3) +#define HDCP_RESET BIT(4) + +#define INT_STATUS_01 0x06 +#define INT_MASK_01 0x09 +#define INT_HPD_CHANGE 0 +#define INT_RECEIVE_HPD_IRQ 1 +#define INT_SCDT_CHANGE 2 +#define INT_HDCP_FAIL 3 +#define INT_HDCP_DONE 4 +#define BIT_OFFSET(x) (((x) - INT_STATUS_01) * BITS_PER_BYTE) +#define BIT_INT_HPD INT_HPD_CHANGE +#define BIT_INT_HPD_IRQ INT_RECEIVE_HPD_IRQ +#define BIT_INT_SCDT INT_SCDT_CHANGE +#define BIT_INT_HDCP_FAIL INT_HDCP_FAIL +#define BIT_INT_HDCP_DONE INT_HDCP_DONE + +#define INT_STATUS_02 0x07 +#define INT_MASK_02 0x0A +#define INT_AUX_CMD_FAIL 0 +#define INT_HDCP_KSV_CHECK 1 +#define INT_AUDIO_FIFO_ERROR 2 +#define BIT_INT_AUX_CMD_FAIL (BIT_OFFSET(0x07) + INT_AUX_CMD_FAIL) +#define BIT_INT_HDCP_KSV_CHECK (BIT_OFFSET(0x07) + INT_HDCP_KSV_CHECK) +#define BIT_INT_AUDIO_FIFO_ERROR (BIT_OFFSET(0x07) + INT_AUDIO_FIFO_ERROR) + +#define INT_STATUS_03 0x08 +#define INT_MASK_03 0x0B +#define INT_LINK_TRAIN_FAIL 4 +#define INT_VID_FIFO_ERROR 5 +#define INT_IO_LATCH_FIFO_OVERFLOW 7 +#define BIT_INT_LINK_TRAIN_FAIL (BIT_OFFSET(0x08) + INT_LINK_TRAIN_FAIL) +#define BIT_INT_VID_FIFO_ERROR (BIT_OFFSET(0x08) + INT_VID_FIFO_ERROR) +#define BIT_INT_IO_FIFO_OVERFLOW (BIT_OFFSET(0x08) + INT_IO_LATCH_FIFO_OVERFLOW) + +#define REG_SYSTEM_STS 0x0D +#define INT_STS BIT(0) +#define HPD_STS BIT(1) +#define VIDEO_STB BIT(2) + +#define REG_LINK_TRAIN_STS 0x0E +#define LINK_STATE_CR BIT(2) +#define LINK_STATE_EQ BIT(3) +#define LINK_STATE_NORP BIT(4) + +#define REG_BANK_SEL 0x0F +#define REG_CLK_CTRL0 0x10 +#define M_PCLK_DELAY 0x03 + +#define REG_AUX_OPT 0x11 +#define AUX_AUTO_RST BIT(0) +#define AUX_FIX_FREQ BIT(3) + +#define REG_DATA_CTRL0 0x12 +#define VIDEO_LATCH_EDGE BIT(4) +#define ENABLE_PCLK_COUNTER BIT(7) + +#define REG_PCLK_COUNTER_VALUE 0x13 + +#define REG_501_FIFO_CTRL 0x15 +#define RST_501_FIFO BIT(1) + +#define REG_TRAIN_CTRL0 0x16 +#define FORCE_LBR BIT(0) +#define LANE_COUNT_MASK 0x06 +#define LANE_SWAP BIT(3) +#define SPREAD_AMP_5 BIT(4) +#define FORCE_CR_DONE BIT(5) +#define FORCE_EQ_DONE BIT(6) + +#define REG_TRAIN_CTRL1 0x17 +#define AUTO_TRAIN BIT(0) +#define MANUAL_TRAIN BIT(1) +#define FORCE_RETRAIN BIT(2) + +#define REG_AUX_CTRL 0x23 +#define CLR_EDID_FIFO BIT(0) +#define AUX_USER_MODE BIT(1) +#define AUX_NO_SEGMENT_WR BIT(6) +#define AUX_EN_FIFO_READ BIT(7) + +#define REG_AUX_ADR_0_7 0x24 +#define REG_AUX_ADR_8_15 0x25 +#define REG_AUX_ADR_16_19 0x26 +#define REG_AUX_OUT_DATA0 0x27 + +#define REG_AUX_CMD_REQ 0x2B +#define AUX_BUSY BIT(5) + +#define REG_AUX_DATA_0_7 0x2C +#define REG_AUX_DATA_8_15 0x2D +#define REG_AUX_DATA_16_23 0x2E +#define REG_AUX_DATA_24_31 0x2F + +#define REG_AUX_DATA_FIFO 0x2F + +#define REG_AUX_ERROR_STS 0x9F +#define M_AUX_REQ_FAIL 0x03 + +#define REG_HDCP_CTRL1 0x38 +#define HDCP_CP_ENABLE BIT(0) + +#define REG_HDCP_TRIGGER 0x39 +#define HDCP_TRIGGER_START BIT(0) +#define HDCP_TRIGGER_CPIRQ BIT(1) +#define HDCP_TRIGGER_KSV_DONE BIT(4) +#define HDCP_TRIGGER_KSV_FAIL BIT(5) + +#define REG_HDCP_CTRL2 0x3A +#define HDCP_AN_SEL BIT(0) +#define HDCP_AN_GEN BIT(1) +#define HDCP_HW_HPDIRQ_ACT BIT(2) +#define HDCP_EN_M0_READ BIT(5) + +#define REG_M0_0_7 0x4C +#define REG_AN_0_7 0x4C +#define REG_SP_CTRL0 0x58 +#define REG_IP_CTRL1 0x59 +#define REG_IP_CTRL2 0x5A + +#define REG_LINK_DRV 0x5C +#define DRV_HS BIT(1) + +#define REG_DRV_LN_DATA_SEL 0x5D + +#define REG_AUX 0x5E + +#define REG_VID_BUS_CTRL0 0x60 +#define IN_DDR BIT(2) +#define DDR_CD (0x01 << 6) + +#define REG_VID_BUS_CTRL1 0x61 +#define TX_FIFO_RESET BIT(1) + +#define REG_INPUT_CTRL 0xA0 +#define INPUT_HSYNC_POL BIT(0) +#define INPUT_VSYNC_POL BIT(2) +#define INPUT_INTERLACED BIT(4) + +#define REG_INPUT_HTOTAL 0xA1 +#define REG_INPUT_HACTIVE_START 0xA3 +#define REG_INPUT_HACTIVE_WIDTH 0xA5 +#define REG_INPUT_HFRONT_PORCH 0xA7 +#define REG_INPUT_HSYNC_WIDTH 0xA9 +#define REG_INPUT_VTOTAL 0xAB +#define REG_INPUT_VACTIVE_START 0xAD +#define REG_INPUT_VACTIVE_WIDTH 0xAF +#define REG_INPUT_VFRONT_PORCH 0xB1 +#define REG_INPUT_VSYNC_WIDTH 0xB3 + +#define REG_AUDIO_SRC_CTRL 0xB8 +#define M_AUDIO_I2S_EN 0x0F +#define EN_I2S0 BIT(0) +#define EN_I2S1 BIT(1) +#define EN_I2S2 BIT(2) +#define EN_I2S3 BIT(3) +#define AUDIO_FIFO_RESET BIT(7) + +#define REG_AUDIO_FMT 0xB9 +#define REG_AUDIO_FIFO_SEL 0xBA + +#define REG_AUDIO_CTRL0 0xBB +#define AUDIO_FULL_PKT BIT(4) +#define AUDIO_16B_BOUND BIT(5) + +#define REG_AUDIO_CTRL1 0xBC +#define REG_AUDIO_INPUT_FREQ 0xBE + +#define REG_IEC958_STS0 0xBF +#define REG_IEC958_STS1 0xC0 +#define REG_IEC958_STS2 0xC1 +#define REG_IEC958_STS3 0xC2 +#define REG_IEC958_STS4 0xC3 + +#define REG_HPD_IRQ_TIME 0xC9 +#define REG_AUX_DEBUG_MODE 0xCA +#define REG_AUX_OPT2 0xCB +#define REG_HDCP_OPT 0xCE +#define REG_USER_DRV_PRE 0xCF + +#define REG_DATA_MUTE_CTRL 0xD3 +#define ENABLE_ENHANCED_FRAME BIT(0) +#define ENABLE_AUTO_VIDEO_FIFO_RESET BIT(1) +#define EN_VID_MUTE BIT(4) +#define EN_AUD_MUTE BIT(5) + +#define REG_TIME_STMP_CTRL 0xD4 +#define EN_ENHANCE_VID_STMP BIT(0) +#define EN_ENHANCE_AUD_STMP BIT(2) +#define M_STAMP_STEP 0x30 +#define EN_SSC_GAT BIT(6) + +#define REG_INFOFRAME_CTRL 0xE8 +#define EN_AVI_PKT BIT(0) +#define EN_AUD_PKT BIT(1) +#define EN_MPG_PKT BIT(2) +#define EN_GEN_PKT BIT(3) +#define EN_VID_TIME_STMP BIT(4) +#define EN_AUD_TIME_STMP BIT(5) +#define EN_VID_CTRL_PKT (EN_AVI_PKT | EN_VID_TIME_STMP) +#define EN_AUD_CTRL_PKT (EN_AUD_PKT | EN_AUD_TIME_STMP) + +#define REG_AUDIO_N_0_7 0xDE +#define REG_AUDIO_N_8_15 0xDF +#define REG_AUDIO_N_16_23 0xE0 + +#define REG_AVI_INFO_DB1 0xE9 +#define REG_AVI_INFO_DB2 0xEA +#define REG_AVI_INFO_DB3 0xEB +#define REG_AVI_INFO_DB4 0xEC +#define REG_AVI_INFO_DB5 0xED +#define REG_AVI_INFO_SUM 0xF6 + +#define REG_AUD_INFOFRAM_DB1 0xF7 +#define REG_AUD_INFOFRAM_DB2 0xF8 +#define REG_AUD_INFOFRAM_DB3 0xF9 +#define REG_AUD_INFOFRAM_DB4 0xFA +#define REG_AUD_INFOFRAM_SUM 0xFB + +/* the following six registers are in bank1 */ +#define REG_DRV_0_DB_800_MV 0x7E +#define REG_PRE_0_DB_800_MV 0x7F +#define REG_PRE_3P5_DB_800_MV 0x81 +#define REG_SSC_CTRL0 0x88 +#define REG_SSC_CTRL1 0x89 +#define REG_SSC_CTRL2 0x8A + +#define RBR DP_LINK_BW_1_62 +#define HBR DP_LINK_BW_2_7 +#define HBR2 DP_LINK_BW_5_4 +#define HBR3 DP_LINK_BW_8_1 + +#define DPCD_V_1_1 0x11 +#define MISC_VERB 0xF0 +#define MISC_VERC 0x70 +#define I2S_INPUT_FORMAT_STANDARD 0 +#define I2S_INPUT_FORMAT_32BIT 1 +#define I2S_INPUT_LEFT_JUSTIFIED 0 +#define I2S_INPUT_RIGHT_JUSTIFIED 1 +#define I2S_DATA_1T_DELAY 0 +#define I2S_DATA_NO_DELAY 1 +#define I2S_WS_LEFT_CHANNEL 0 +#define I2S_WS_RIGHT_CHANNEL 1 +#define I2S_DATA_MSB_FIRST 0 +#define I2S_DATA_LSB_FIRST 1 +#define WORD_LENGTH_16BIT 0 +#define WORD_LENGTH_18BIT 1 +#define WORD_LENGTH_20BIT 2 +#define WORD_LENGTH_24BIT 3 +#define DEBUGFS_DIR_NAME "it6505-debugfs" +#define READ_BUFFER_SIZE 400 + +/* Vendor option */ +#define HDCP_DESIRED 1 +#define MAX_LANE_COUNT 4 +#define MAX_LINK_RATE HBR +#define AUTO_TRAIN_RETRY 3 +#define MAX_HDCP_DOWN_STREAM_COUNT 10 +#define MAX_CR_LEVEL 0x03 +#define MAX_EQ_LEVEL 0x03 +#define AUX_WAIT_TIMEOUT_MS 15 +#define AUX_FIFO_MAX_SIZE 32 +#define PIXEL_CLK_DELAY 1 +#define PIXEL_CLK_INVERSE 0 +#define ADJUST_PHASE_THRESHOLD 80000 +#define DPI_PIXEL_CLK_MAX 95000 +#define HDCP_SHA1_FIFO_LEN (MAX_HDCP_DOWN_STREAM_COUNT * 5 + 10) +#define DEFAULT_PWR_ON 0 +#define DEFAULT_DRV_HOLD 0 + +#define AUDIO_SELECT I2S +#define AUDIO_TYPE LPCM +#define AUDIO_SAMPLE_RATE SAMPLE_RATE_48K +#define AUDIO_CHANNEL_COUNT 2 +#define I2S_INPUT_FORMAT I2S_INPUT_FORMAT_32BIT +#define I2S_JUSTIFIED I2S_INPUT_LEFT_JUSTIFIED +#define I2S_DATA_DELAY I2S_DATA_1T_DELAY +#define I2S_WS_CHANNEL I2S_WS_LEFT_CHANNEL +#define I2S_DATA_SEQUENCE I2S_DATA_MSB_FIRST +#define AUDIO_WORD_LENGTH WORD_LENGTH_24BIT + +enum aux_cmd_type { + CMD_AUX_NATIVE_READ = 0x0, + CMD_AUX_NATIVE_WRITE = 0x5, + CMD_AUX_I2C_EDID_READ = 0xB, +}; + +enum aux_cmd_reply { + REPLY_ACK, + REPLY_NACK, + REPLY_DEFER, +}; + +enum link_train_status { + LINK_IDLE, + LINK_BUSY, + LINK_OK, +}; + +enum hdcp_state { + HDCP_AUTH_IDLE, + HDCP_AUTH_GOING, + HDCP_AUTH_DONE, +}; + +struct it6505_platform_data { + struct regulator *pwr18; + struct regulator *ovdd; + struct gpio_desc *gpiod_reset; +}; + +enum it6505_audio_select { + I2S = 0, + SPDIF, +}; + +enum it6505_audio_sample_rate { + SAMPLE_RATE_24K = 0x6, + SAMPLE_RATE_32K = 0x3, + SAMPLE_RATE_48K = 0x2, + SAMPLE_RATE_96K = 0xA, + SAMPLE_RATE_192K = 0xE, + SAMPLE_RATE_44_1K = 0x0, + SAMPLE_RATE_88_2K = 0x8, + SAMPLE_RATE_176_4K = 0xC, +}; + +enum it6505_audio_type { + LPCM = 0, + NLPCM, + DSS, +}; + +struct it6505_audio_data { + enum it6505_audio_select select; + enum it6505_audio_sample_rate sample_rate; + enum it6505_audio_type type; + u8 word_length; + u8 channel_count; + u8 i2s_input_format; + u8 i2s_justified; + u8 i2s_data_delay; + u8 i2s_ws_channel; + u8 i2s_data_sequence; +}; + +struct it6505_audio_sample_rate_map { + enum it6505_audio_sample_rate rate; + int sample_rate_value; +}; + +struct it6505_drm_dp_link { + unsigned char revision; + unsigned int rate; + unsigned int num_lanes; + unsigned long capabilities; +}; + +struct debugfs_entries { + char *name; + const struct file_operations *fops; +}; + +struct it6505 { + struct drm_dp_aux aux; + struct drm_bridge bridge; + struct i2c_client *client; + struct it6505_drm_dp_link link; + struct it6505_platform_data pdata; + /* + * Mutex protects extcon and interrupt functions from interfering + * each other. + */ + struct mutex extcon_lock; + struct mutex mode_lock; /* used to bridge_detect */ + struct mutex aux_lock; /* used to aux data transfers */ + struct regmap *regmap; + struct drm_display_mode source_output_mode; + struct drm_display_mode video_info; + struct notifier_block event_nb; + struct extcon_dev *extcon; + struct work_struct extcon_wq; + enum drm_connector_status connector_status; + enum link_train_status link_state; + struct work_struct link_works; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + u8 lane_count; + u8 link_rate_bw_code; + u8 sink_count; + bool step_train; + bool branch_device; + bool enable_ssc; + bool lane_swap_disabled; + bool lane_swap; + bool powered; + bool hpd_state; + u32 afe_setting; + enum hdcp_state hdcp_status; + struct delayed_work hdcp_work; + struct work_struct hdcp_wait_ksv_list; + struct completion wait_edid_complete; + u8 auto_train_retry; + bool hdcp_desired; + bool is_repeater; + u8 hdcp_down_stream_count; + u8 bksvs[DRM_HDCP_KSV_LEN]; + u8 sha1_input[HDCP_SHA1_FIFO_LEN]; + bool enable_enhanced_frame; + hdmi_codec_plugged_cb plugged_cb; + struct device *codec_dev; + struct delayed_work delayed_audio; + struct it6505_audio_data audio; + struct dentry *debugfs; + + /* it6505 driver hold option */ + bool enable_drv_hold; +}; + +struct it6505_step_train_para { + u8 voltage_swing[MAX_LANE_COUNT]; + u8 pre_emphasis[MAX_LANE_COUNT]; +}; + +/* + * Vendor option afe settings for different platforms + * 0: without FPC cable + * 1: with FPC cable + */ + +static const u8 afe_setting_table[][3] = { + {0x82, 0x00, 0x45}, + {0x93, 0x2A, 0x85} +}; + +static const struct it6505_audio_sample_rate_map audio_sample_rate_map[] = { + {SAMPLE_RATE_24K, 24000}, + {SAMPLE_RATE_32K, 32000}, + {SAMPLE_RATE_48K, 48000}, + {SAMPLE_RATE_96K, 96000}, + {SAMPLE_RATE_192K, 192000}, + {SAMPLE_RATE_44_1K, 44100}, + {SAMPLE_RATE_88_2K, 88200}, + {SAMPLE_RATE_176_4K, 176400}, +}; + +static const struct regmap_range it6505_bridge_volatile_ranges[] = { + { .range_min = 0, .range_max = 0xFF }, +}; + +static const struct regmap_access_table it6505_bridge_volatile_table = { + .yes_ranges = it6505_bridge_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(it6505_bridge_volatile_ranges), +}; + +static const struct regmap_config it6505_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_table = &it6505_bridge_volatile_table, + .cache_type = REGCACHE_NONE, +}; + +static int it6505_read(struct it6505 *it6505, unsigned int reg_addr) +{ + unsigned int value; + int err; + struct device *dev = &it6505->client->dev; + + err = regmap_read(it6505->regmap, reg_addr, &value); + if (err < 0) { + dev_err(dev, "read failed reg[0x%x] err: %d", reg_addr, err); + return err; + } + + return value; +} + +static int it6505_write(struct it6505 *it6505, unsigned int reg_addr, + unsigned int reg_val) +{ + int err; + struct device *dev = &it6505->client->dev; + + err = regmap_write(it6505->regmap, reg_addr, reg_val); + + if (err < 0) { + dev_err(dev, "write failed reg[0x%x] = 0x%x err = %d", + reg_addr, reg_val, err); + return err; + } + + return 0; +} + +static int it6505_set_bits(struct it6505 *it6505, unsigned int reg, + unsigned int mask, unsigned int value) +{ + int err; + struct device *dev = &it6505->client->dev; + + err = regmap_update_bits(it6505->regmap, reg, mask, value); + if (err < 0) { + dev_err(dev, "write reg[0x%x] = 0x%x mask = 0x%x failed err %d", + reg, value, mask, err); + return err; + } + + return 0; +} + +static void it6505_debug_print(struct it6505 *it6505, unsigned int reg, + const char *prefix) +{ + struct device *dev = &it6505->client->dev; + int val; + + if (likely(!(__drm_debug & DRM_UT_DRIVER))) + return; + + val = it6505_read(it6505, reg); + if (val < 0) + DRM_DEV_DEBUG_DRIVER(dev, "%s reg[%02x] read error (%d)", + prefix, reg, val); + else + DRM_DEV_DEBUG_DRIVER(dev, "%s reg[%02x] = 0x%02x", prefix, reg, + val); +} + +static int it6505_dpcd_read(struct it6505 *it6505, unsigned long offset) +{ + u8 value; + int ret; + struct device *dev = &it6505->client->dev; + + ret = drm_dp_dpcd_readb(&it6505->aux, offset, &value); + if (ret < 0) { + dev_err(dev, "DPCD read failed [0x%lx] ret: %d", offset, ret); + return ret; + } + return value; +} + +static int it6505_dpcd_write(struct it6505 *it6505, unsigned long offset, + u8 datain) +{ + int ret; + struct device *dev = &it6505->client->dev; + + ret = drm_dp_dpcd_writeb(&it6505->aux, offset, datain); + if (ret < 0) { + dev_err(dev, "DPCD write failed [0x%lx] ret: %d", offset, ret); + return ret; + } + return 0; +} + +static int it6505_get_dpcd(struct it6505 *it6505, int offset, u8 *dpcd, int num) +{ + int ret; + struct device *dev = &it6505->client->dev; + + ret = drm_dp_dpcd_read(&it6505->aux, offset, dpcd, num); + + if (ret < 0) + return ret; + + DRM_DEV_DEBUG_DRIVER(dev, "ret = %d DPCD[0x%x] = 0x%*ph", ret, offset, + num, dpcd); + + return 0; +} + +static void it6505_dump(struct it6505 *it6505) +{ + unsigned int i, j; + u8 regs[16]; + struct device *dev = &it6505->client->dev; + + for (i = 0; i <= 0xff; i += 16) { + for (j = 0; j < 16; j++) + regs[j] = it6505_read(it6505, i + j); + + DRM_DEV_DEBUG_DRIVER(dev, "[0x%02x] = %16ph", i, regs); + } +} + +static bool it6505_get_sink_hpd_status(struct it6505 *it6505) +{ + int reg_0d; + + reg_0d = it6505_read(it6505, REG_SYSTEM_STS); + + if (reg_0d < 0) + return false; + + return reg_0d & HPD_STS; +} + +static int it6505_read_word(struct it6505 *it6505, unsigned int reg) +{ + int val0, val1; + + val0 = it6505_read(it6505, reg); + if (val0 < 0) + return val0; + + val1 = it6505_read(it6505, reg + 1); + if (val1 < 0) + return val1; + + return (val1 << 8) | val0; +} + +static void it6505_calc_video_info(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + int hsync_pol, vsync_pol, interlaced; + int htotal, hdes, hdew, hfph, hsyncw; + int vtotal, vdes, vdew, vfph, vsyncw; + int rddata, i, pclk, sum = 0; + + usleep_range(10000, 15000); + rddata = it6505_read(it6505, REG_INPUT_CTRL); + hsync_pol = rddata & INPUT_HSYNC_POL; + vsync_pol = (rddata & INPUT_VSYNC_POL) >> 2; + interlaced = (rddata & INPUT_INTERLACED) >> 4; + + htotal = it6505_read_word(it6505, REG_INPUT_HTOTAL) & 0x1FFF; + hdes = it6505_read_word(it6505, REG_INPUT_HACTIVE_START) & 0x1FFF; + hdew = it6505_read_word(it6505, REG_INPUT_HACTIVE_WIDTH) & 0x1FFF; + hfph = it6505_read_word(it6505, REG_INPUT_HFRONT_PORCH) & 0x1FFF; + hsyncw = it6505_read_word(it6505, REG_INPUT_HSYNC_WIDTH) & 0x1FFF; + + vtotal = it6505_read_word(it6505, REG_INPUT_VTOTAL) & 0xFFF; + vdes = it6505_read_word(it6505, REG_INPUT_VACTIVE_START) & 0xFFF; + vdew = it6505_read_word(it6505, REG_INPUT_VACTIVE_WIDTH) & 0xFFF; + vfph = it6505_read_word(it6505, REG_INPUT_VFRONT_PORCH) & 0xFFF; + vsyncw = it6505_read_word(it6505, REG_INPUT_VSYNC_WIDTH) & 0xFFF; + + DRM_DEV_DEBUG_DRIVER(dev, "hsync_pol:%d, vsync_pol:%d, interlaced:%d", + hsync_pol, vsync_pol, interlaced); + DRM_DEV_DEBUG_DRIVER(dev, "hactive_start:%d, vactive_start:%d", + hdes, vdes); + + for (i = 0; i < 10; i++) { + it6505_set_bits(it6505, REG_DATA_CTRL0, ENABLE_PCLK_COUNTER, + ENABLE_PCLK_COUNTER); + usleep_range(10000, 15000); + it6505_set_bits(it6505, REG_DATA_CTRL0, ENABLE_PCLK_COUNTER, + 0x00); + rddata = it6505_read_word(it6505, REG_PCLK_COUNTER_VALUE) & + 0xFFF; + + sum += rddata; + } + + if (sum == 0) { + DRM_DEV_DEBUG_DRIVER(dev, "calc video timing error"); + return; + } + + sum /= 10; + pclk = 13500 * 2048 / sum; + it6505->video_info.clock = pclk; + it6505->video_info.hdisplay = hdew; + it6505->video_info.hsync_start = hdew + hfph; + it6505->video_info.hsync_end = hdew + hfph + hsyncw; + it6505->video_info.htotal = htotal; + it6505->video_info.vdisplay = vdew; + it6505->video_info.vsync_start = vdew + vfph; + it6505->video_info.vsync_end = vdew + vfph + vsyncw; + it6505->video_info.vtotal = vtotal; + + DRM_DEV_DEBUG_DRIVER(dev, DRM_MODE_FMT, + DRM_MODE_ARG(&it6505->video_info)); +} + +static int it6505_drm_dp_link_probe(struct drm_dp_aux *aux, + struct it6505_drm_dp_link *link) +{ + u8 values[3]; + int err; + + memset(link, 0, sizeof(*link)); + + err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values)); + if (err < 0) + return err; + + link->revision = values[0]; + link->rate = drm_dp_bw_code_to_link_rate(values[1]); + link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK; + + if (values[2] & DP_ENHANCED_FRAME_CAP) + link->capabilities = DP_ENHANCED_FRAME_CAP; + + return 0; +} + +static int it6505_drm_dp_link_power_up(struct drm_dp_aux *aux, + struct it6505_drm_dp_link *link) +{ + u8 value; + int err; + + /* DP_SET_POWER register is only available on DPCD v1.1 and later */ + if (link->revision < DPCD_V_1_1) + return 0; + + err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value); + if (err < 0) + return err; + + value &= ~DP_SET_POWER_MASK; + value |= DP_SET_POWER_D0; + + err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value); + if (err < 0) + return err; + + /* + * According to the DP 1.1 specification, a "Sink Device must exit the + * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink + * Control Field" (register 0x600). + */ + usleep_range(1000, 2000); + + return 0; +} + +static void it6505_clear_int(struct it6505 *it6505) +{ + it6505_write(it6505, INT_STATUS_01, 0xFF); + it6505_write(it6505, INT_STATUS_02, 0xFF); + it6505_write(it6505, INT_STATUS_03, 0xFF); +} + +static void it6505_int_mask_enable(struct it6505 *it6505) +{ + it6505_write(it6505, INT_MASK_01, BIT(INT_HPD_CHANGE) | + BIT(INT_RECEIVE_HPD_IRQ) | BIT(INT_SCDT_CHANGE) | + BIT(INT_HDCP_FAIL) | BIT(INT_HDCP_DONE)); + + it6505_write(it6505, INT_MASK_02, BIT(INT_AUX_CMD_FAIL) | + BIT(INT_HDCP_KSV_CHECK) | BIT(INT_AUDIO_FIFO_ERROR)); + + it6505_write(it6505, INT_MASK_03, BIT(INT_LINK_TRAIN_FAIL) | + BIT(INT_VID_FIFO_ERROR) | BIT(INT_IO_LATCH_FIFO_OVERFLOW)); +} + +static void it6505_int_mask_disable(struct it6505 *it6505) +{ + it6505_write(it6505, INT_MASK_01, 0x00); + it6505_write(it6505, INT_MASK_02, 0x00); + it6505_write(it6505, INT_MASK_03, 0x00); +} + +static void it6505_lane_termination_on(struct it6505 *it6505) +{ + int regcf; + + regcf = it6505_read(it6505, REG_USER_DRV_PRE); + + if (regcf == MISC_VERB) + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, 0x80, 0x00); + + if (regcf == MISC_VERC) { + if (it6505->lane_swap) { + switch (it6505->lane_count) { + case 1: + case 2: + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, + 0x0C, 0x08); + break; + default: + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, + 0x0C, 0x0C); + break; + } + } else { + switch (it6505->lane_count) { + case 1: + case 2: + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, + 0x0C, 0x04); + break; + default: + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, + 0x0C, 0x0C); + break; + } + } + } +} + +static void it6505_lane_termination_off(struct it6505 *it6505) +{ + int regcf; + + regcf = it6505_read(it6505, REG_USER_DRV_PRE); + + if (regcf == MISC_VERB) + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, 0x80, 0x80); + + if (regcf == MISC_VERC) + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, 0x0C, 0x00); +} + +static void it6505_lane_power_on(struct it6505 *it6505) +{ + it6505_set_bits(it6505, REG_LINK_DRV, 0xF1, + (it6505->lane_swap ? + GENMASK(7, 8 - it6505->lane_count) : + GENMASK(3 + it6505->lane_count, 4)) | + 0x01); +} + +static void it6505_lane_power_off(struct it6505 *it6505) +{ + it6505_set_bits(it6505, REG_LINK_DRV, 0xF0, 0x00); +} + +static void it6505_lane_off(struct it6505 *it6505) +{ + it6505_lane_power_off(it6505); + it6505_lane_termination_off(it6505); +} + +static void it6505_aux_termination_on(struct it6505 *it6505) +{ + int regcf; + + regcf = it6505_read(it6505, REG_USER_DRV_PRE); + + if (regcf == MISC_VERB) + it6505_lane_termination_on(it6505); + + if (regcf == MISC_VERC) + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, 0x80, 0x80); +} + +static void it6505_aux_power_on(struct it6505 *it6505) +{ + it6505_set_bits(it6505, REG_AUX, 0x02, 0x02); +} + +static void it6505_aux_on(struct it6505 *it6505) +{ + it6505_aux_power_on(it6505); + it6505_aux_termination_on(it6505); +} + +static void it6505_aux_reset(struct it6505 *it6505) +{ + it6505_set_bits(it6505, REG_RESET_CTRL, AUX_RESET, AUX_RESET); + it6505_set_bits(it6505, REG_RESET_CTRL, AUX_RESET, 0x00); +} + +static void it6505_reset_logic(struct it6505 *it6505) +{ + regmap_write(it6505->regmap, REG_RESET_CTRL, ALL_LOGIC_RESET); + usleep_range(1000, 1500); +} + +static bool it6505_aux_op_finished(struct it6505 *it6505) +{ + int reg2b = it6505_read(it6505, REG_AUX_CMD_REQ); + + if (reg2b < 0) + return false; + + return (reg2b & AUX_BUSY) == 0; +} + +static int it6505_aux_wait(struct it6505 *it6505) +{ + int status; + unsigned long timeout; + struct device *dev = &it6505->client->dev; + + timeout = jiffies + msecs_to_jiffies(AUX_WAIT_TIMEOUT_MS) + 1; + + while (!it6505_aux_op_finished(it6505)) { + if (time_after(jiffies, timeout)) { + dev_err(dev, "Timed out waiting AUX to finish"); + return -ETIMEDOUT; + } + usleep_range(1000, 2000); + } + + status = it6505_read(it6505, REG_AUX_ERROR_STS); + if (status < 0) { + dev_err(dev, "Failed to read AUX channel: %d", status); + return status; + } + + return 0; +} + +static ssize_t it6505_aux_operation(struct it6505 *it6505, + enum aux_cmd_type cmd, + unsigned int address, u8 *buffer, + size_t size, enum aux_cmd_reply *reply) +{ + int i, ret; + bool aux_write_check = false; + + if (!it6505_get_sink_hpd_status(it6505)) + return -EIO; + + /* set AUX user mode */ + it6505_set_bits(it6505, REG_AUX_CTRL, AUX_USER_MODE, AUX_USER_MODE); + +aux_op_start: + if (cmd == CMD_AUX_I2C_EDID_READ) { + /* AUX EDID FIFO has max length of AUX_FIFO_MAX_SIZE bytes. */ + size = min_t(size_t, size, AUX_FIFO_MAX_SIZE); + /* Enable AUX FIFO read back and clear FIFO */ + it6505_set_bits(it6505, REG_AUX_CTRL, + AUX_EN_FIFO_READ | CLR_EDID_FIFO, + AUX_EN_FIFO_READ | CLR_EDID_FIFO); + + it6505_set_bits(it6505, REG_AUX_CTRL, + AUX_EN_FIFO_READ | CLR_EDID_FIFO, + AUX_EN_FIFO_READ); + } else { + /* The DP AUX transmit buffer has 4 bytes. */ + size = min_t(size_t, size, 4); + it6505_set_bits(it6505, REG_AUX_CTRL, AUX_NO_SEGMENT_WR, + AUX_NO_SEGMENT_WR); + } + + /* Start Address[7:0] */ + it6505_write(it6505, REG_AUX_ADR_0_7, (address >> 0) & 0xFF); + /* Start Address[15:8] */ + it6505_write(it6505, REG_AUX_ADR_8_15, (address >> 8) & 0xFF); + /* WriteNum[3:0]+StartAdr[19:16] */ + it6505_write(it6505, REG_AUX_ADR_16_19, + ((address >> 16) & 0x0F) | ((size - 1) << 4)); + + if (cmd == CMD_AUX_NATIVE_WRITE) + regmap_bulk_write(it6505->regmap, REG_AUX_OUT_DATA0, buffer, + size); + + /* Aux Fire */ + it6505_write(it6505, REG_AUX_CMD_REQ, cmd); + + ret = it6505_aux_wait(it6505); + if (ret < 0) + goto aux_op_err; + + ret = it6505_read(it6505, REG_AUX_ERROR_STS); + if (ret < 0) + goto aux_op_err; + + switch ((ret >> 6) & 0x3) { + case 0: + *reply = REPLY_ACK; + break; + case 1: + *reply = REPLY_DEFER; + ret = -EAGAIN; + goto aux_op_err; + case 2: + *reply = REPLY_NACK; + ret = -EIO; + goto aux_op_err; + case 3: + ret = -ETIMEDOUT; + goto aux_op_err; + } + + /* Read back Native Write data */ + if (cmd == CMD_AUX_NATIVE_WRITE) { + aux_write_check = true; + cmd = CMD_AUX_NATIVE_READ; + goto aux_op_start; + } + + if (cmd == CMD_AUX_I2C_EDID_READ) { + for (i = 0; i < size; i++) { + ret = it6505_read(it6505, REG_AUX_DATA_FIFO); + if (ret < 0) + goto aux_op_err; + buffer[i] = ret; + } + } else { + for (i = 0; i < size; i++) { + ret = it6505_read(it6505, REG_AUX_DATA_0_7 + i); + if (ret < 0) + goto aux_op_err; + + if (aux_write_check && buffer[size - 1 - i] != ret) { + ret = -EINVAL; + goto aux_op_err; + } + + buffer[size - 1 - i] = ret; + } + } + + ret = i; + +aux_op_err: + if (cmd == CMD_AUX_I2C_EDID_READ) { + /* clear AUX FIFO */ + it6505_set_bits(it6505, REG_AUX_CTRL, + AUX_EN_FIFO_READ | CLR_EDID_FIFO, + AUX_EN_FIFO_READ | CLR_EDID_FIFO); + it6505_set_bits(it6505, REG_AUX_CTRL, + AUX_EN_FIFO_READ | CLR_EDID_FIFO, 0x00); + } + + /* Leave AUX user mode */ + it6505_set_bits(it6505, REG_AUX_CTRL, AUX_USER_MODE, 0); + + return ret; +} + +static ssize_t it6505_aux_do_transfer(struct it6505 *it6505, + enum aux_cmd_type cmd, + unsigned int address, u8 *buffer, + size_t size, enum aux_cmd_reply *reply) +{ + int i, ret_size, ret = 0, request_size; + + mutex_lock(&it6505->aux_lock); + for (i = 0; i < size; i += 4) { + request_size = min((int)size - i, 4); + ret_size = it6505_aux_operation(it6505, cmd, address + i, + buffer + i, request_size, + reply); + if (ret_size < 0) { + ret = ret_size; + goto aux_op_err; + } + + ret += ret_size; + } + +aux_op_err: + mutex_unlock(&it6505->aux_lock); + return ret; +} + +static ssize_t it6505_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + struct it6505 *it6505 = container_of(aux, struct it6505, aux); + u8 cmd; + bool is_i2c = !(msg->request & DP_AUX_NATIVE_WRITE); + int ret; + enum aux_cmd_reply reply; + + /* IT6505 doesn't support arbitrary I2C read / write. */ + if (is_i2c) + return -EINVAL; + + switch (msg->request) { + case DP_AUX_NATIVE_READ: + cmd = CMD_AUX_NATIVE_READ; + break; + case DP_AUX_NATIVE_WRITE: + cmd = CMD_AUX_NATIVE_WRITE; + break; + default: + return -EINVAL; + } + + ret = it6505_aux_do_transfer(it6505, cmd, msg->address, msg->buffer, + msg->size, &reply); + if (ret < 0) + return ret; + + switch (reply) { + case REPLY_ACK: + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + break; + case REPLY_NACK: + msg->reply = DP_AUX_NATIVE_REPLY_NACK; + break; + case REPLY_DEFER: + msg->reply = DP_AUX_NATIVE_REPLY_DEFER; + break; + } + + return ret; +} + +static int it6505_get_edid_block(void *data, u8 *buf, unsigned int block, + size_t len) +{ + struct it6505 *it6505 = data; + struct device *dev = &it6505->client->dev; + enum aux_cmd_reply reply; + int offset, ret, aux_retry = 100; + + it6505_aux_reset(it6505); + DRM_DEV_DEBUG_DRIVER(dev, "block number = %d", block); + + for (offset = 0; offset < EDID_LENGTH;) { + ret = it6505_aux_do_transfer(it6505, CMD_AUX_I2C_EDID_READ, + block * EDID_LENGTH + offset, + buf + offset, 8, &reply); + + if (ret < 0 && ret != -EAGAIN) + return ret; + + switch (reply) { + case REPLY_ACK: + DRM_DEV_DEBUG_DRIVER(dev, "[0x%02x]: %8ph", offset, + buf + offset); + offset += 8; + aux_retry = 100; + break; + case REPLY_NACK: + return -EIO; + case REPLY_DEFER: + msleep(20); + if (!(--aux_retry)) + return -EIO; + } + } + + return 0; +} + +static void it6505_variable_config(struct it6505 *it6505) +{ + it6505->link_rate_bw_code = HBR; + it6505->lane_count = MAX_LANE_COUNT; + it6505->link_state = LINK_IDLE; + it6505->hdcp_desired = HDCP_DESIRED; + it6505->auto_train_retry = AUTO_TRAIN_RETRY; + it6505->audio.select = AUDIO_SELECT; + it6505->audio.sample_rate = AUDIO_SAMPLE_RATE; + it6505->audio.channel_count = AUDIO_CHANNEL_COUNT; + it6505->audio.type = AUDIO_TYPE; + it6505->audio.i2s_input_format = I2S_INPUT_FORMAT; + it6505->audio.i2s_justified = I2S_JUSTIFIED; + it6505->audio.i2s_data_delay = I2S_DATA_DELAY; + it6505->audio.i2s_ws_channel = I2S_WS_CHANNEL; + it6505->audio.i2s_data_sequence = I2S_DATA_SEQUENCE; + it6505->audio.word_length = AUDIO_WORD_LENGTH; + memset(it6505->sha1_input, 0, sizeof(it6505->sha1_input)); + memset(it6505->bksvs, 0, sizeof(it6505->bksvs)); +} + +static int it6505_send_video_infoframe(struct it6505 *it6505, + struct hdmi_avi_infoframe *frame) +{ + u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE]; + int err; + struct device *dev = &it6505->client->dev; + + err = hdmi_avi_infoframe_pack(frame, buffer, sizeof(buffer)); + if (err < 0) { + dev_err(dev, "Failed to pack AVI infoframe: %d", err); + return err; + } + + err = it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_AVI_PKT, 0x00); + if (err) + return err; + + err = regmap_bulk_write(it6505->regmap, REG_AVI_INFO_DB1, + buffer + HDMI_INFOFRAME_HEADER_SIZE, + frame->length); + if (err) + return err; + + err = it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_AVI_PKT, + EN_AVI_PKT); + if (err) + return err; + + return 0; +} + +static void it6505_get_extcon_property(struct it6505 *it6505) +{ + int err; + union extcon_property_value property; + struct device *dev = &it6505->client->dev; + + if (it6505->extcon && !it6505->lane_swap_disabled) { + err = extcon_get_property(it6505->extcon, EXTCON_DISP_DP, + EXTCON_PROP_USB_TYPEC_POLARITY, + &property); + if (err) { + dev_err(dev, "get property fail!"); + return; + } + it6505->lane_swap = property.intval; + } +} + +static void it6505_clk_phase_adjustment(struct it6505 *it6505, + const struct drm_display_mode *mode) +{ + int clock = mode->clock; + + it6505_set_bits(it6505, REG_CLK_CTRL0, M_PCLK_DELAY, + clock < ADJUST_PHASE_THRESHOLD ? PIXEL_CLK_DELAY : 0); + it6505_set_bits(it6505, REG_DATA_CTRL0, VIDEO_LATCH_EDGE, + PIXEL_CLK_INVERSE << 4); +} + +static void it6505_link_reset_step_train(struct it6505 *it6505) +{ + it6505_set_bits(it6505, REG_TRAIN_CTRL0, + FORCE_CR_DONE | FORCE_EQ_DONE, 0x00); + it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_DISABLE); +} + +static void it6505_init(struct it6505 *it6505) +{ + it6505_write(it6505, REG_AUX_OPT, AUX_AUTO_RST | AUX_FIX_FREQ); + it6505_write(it6505, REG_AUX_CTRL, AUX_NO_SEGMENT_WR); + it6505_write(it6505, REG_HDCP_CTRL2, HDCP_AN_SEL | HDCP_HW_HPDIRQ_ACT); + it6505_write(it6505, REG_VID_BUS_CTRL0, IN_DDR | DDR_CD); + it6505_write(it6505, REG_VID_BUS_CTRL1, 0x01); + it6505_write(it6505, REG_AUDIO_CTRL0, AUDIO_16B_BOUND); + + /* chip internal setting, don't modify */ + it6505_write(it6505, REG_HPD_IRQ_TIME, 0xF5); + it6505_write(it6505, REG_AUX_DEBUG_MODE, 0x4D); + it6505_write(it6505, REG_AUX_OPT2, 0x17); + it6505_write(it6505, REG_HDCP_OPT, 0x60); + it6505_write(it6505, REG_DATA_MUTE_CTRL, + EN_VID_MUTE | EN_AUD_MUTE | ENABLE_AUTO_VIDEO_FIFO_RESET); + it6505_write(it6505, REG_TIME_STMP_CTRL, + EN_SSC_GAT | EN_ENHANCE_VID_STMP | EN_ENHANCE_AUD_STMP); + it6505_write(it6505, REG_INFOFRAME_CTRL, 0x00); + it6505_write(it6505, REG_BANK_SEL, 0x01); + it6505_write(it6505, REG_DRV_0_DB_800_MV, + afe_setting_table[it6505->afe_setting][0]); + it6505_write(it6505, REG_PRE_0_DB_800_MV, + afe_setting_table[it6505->afe_setting][1]); + it6505_write(it6505, REG_PRE_3P5_DB_800_MV, + afe_setting_table[it6505->afe_setting][2]); + it6505_write(it6505, REG_SSC_CTRL0, 0x9E); + it6505_write(it6505, REG_SSC_CTRL1, 0x1C); + it6505_write(it6505, REG_SSC_CTRL2, 0x42); + it6505_write(it6505, REG_BANK_SEL, 0x00); +} + +static void it6505_video_disable(struct it6505 *it6505) +{ + it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_VID_MUTE, EN_VID_MUTE); + it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_VID_CTRL_PKT, 0x00); + it6505_set_bits(it6505, REG_RESET_CTRL, VIDEO_RESET, VIDEO_RESET); +} + +static void it6505_video_reset(struct it6505 *it6505) +{ + it6505_link_reset_step_train(it6505); + it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_VID_MUTE, EN_VID_MUTE); + it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_VID_CTRL_PKT, 0x00); + it6505_set_bits(it6505, REG_RESET_CTRL, VIDEO_RESET, VIDEO_RESET); + it6505_set_bits(it6505, REG_501_FIFO_CTRL, RST_501_FIFO, RST_501_FIFO); + it6505_set_bits(it6505, REG_501_FIFO_CTRL, RST_501_FIFO, 0x00); + it6505_set_bits(it6505, REG_RESET_CTRL, VIDEO_RESET, 0x00); +} + +static void it6505_update_video_parameter(struct it6505 *it6505, + const struct drm_display_mode *mode) +{ + it6505_clk_phase_adjustment(it6505, mode); + it6505_video_disable(it6505); +} + +static bool it6505_audio_input(struct it6505 *it6505) +{ + int reg05, regbe; + + reg05 = it6505_read(it6505, REG_RESET_CTRL); + it6505_set_bits(it6505, REG_RESET_CTRL, AUDIO_RESET, 0x00); + usleep_range(3000, 4000); + regbe = it6505_read(it6505, REG_AUDIO_INPUT_FREQ); + it6505_write(it6505, REG_RESET_CTRL, reg05); + + return regbe != 0xFF; +} + +static void it6505_setup_audio_channel_status(struct it6505 *it6505) +{ + enum it6505_audio_sample_rate sample_rate = it6505->audio.sample_rate; + u8 audio_word_length_map[] = { 0x02, 0x04, 0x03, 0x0B }; + + /* Channel Status */ + it6505_write(it6505, REG_IEC958_STS0, it6505->audio.type << 1); + it6505_write(it6505, REG_IEC958_STS1, 0x00); + it6505_write(it6505, REG_IEC958_STS2, 0x00); + it6505_write(it6505, REG_IEC958_STS3, sample_rate); + it6505_write(it6505, REG_IEC958_STS4, (~sample_rate << 4) | + audio_word_length_map[it6505->audio.word_length]); +} + +static void it6505_setup_audio_format(struct it6505 *it6505) +{ + /* I2S MODE */ + it6505_write(it6505, REG_AUDIO_FMT, + (it6505->audio.word_length << 5) | + (it6505->audio.i2s_data_sequence << 4) | + (it6505->audio.i2s_ws_channel << 3) | + (it6505->audio.i2s_data_delay << 2) | + (it6505->audio.i2s_justified << 1) | + it6505->audio.i2s_input_format); + if (it6505->audio.select == SPDIF) { + it6505_write(it6505, REG_AUDIO_FIFO_SEL, 0x00); + /* 0x30 = 128*FS */ + it6505_set_bits(it6505, REG_AUX_OPT, 0xF0, 0x30); + } else { + it6505_write(it6505, REG_AUDIO_FIFO_SEL, 0xE4); + } + + it6505_write(it6505, REG_AUDIO_CTRL0, 0x20); + it6505_write(it6505, REG_AUDIO_CTRL1, 0x00); +} + +static void it6505_enable_audio_source(struct it6505 *it6505) +{ + unsigned int audio_source_count; + + audio_source_count = BIT(DIV_ROUND_UP(it6505->audio.channel_count, 2)) + - 1; + + audio_source_count |= it6505->audio.select << 4; + + it6505_write(it6505, REG_AUDIO_SRC_CTRL, audio_source_count); +} + +static void it6505_enable_audio_infoframe(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + u8 audio_info_ca[] = { 0x00, 0x00, 0x01, 0x03, 0x07, 0x0B, 0x0F, 0x1F }; + + DRM_DEV_DEBUG_DRIVER(dev, "infoframe channel_allocation:0x%02x", + audio_info_ca[it6505->audio.channel_count - 1]); + + it6505_write(it6505, REG_AUD_INFOFRAM_DB1, it6505->audio.channel_count + - 1); + it6505_write(it6505, REG_AUD_INFOFRAM_DB2, 0x00); + it6505_write(it6505, REG_AUD_INFOFRAM_DB3, + audio_info_ca[it6505->audio.channel_count - 1]); + it6505_write(it6505, REG_AUD_INFOFRAM_DB4, 0x00); + it6505_write(it6505, REG_AUD_INFOFRAM_SUM, 0x00); + + /* Enable Audio InfoFrame */ + it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_AUD_CTRL_PKT, + EN_AUD_CTRL_PKT); +} + +static void it6505_disable_audio(struct it6505 *it6505) +{ + it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_AUD_MUTE, EN_AUD_MUTE); + it6505_set_bits(it6505, REG_AUDIO_SRC_CTRL, M_AUDIO_I2S_EN, 0x00); + it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_AUD_CTRL_PKT, 0x00); + it6505_set_bits(it6505, REG_RESET_CTRL, AUDIO_RESET, AUDIO_RESET); +} + +static void it6505_enable_audio(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + int regbe; + + DRM_DEV_DEBUG_DRIVER(dev, "start"); + it6505_disable_audio(it6505); + + it6505_setup_audio_channel_status(it6505); + it6505_setup_audio_format(it6505); + it6505_enable_audio_source(it6505); + it6505_enable_audio_infoframe(it6505); + + it6505_write(it6505, REG_AUDIO_N_0_7, 0x00); + it6505_write(it6505, REG_AUDIO_N_8_15, 0x80); + it6505_write(it6505, REG_AUDIO_N_16_23, 0x00); + + it6505_set_bits(it6505, REG_AUDIO_SRC_CTRL, AUDIO_FIFO_RESET, + AUDIO_FIFO_RESET); + it6505_set_bits(it6505, REG_AUDIO_SRC_CTRL, AUDIO_FIFO_RESET, 0x00); + it6505_set_bits(it6505, REG_RESET_CTRL, AUDIO_RESET, 0x00); + regbe = it6505_read(it6505, REG_AUDIO_INPUT_FREQ); + DRM_DEV_DEBUG_DRIVER(dev, "regbe:0x%02x audio input fs: %d.%d kHz", + regbe, 6750 / regbe, (6750 % regbe) * 10 / regbe); + it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_AUD_MUTE, 0x00); +} + +static bool it6505_use_step_train_check(struct it6505 *it6505) +{ + if (it6505->link.revision >= 0x12) + return it6505->dpcd[DP_TRAINING_AUX_RD_INTERVAL] >= 0x01; + + return true; +} + +static void it6505_parse_link_capabilities(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + struct it6505_drm_dp_link *link = &it6505->link; + int bcaps; + + if (it6505->dpcd[0] == 0) { + it6505_aux_on(it6505); + it6505_get_dpcd(it6505, DP_DPCD_REV, it6505->dpcd, + ARRAY_SIZE(it6505->dpcd)); + } + + DRM_DEV_DEBUG_DRIVER(dev, "DPCD Rev.: %d.%d", + link->revision >> 4, link->revision & 0x0F); + + DRM_DEV_DEBUG_DRIVER(dev, "Sink max link rate: %d.%02d Gbps per lane", + link->rate / 100000, link->rate / 1000 % 100); + + it6505->link_rate_bw_code = drm_dp_link_rate_to_bw_code(link->rate); + DRM_DEV_DEBUG_DRIVER(dev, "link rate bw code:0x%02x", + it6505->link_rate_bw_code); + it6505->link_rate_bw_code = min_t(int, it6505->link_rate_bw_code, + MAX_LINK_RATE); + + it6505->lane_count = link->num_lanes; + DRM_DEV_DEBUG_DRIVER(dev, "Sink support %d lanes training", + it6505->lane_count); + it6505->lane_count = min_t(int, it6505->lane_count, MAX_LANE_COUNT); + + it6505->branch_device = drm_dp_is_branch(it6505->dpcd); + DRM_DEV_DEBUG_DRIVER(dev, "Sink %sbranch device", + it6505->branch_device ? "" : "Not "); + + it6505->enable_enhanced_frame = link->capabilities; + DRM_DEV_DEBUG_DRIVER(dev, "Sink %sSupport Enhanced Framing", + it6505->enable_enhanced_frame ? "" : "Not "); + + it6505->enable_ssc = (it6505->dpcd[DP_MAX_DOWNSPREAD] & + DP_MAX_DOWNSPREAD_0_5); + DRM_DEV_DEBUG_DRIVER(dev, "Maximum Down-Spread: %s, %ssupport SSC!", + it6505->enable_ssc ? "0.5" : "0", + it6505->enable_ssc ? "" : "Not "); + + it6505->step_train = it6505_use_step_train_check(it6505); + if (it6505->step_train) + DRM_DEV_DEBUG_DRIVER(dev, "auto train fail, will step train"); + + bcaps = it6505_dpcd_read(it6505, DP_AUX_HDCP_BCAPS); + DRM_DEV_DEBUG_DRIVER(dev, "bcaps:0x%02x", bcaps); + if (bcaps & DP_BCAPS_HDCP_CAPABLE) { + it6505->is_repeater = (bcaps & DP_BCAPS_REPEATER_PRESENT); + DRM_DEV_DEBUG_DRIVER(dev, "Support HDCP! Downstream is %s!", + it6505->is_repeater ? "repeater" : + "receiver"); + } else { + DRM_DEV_DEBUG_DRIVER(dev, "Sink not support HDCP!"); + it6505->hdcp_desired = false; + } + DRM_DEV_DEBUG_DRIVER(dev, "HDCP %s", + it6505->hdcp_desired ? "desired" : "undesired"); +} + +static void it6505_setup_ssc(struct it6505 *it6505) +{ + it6505_set_bits(it6505, REG_TRAIN_CTRL0, SPREAD_AMP_5, + it6505->enable_ssc ? SPREAD_AMP_5 : 0x00); + if (it6505->enable_ssc) { + it6505_write(it6505, REG_BANK_SEL, 0x01); + it6505_write(it6505, REG_SSC_CTRL0, 0x9E); + it6505_write(it6505, REG_SSC_CTRL1, 0x1C); + it6505_write(it6505, REG_SSC_CTRL2, 0x42); + it6505_write(it6505, REG_BANK_SEL, 0x00); + it6505_write(it6505, REG_SP_CTRL0, 0x07); + it6505_write(it6505, REG_IP_CTRL1, 0x29); + it6505_write(it6505, REG_IP_CTRL2, 0x03); + /* Stamp Interrupt Step */ + it6505_set_bits(it6505, REG_TIME_STMP_CTRL, M_STAMP_STEP, + 0x10); + it6505_dpcd_write(it6505, DP_DOWNSPREAD_CTRL, + DP_SPREAD_AMP_0_5); + } else { + it6505_dpcd_write(it6505, DP_DOWNSPREAD_CTRL, 0x00); + it6505_set_bits(it6505, REG_TIME_STMP_CTRL, M_STAMP_STEP, + 0x00); + } +} + +static inline void it6505_link_rate_setup(struct it6505 *it6505) +{ + it6505_set_bits(it6505, REG_TRAIN_CTRL0, FORCE_LBR, + (it6505->link_rate_bw_code == RBR) ? FORCE_LBR : 0x00); + it6505_set_bits(it6505, REG_LINK_DRV, DRV_HS, + (it6505->link_rate_bw_code == RBR) ? 0x00 : DRV_HS); +} + +static void it6505_lane_count_setup(struct it6505 *it6505) +{ + it6505_get_extcon_property(it6505); + it6505_set_bits(it6505, REG_TRAIN_CTRL0, LANE_SWAP, + it6505->lane_swap ? LANE_SWAP : 0x00); + it6505_set_bits(it6505, REG_TRAIN_CTRL0, LANE_COUNT_MASK, + (it6505->lane_count - 1) << 1); +} + +static void it6505_link_training_setup(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + + if (it6505->enable_enhanced_frame) + it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, + ENABLE_ENHANCED_FRAME, ENABLE_ENHANCED_FRAME); + + it6505_link_rate_setup(it6505); + it6505_lane_count_setup(it6505); + it6505_setup_ssc(it6505); + DRM_DEV_DEBUG_DRIVER(dev, + "%s, %d lanes, %sable ssc, %sable enhanced frame", + it6505->link_rate_bw_code != RBR ? "HBR" : "RBR", + it6505->lane_count, + it6505->enable_ssc ? "en" : "dis", + it6505->enable_enhanced_frame ? "en" : "dis"); +} + +static bool it6505_link_start_auto_train(struct it6505 *it6505) +{ + int timeout = 500, link_training_state; + bool state = false; + + mutex_lock(&it6505->aux_lock); + it6505_set_bits(it6505, REG_TRAIN_CTRL0, + FORCE_CR_DONE | FORCE_EQ_DONE, 0x00); + it6505_write(it6505, REG_TRAIN_CTRL1, FORCE_RETRAIN); + it6505_write(it6505, REG_TRAIN_CTRL1, AUTO_TRAIN); + + while (timeout > 0) { + usleep_range(1000, 2000); + link_training_state = it6505_read(it6505, REG_LINK_TRAIN_STS); + + if (link_training_state > 0 && + (link_training_state & LINK_STATE_NORP)) { + state = true; + goto unlock; + } + + timeout--; + } +unlock: + mutex_unlock(&it6505->aux_lock); + + return state; +} + +static int it6505_drm_dp_link_configure(struct it6505 *it6505) +{ + u8 values[2]; + int err; + struct drm_dp_aux *aux = &it6505->aux; + + values[0] = it6505->link_rate_bw_code; + values[1] = it6505->lane_count; + + if (it6505->enable_enhanced_frame) + values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + + err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values)); + if (err < 0) + return err; + + return 0; +} + +static bool it6505_check_voltage_swing_max(u8 lane_voltage_swing_pre_emphasis) +{ + return ((lane_voltage_swing_pre_emphasis & 0x03) == MAX_CR_LEVEL); +} + +static bool it6505_check_pre_emphasis_max(u8 lane_voltage_swing_pre_emphasis) +{ + return ((lane_voltage_swing_pre_emphasis & 0x03) == MAX_EQ_LEVEL); +} + +static bool it6505_check_max_voltage_swing_reached(u8 *lane_voltage_swing, + u8 lane_count) +{ + u8 i; + + for (i = 0; i < lane_count; i++) { + if (lane_voltage_swing[i] & DP_TRAIN_MAX_SWING_REACHED) + return true; + } + + return false; +} + +static bool +step_train_lane_voltage_para_set(struct it6505 *it6505, + struct it6505_step_train_para + *lane_voltage_pre_emphasis, + u8 *lane_voltage_pre_emphasis_set) +{ + u8 *voltage_swing = lane_voltage_pre_emphasis->voltage_swing; + u8 *pre_emphasis = lane_voltage_pre_emphasis->pre_emphasis; + u8 i; + + for (i = 0; i < it6505->lane_count; i++) { + voltage_swing[i] &= 0x03; + lane_voltage_pre_emphasis_set[i] = voltage_swing[i]; + if (it6505_check_voltage_swing_max(voltage_swing[i])) + lane_voltage_pre_emphasis_set[i] |= + DP_TRAIN_MAX_SWING_REACHED; + + pre_emphasis[i] &= 0x03; + lane_voltage_pre_emphasis_set[i] |= pre_emphasis[i] + << DP_TRAIN_PRE_EMPHASIS_SHIFT; + if (it6505_check_pre_emphasis_max(pre_emphasis[i])) + lane_voltage_pre_emphasis_set[i] |= + DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + it6505_dpcd_write(it6505, DP_TRAINING_LANE0_SET + i, + lane_voltage_pre_emphasis_set[i]); + + if (lane_voltage_pre_emphasis_set[i] != + it6505_dpcd_read(it6505, DP_TRAINING_LANE0_SET + i)) + return false; + } + + return true; +} + +static bool +it6505_step_cr_train(struct it6505 *it6505, + struct it6505_step_train_para *lane_voltage_pre_emphasis) +{ + u8 loop_count = 0, i = 0, j; + u8 link_status[DP_LINK_STATUS_SIZE] = { 0 }; + u8 lane_level_config[MAX_LANE_COUNT] = { 0 }; + int pre_emphasis_adjust = -1, voltage_swing_adjust = -1; + const struct drm_dp_aux *aux = &it6505->aux; + + it6505_dpcd_write(it6505, DP_DOWNSPREAD_CTRL, + it6505->enable_ssc ? DP_SPREAD_AMP_0_5 : 0x00); + it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_1); + + while (loop_count < 5 && i < 10) { + i++; + if (!step_train_lane_voltage_para_set(it6505, + lane_voltage_pre_emphasis, + lane_level_config)) + continue; + drm_dp_link_train_clock_recovery_delay(aux, it6505->dpcd); + drm_dp_dpcd_read_link_status(&it6505->aux, link_status); + + if (drm_dp_clock_recovery_ok(link_status, it6505->lane_count)) { + it6505_set_bits(it6505, REG_TRAIN_CTRL0, FORCE_CR_DONE, + FORCE_CR_DONE); + return true; + } + DRM_DEV_DEBUG_DRIVER(&it6505->client->dev, "cr not done"); + + if (it6505_check_max_voltage_swing_reached(lane_level_config, + it6505->lane_count)) + goto cr_train_fail; + + for (j = 0; j < it6505->lane_count; j++) { + lane_voltage_pre_emphasis->voltage_swing[j] = + drm_dp_get_adjust_request_voltage(link_status, + j) >> + DP_TRAIN_VOLTAGE_SWING_SHIFT; + lane_voltage_pre_emphasis->pre_emphasis[j] = + drm_dp_get_adjust_request_pre_emphasis(link_status, + j) >> + DP_TRAIN_PRE_EMPHASIS_SHIFT; + if (voltage_swing_adjust == + lane_voltage_pre_emphasis->voltage_swing[j] && + pre_emphasis_adjust == + lane_voltage_pre_emphasis->pre_emphasis[j]) { + loop_count++; + continue; + } + + voltage_swing_adjust = + lane_voltage_pre_emphasis->voltage_swing[j]; + pre_emphasis_adjust = + lane_voltage_pre_emphasis->pre_emphasis[j]; + loop_count = 0; + + if (voltage_swing_adjust + pre_emphasis_adjust > + MAX_EQ_LEVEL) + lane_voltage_pre_emphasis->voltage_swing[j] = + MAX_EQ_LEVEL - + lane_voltage_pre_emphasis + ->pre_emphasis[j]; + } + } + +cr_train_fail: + it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_DISABLE); + + return false; +} + +static bool +it6505_step_eq_train(struct it6505 *it6505, + struct it6505_step_train_para *lane_voltage_pre_emphasis) +{ + u8 loop_count = 0, i, link_status[DP_LINK_STATUS_SIZE] = { 0 }; + u8 lane_level_config[MAX_LANE_COUNT] = { 0 }; + const struct drm_dp_aux *aux = &it6505->aux; + + it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_2); + + while (loop_count < 6) { + loop_count++; + + if (!step_train_lane_voltage_para_set(it6505, + lane_voltage_pre_emphasis, + lane_level_config)) + continue; + + drm_dp_link_train_channel_eq_delay(aux, it6505->dpcd); + drm_dp_dpcd_read_link_status(&it6505->aux, link_status); + + if (!drm_dp_clock_recovery_ok(link_status, it6505->lane_count)) + goto eq_train_fail; + + if (drm_dp_channel_eq_ok(link_status, it6505->lane_count)) { + it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_DISABLE); + it6505_set_bits(it6505, REG_TRAIN_CTRL0, FORCE_EQ_DONE, + FORCE_EQ_DONE); + return true; + } + DRM_DEV_DEBUG_DRIVER(&it6505->client->dev, "eq not done"); + + for (i = 0; i < it6505->lane_count; i++) { + lane_voltage_pre_emphasis->voltage_swing[i] = + drm_dp_get_adjust_request_voltage(link_status, + i) >> + DP_TRAIN_VOLTAGE_SWING_SHIFT; + lane_voltage_pre_emphasis->pre_emphasis[i] = + drm_dp_get_adjust_request_pre_emphasis(link_status, + i) >> + DP_TRAIN_PRE_EMPHASIS_SHIFT; + + if (lane_voltage_pre_emphasis->voltage_swing[i] + + lane_voltage_pre_emphasis->pre_emphasis[i] > + MAX_EQ_LEVEL) + lane_voltage_pre_emphasis->voltage_swing[i] = + 0x03 - lane_voltage_pre_emphasis + ->pre_emphasis[i]; + } + } + +eq_train_fail: + it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_DISABLE); + return false; +} + +static bool it6505_link_start_step_train(struct it6505 *it6505) +{ + int err; + struct it6505_step_train_para lane_voltage_pre_emphasis = { + .voltage_swing = { 0 }, + .pre_emphasis = { 0 }, + }; + + DRM_DEV_DEBUG_DRIVER(&it6505->client->dev, "start"); + err = it6505_drm_dp_link_configure(it6505); + + if (err < 0) + return false; + if (!it6505_step_cr_train(it6505, &lane_voltage_pre_emphasis)) + return false; + if (!it6505_step_eq_train(it6505, &lane_voltage_pre_emphasis)) + return false; + return true; +} + +static bool it6505_get_video_status(struct it6505 *it6505) +{ + int reg_0d; + + reg_0d = it6505_read(it6505, REG_SYSTEM_STS); + + if (reg_0d < 0) + return false; + + return reg_0d & VIDEO_STB; +} + +static void it6505_reset_hdcp(struct it6505 *it6505) +{ + it6505->hdcp_status = HDCP_AUTH_IDLE; + /* Disable CP_Desired */ + it6505_set_bits(it6505, REG_HDCP_CTRL1, HDCP_CP_ENABLE, 0x00); + it6505_set_bits(it6505, REG_RESET_CTRL, HDCP_RESET, HDCP_RESET); +} + +static void it6505_start_hdcp(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "start"); + it6505_reset_hdcp(it6505); + queue_delayed_work(system_wq, &it6505->hdcp_work, + msecs_to_jiffies(2400)); +} + +static void it6505_stop_hdcp(struct it6505 *it6505) +{ + it6505_reset_hdcp(it6505); + cancel_delayed_work(&it6505->hdcp_work); +} + +static bool it6505_hdcp_is_ksv_valid(u8 *ksv) +{ + int i, ones = 0; + + /* KSV has 20 1's and 20 0's */ + for (i = 0; i < DRM_HDCP_KSV_LEN; i++) + ones += hweight8(ksv[i]); + if (ones != 20) + return false; + return true; +} + +static void it6505_hdcp_part1_auth(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + u8 hdcp_bcaps; + + it6505_set_bits(it6505, REG_RESET_CTRL, HDCP_RESET, 0x00); + /* Disable CP_Desired */ + it6505_set_bits(it6505, REG_HDCP_CTRL1, HDCP_CP_ENABLE, 0x00); + + usleep_range(1000, 1500); + hdcp_bcaps = it6505_dpcd_read(it6505, DP_AUX_HDCP_BCAPS); + DRM_DEV_DEBUG_DRIVER(dev, "DPCD[0x68028]: 0x%02x", + hdcp_bcaps); + + if (!hdcp_bcaps) + return; + + /* clear the repeater List Chk Done and fail bit */ + it6505_set_bits(it6505, REG_HDCP_TRIGGER, + HDCP_TRIGGER_KSV_DONE | HDCP_TRIGGER_KSV_FAIL, + 0x00); + + /* Enable An Generator */ + it6505_set_bits(it6505, REG_HDCP_CTRL2, HDCP_AN_GEN, HDCP_AN_GEN); + /* delay1ms(10);*/ + usleep_range(10000, 15000); + /* Stop An Generator */ + it6505_set_bits(it6505, REG_HDCP_CTRL2, HDCP_AN_GEN, 0x00); + + it6505_set_bits(it6505, REG_HDCP_CTRL1, HDCP_CP_ENABLE, HDCP_CP_ENABLE); + + it6505_set_bits(it6505, REG_HDCP_TRIGGER, HDCP_TRIGGER_START, + HDCP_TRIGGER_START); + + it6505->hdcp_status = HDCP_AUTH_GOING; +} + +static int it6505_sha1_digest(struct it6505 *it6505, u8 *sha1_input, + unsigned int size, u8 *output_av) +{ + struct shash_desc *desc; + struct crypto_shash *tfm; + int err; + struct device *dev = &it6505->client->dev; + + tfm = crypto_alloc_shash("sha1", 0, 0); + if (IS_ERR(tfm)) { + dev_err(dev, "crypto_alloc_shash sha1 failed"); + return PTR_ERR(tfm); + } + desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL); + if (!desc) { + crypto_free_shash(tfm); + return -ENOMEM; + } + + desc->tfm = tfm; + err = crypto_shash_digest(desc, sha1_input, size, output_av); + if (err) + dev_err(dev, "crypto_shash_digest sha1 failed"); + + crypto_free_shash(tfm); + kfree(desc); + return err; +} + +static int it6505_setup_sha1_input(struct it6505 *it6505, u8 *sha1_input) +{ + struct device *dev = &it6505->client->dev; + u8 binfo[2]; + int down_stream_count, i, err, msg_count = 0; + + err = it6505_get_dpcd(it6505, DP_AUX_HDCP_BINFO, binfo, + ARRAY_SIZE(binfo)); + + if (err < 0) { + dev_err(dev, "Read binfo value Fail"); + return err; + } + + down_stream_count = binfo[0] & 0x7F; + DRM_DEV_DEBUG_DRIVER(dev, "binfo:0x%*ph", (int)ARRAY_SIZE(binfo), + binfo); + + if ((binfo[0] & BIT(7)) || (binfo[1] & BIT(3))) { + dev_err(dev, "HDCP max cascade device exceed"); + return 0; + } + + if (!down_stream_count || + down_stream_count > MAX_HDCP_DOWN_STREAM_COUNT) { + dev_err(dev, "HDCP down stream count Error %d", + down_stream_count); + return 0; + } + + for (i = 0; i < down_stream_count; i++) { + err = it6505_get_dpcd(it6505, DP_AUX_HDCP_KSV_FIFO + + (i % 3) * DRM_HDCP_KSV_LEN, + sha1_input + msg_count, + DRM_HDCP_KSV_LEN); + + if (err < 0) + return err; + + msg_count += 5; + } + + it6505->hdcp_down_stream_count = down_stream_count; + sha1_input[msg_count++] = binfo[0]; + sha1_input[msg_count++] = binfo[1]; + + it6505_set_bits(it6505, REG_HDCP_CTRL2, HDCP_EN_M0_READ, + HDCP_EN_M0_READ); + + err = regmap_bulk_read(it6505->regmap, REG_M0_0_7, + sha1_input + msg_count, 8); + + it6505_set_bits(it6505, REG_HDCP_CTRL2, HDCP_EN_M0_READ, 0x00); + + if (err < 0) { + dev_err(dev, " Warning, Read M value Fail"); + return err; + } + + msg_count += 8; + + return msg_count; +} + +static bool it6505_hdcp_part2_ksvlist_check(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + u8 av[5][4], bv[5][4]; + int i, err; + + i = it6505_setup_sha1_input(it6505, it6505->sha1_input); + if (i <= 0) { + dev_err(dev, "SHA-1 Input length error %d", i); + return false; + } + + it6505_sha1_digest(it6505, it6505->sha1_input, i, (u8 *)av); + + err = it6505_get_dpcd(it6505, DP_AUX_HDCP_V_PRIME(0), (u8 *)bv, + sizeof(bv)); + + if (err < 0) { + dev_err(dev, "Read V' value Fail"); + return false; + } + + for (i = 0; i < 5; i++) + if (bv[i][3] != av[i][0] || bv[i][2] != av[i][1] || + bv[i][1] != av[i][2] || bv[i][0] != av[i][3]) + return false; + + DRM_DEV_DEBUG_DRIVER(dev, "V' all match!!"); + return true; +} + +static void it6505_hdcp_wait_ksv_list(struct work_struct *work) +{ + struct it6505 *it6505 = container_of(work, struct it6505, + hdcp_wait_ksv_list); + struct device *dev = &it6505->client->dev; + unsigned int timeout = 5000; + u8 bstatus = 0; + bool ksv_list_check; + + timeout /= 20; + while (timeout > 0) { + if (!it6505_get_sink_hpd_status(it6505)) + return; + + bstatus = it6505_dpcd_read(it6505, DP_AUX_HDCP_BSTATUS); + + if (bstatus & DP_BSTATUS_READY) + break; + + msleep(20); + timeout--; + } + + if (timeout == 0) { + DRM_DEV_DEBUG_DRIVER(dev, "timeout and ksv list wait failed"); + goto timeout; + } + + ksv_list_check = it6505_hdcp_part2_ksvlist_check(it6505); + DRM_DEV_DEBUG_DRIVER(dev, "ksv list ready, ksv list check %s", + ksv_list_check ? "pass" : "fail"); + if (ksv_list_check) { + it6505_set_bits(it6505, REG_HDCP_TRIGGER, + HDCP_TRIGGER_KSV_DONE, HDCP_TRIGGER_KSV_DONE); + return; + } +timeout: + it6505_set_bits(it6505, REG_HDCP_TRIGGER, + HDCP_TRIGGER_KSV_DONE | HDCP_TRIGGER_KSV_FAIL, + HDCP_TRIGGER_KSV_DONE | HDCP_TRIGGER_KSV_FAIL); +} + +static void it6505_hdcp_work(struct work_struct *work) +{ + struct it6505 *it6505 = container_of(work, struct it6505, + hdcp_work.work); + struct device *dev = &it6505->client->dev; + int ret; + u8 link_status[DP_LINK_STATUS_SIZE] = { 0 }; + + DRM_DEV_DEBUG_DRIVER(dev, "start"); + + if (!it6505_get_sink_hpd_status(it6505)) + return; + + ret = drm_dp_dpcd_read_link_status(&it6505->aux, link_status); + DRM_DEV_DEBUG_DRIVER(dev, "ret: %d link_status: %*ph", ret, + (int)sizeof(link_status), link_status); + + if (ret < 0 || !drm_dp_channel_eq_ok(link_status, it6505->lane_count) || + !it6505_get_video_status(it6505)) { + DRM_DEV_DEBUG_DRIVER(dev, "link train not done or no video"); + return; + } + + ret = it6505_get_dpcd(it6505, DP_AUX_HDCP_BKSV, it6505->bksvs, + ARRAY_SIZE(it6505->bksvs)); + if (ret < 0) { + dev_err(dev, "fail to get bksv ret: %d", ret); + it6505_set_bits(it6505, REG_HDCP_TRIGGER, + HDCP_TRIGGER_KSV_FAIL, HDCP_TRIGGER_KSV_FAIL); + } + + DRM_DEV_DEBUG_DRIVER(dev, "bksv = 0x%*ph", + (int)ARRAY_SIZE(it6505->bksvs), it6505->bksvs); + + if (!it6505_hdcp_is_ksv_valid(it6505->bksvs)) { + dev_err(dev, "Display Port bksv not valid"); + it6505_set_bits(it6505, REG_HDCP_TRIGGER, + HDCP_TRIGGER_KSV_FAIL, HDCP_TRIGGER_KSV_FAIL); + } + + it6505_hdcp_part1_auth(it6505); +} + +static void it6505_show_hdcp_info(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + int i; + u8 *sha1 = it6505->sha1_input; + + DRM_DEV_DEBUG_DRIVER(dev, "hdcp_status: %d is_repeater: %d", + it6505->hdcp_status, it6505->is_repeater); + DRM_DEV_DEBUG_DRIVER(dev, "bksv = 0x%*ph", + (int)ARRAY_SIZE(it6505->bksvs), it6505->bksvs); + + if (it6505->is_repeater) { + DRM_DEV_DEBUG_DRIVER(dev, "hdcp_down_stream_count: %d", + it6505->hdcp_down_stream_count); + DRM_DEV_DEBUG_DRIVER(dev, "sha1_input: 0x%*ph", + (int)ARRAY_SIZE(it6505->sha1_input), + it6505->sha1_input); + for (i = 0; i < it6505->hdcp_down_stream_count; i++) { + DRM_DEV_DEBUG_DRIVER(dev, "KSV_%d = 0x%*ph", i, + DRM_HDCP_KSV_LEN, sha1); + sha1 += DRM_HDCP_KSV_LEN; + } + DRM_DEV_DEBUG_DRIVER(dev, "binfo: 0x%2ph M0: 0x%8ph", + sha1, sha1 + 2); + } +} + +static void it6505_stop_link_train(struct it6505 *it6505) +{ + it6505->link_state = LINK_IDLE; + cancel_work_sync(&it6505->link_works); + it6505_write(it6505, REG_TRAIN_CTRL1, FORCE_RETRAIN); +} + +static void it6505_link_train_ok(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + + it6505->link_state = LINK_OK; + /* disalbe mute enable avi info frame */ + it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_VID_MUTE, 0x00); + it6505_set_bits(it6505, REG_INFOFRAME_CTRL, + EN_VID_CTRL_PKT, EN_VID_CTRL_PKT); + + if (it6505_audio_input(it6505)) { + DRM_DEV_DEBUG_DRIVER(dev, "Enable audio!"); + it6505_enable_audio(it6505); + } + + if (it6505->hdcp_desired) + it6505_start_hdcp(it6505); +} + +static void it6505_link_step_train_process(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + int ret, i, step_retry = 3; + + DRM_DEV_DEBUG_DRIVER(dev, "Start step train"); + + if (it6505->sink_count == 0) { + DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count:%d, force eq", + it6505->sink_count); + it6505_set_bits(it6505, REG_TRAIN_CTRL0, FORCE_EQ_DONE, + FORCE_EQ_DONE); + return; + } + + if (!it6505->step_train) { + DRM_DEV_DEBUG_DRIVER(dev, "not support step train"); + return; + } + + /* step training start here */ + for (i = 0; i < step_retry; i++) { + it6505_link_reset_step_train(it6505); + ret = it6505_link_start_step_train(it6505); + DRM_DEV_DEBUG_DRIVER(dev, "step train %s, retry:%d times", + ret ? "pass" : "failed", i + 1); + if (ret) { + it6505_link_train_ok(it6505); + return; + } + } + + DRM_DEV_DEBUG_DRIVER(dev, "training fail"); + it6505->link_state = LINK_IDLE; + it6505_video_reset(it6505); +} + +static void it6505_link_training_work(struct work_struct *work) +{ + struct it6505 *it6505 = container_of(work, struct it6505, link_works); + struct device *dev = &it6505->client->dev; + int ret; + + DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count: %d", + it6505->sink_count); + + if (!it6505_get_sink_hpd_status(it6505)) + return; + + it6505_link_training_setup(it6505); + it6505_reset_hdcp(it6505); + it6505_aux_reset(it6505); + + if (it6505->auto_train_retry < 1) { + it6505_link_step_train_process(it6505); + return; + } + + ret = it6505_link_start_auto_train(it6505); + DRM_DEV_DEBUG_DRIVER(dev, "auto train %s, auto_train_retry: %d", + ret ? "pass" : "failed", it6505->auto_train_retry); + it6505->auto_train_retry--; + + if (ret) { + it6505_link_train_ok(it6505); + return; + } + + it6505_dump(it6505); +} + +static void it6505_plugged_status_to_codec(struct it6505 *it6505) +{ + enum drm_connector_status status = it6505->connector_status; + + if (it6505->plugged_cb && it6505->codec_dev) + it6505->plugged_cb(it6505->codec_dev, + status == connector_status_connected); +} + +static int it6505_process_hpd_irq(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + int ret, dpcd_sink_count, dp_irq_vector, bstatus; + u8 link_status[DP_LINK_STATUS_SIZE]; + + if (!it6505_get_sink_hpd_status(it6505)) { + DRM_DEV_DEBUG_DRIVER(dev, "HPD_IRQ HPD low"); + it6505->sink_count = 0; + return 0; + } + + ret = it6505_dpcd_read(it6505, DP_SINK_COUNT); + if (ret < 0) + return ret; + + dpcd_sink_count = DP_GET_SINK_COUNT(ret); + DRM_DEV_DEBUG_DRIVER(dev, "dpcd_sink_count: %d it6505->sink_count:%d", + dpcd_sink_count, it6505->sink_count); + + if (it6505->branch_device && dpcd_sink_count != it6505->sink_count) { + memset(it6505->dpcd, 0, sizeof(it6505->dpcd)); + it6505->sink_count = dpcd_sink_count; + it6505_reset_logic(it6505); + it6505_int_mask_enable(it6505); + it6505_init(it6505); + return 0; + } + + dp_irq_vector = it6505_dpcd_read(it6505, DP_DEVICE_SERVICE_IRQ_VECTOR); + if (dp_irq_vector < 0) + return dp_irq_vector; + + DRM_DEV_DEBUG_DRIVER(dev, "dp_irq_vector = 0x%02x", dp_irq_vector); + + if (dp_irq_vector & DP_CP_IRQ) { + it6505_set_bits(it6505, REG_HDCP_TRIGGER, HDCP_TRIGGER_CPIRQ, + HDCP_TRIGGER_CPIRQ); + + bstatus = it6505_dpcd_read(it6505, DP_AUX_HDCP_BSTATUS); + if (bstatus < 0) + return bstatus; + + DRM_DEV_DEBUG_DRIVER(dev, "Bstatus = 0x%02x", bstatus); + } + + ret = drm_dp_dpcd_read_link_status(&it6505->aux, link_status); + if (ret < 0) { + dev_err(dev, "Fail to read link status ret: %d", ret); + return ret; + } + + DRM_DEV_DEBUG_DRIVER(dev, "link status = 0x%*ph", + (int)ARRAY_SIZE(link_status), link_status); + + if (!drm_dp_channel_eq_ok(link_status, it6505->lane_count)) { + it6505->auto_train_retry = AUTO_TRAIN_RETRY; + it6505_video_reset(it6505); + } + + return 0; +} + +static void it6505_irq_hpd(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + + it6505->hpd_state = it6505_get_sink_hpd_status(it6505); + DRM_DEV_DEBUG_DRIVER(dev, "hpd change interrupt, change to %s", + it6505->hpd_state ? "high" : "low"); + + if (it6505->bridge.dev) + drm_helper_hpd_irq_event(it6505->bridge.dev); + DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count: %d", + it6505->sink_count); + + if (it6505->hpd_state) { + wait_for_completion_timeout(&it6505->wait_edid_complete, + msecs_to_jiffies(6000)); + it6505_lane_termination_on(it6505); + it6505_lane_power_on(it6505); + + /* + * for some dongle which issue HPD_irq + * when sink count change from 0->1 + * it6505 not able to receive HPD_IRQ + * if HW never go into trainig done + */ + + if (it6505->branch_device && it6505->sink_count == 0) + schedule_work(&it6505->link_works); + + if (!it6505_get_video_status(it6505)) + it6505_video_reset(it6505); + + it6505_calc_video_info(it6505); + } else { + memset(it6505->dpcd, 0, sizeof(it6505->dpcd)); + + if (it6505->hdcp_desired) + it6505_stop_hdcp(it6505); + + it6505_video_disable(it6505); + it6505_disable_audio(it6505); + it6505_stop_link_train(it6505); + it6505_lane_off(it6505); + it6505_link_reset_step_train(it6505); + } +} + +static void it6505_irq_hpd_irq(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "hpd_irq interrupt"); + + if (it6505_process_hpd_irq(it6505) < 0) + DRM_DEV_DEBUG_DRIVER(dev, "process hpd_irq fail!"); +} + +static void it6505_irq_scdt(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + bool data; + + data = it6505_get_video_status(it6505); + DRM_DEV_DEBUG_DRIVER(dev, "video stable change interrupt, %s", + data ? "stable" : "unstable"); + it6505_calc_video_info(it6505); + it6505_link_reset_step_train(it6505); + + if (data) + schedule_work(&it6505->link_works); +} + +static void it6505_irq_hdcp_done(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "hdcp done interrupt"); + it6505->hdcp_status = HDCP_AUTH_DONE; + it6505_show_hdcp_info(it6505); +} + +static void it6505_irq_hdcp_fail(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "hdcp fail interrupt"); + it6505->hdcp_status = HDCP_AUTH_IDLE; + it6505_show_hdcp_info(it6505); + it6505_start_hdcp(it6505); +} + +static void it6505_irq_aux_cmd_fail(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "AUX PC Request Fail Interrupt"); +} + +static void it6505_irq_hdcp_ksv_check(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "HDCP event Interrupt"); + schedule_work(&it6505->hdcp_wait_ksv_list); +} + +static void it6505_irq_audio_fifo_error(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "audio fifo error Interrupt"); + + if (it6505_audio_input(it6505)) + it6505_enable_audio(it6505); +} + +static void it6505_irq_link_train_fail(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "link training fail interrupt"); + schedule_work(&it6505->link_works); +} + +static void it6505_irq_video_fifo_error(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "video fifo overflow interrupt"); + it6505->auto_train_retry = AUTO_TRAIN_RETRY; + flush_work(&it6505->link_works); + it6505_stop_hdcp(it6505); + it6505_video_reset(it6505); +} + +static void it6505_irq_io_latch_fifo_overflow(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "IO latch fifo overflow interrupt"); + it6505->auto_train_retry = AUTO_TRAIN_RETRY; + flush_work(&it6505->link_works); + it6505_stop_hdcp(it6505); + it6505_video_reset(it6505); +} + +static bool it6505_test_bit(unsigned int bit, const unsigned int *addr) +{ + return 1 & (addr[bit / BITS_PER_BYTE] >> (bit % BITS_PER_BYTE)); +} + +static irqreturn_t it6505_int_threaded_handler(int unused, void *data) +{ + struct it6505 *it6505 = data; + struct device *dev = &it6505->client->dev; + static const struct { + int bit; + void (*handler)(struct it6505 *it6505); + } irq_vec[] = { + { BIT_INT_HPD, it6505_irq_hpd }, + { BIT_INT_HPD_IRQ, it6505_irq_hpd_irq }, + { BIT_INT_SCDT, it6505_irq_scdt }, + { BIT_INT_HDCP_FAIL, it6505_irq_hdcp_fail }, + { BIT_INT_HDCP_DONE, it6505_irq_hdcp_done }, + { BIT_INT_AUX_CMD_FAIL, it6505_irq_aux_cmd_fail }, + { BIT_INT_HDCP_KSV_CHECK, it6505_irq_hdcp_ksv_check }, + { BIT_INT_AUDIO_FIFO_ERROR, it6505_irq_audio_fifo_error }, + { BIT_INT_LINK_TRAIN_FAIL, it6505_irq_link_train_fail }, + { BIT_INT_VID_FIFO_ERROR, it6505_irq_video_fifo_error }, + { BIT_INT_IO_FIFO_OVERFLOW, it6505_irq_io_latch_fifo_overflow }, + }; + int int_status[3], i; + + msleep(100); + mutex_lock(&it6505->extcon_lock); + + if (it6505->enable_drv_hold || !it6505->powered) + goto unlock; + + int_status[0] = it6505_read(it6505, INT_STATUS_01); + int_status[1] = it6505_read(it6505, INT_STATUS_02); + int_status[2] = it6505_read(it6505, INT_STATUS_03); + + it6505_write(it6505, INT_STATUS_01, int_status[0]); + it6505_write(it6505, INT_STATUS_02, int_status[1]); + it6505_write(it6505, INT_STATUS_03, int_status[2]); + + DRM_DEV_DEBUG_DRIVER(dev, "reg06 = 0x%02x", int_status[0]); + DRM_DEV_DEBUG_DRIVER(dev, "reg07 = 0x%02x", int_status[1]); + DRM_DEV_DEBUG_DRIVER(dev, "reg08 = 0x%02x", int_status[2]); + it6505_debug_print(it6505, REG_SYSTEM_STS, ""); + + if (it6505_test_bit(irq_vec[0].bit, (unsigned int *)int_status)) + irq_vec[0].handler(it6505); + + if (!it6505->hpd_state) + goto unlock; + + for (i = 1; i < ARRAY_SIZE(irq_vec); i++) { + if (it6505_test_bit(irq_vec[i].bit, (unsigned int *)int_status)) + irq_vec[i].handler(it6505); + } + +unlock: + mutex_unlock(&it6505->extcon_lock); + + return IRQ_HANDLED; +} + +static int it6505_poweron(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + struct it6505_platform_data *pdata = &it6505->pdata; + int err; + + DRM_DEV_DEBUG_DRIVER(dev, "it6505 start powered on"); + + if (it6505->powered) { + DRM_DEV_DEBUG_DRIVER(dev, "it6505 already powered on"); + return 0; + } + + if (pdata->pwr18) { + err = regulator_enable(pdata->pwr18); + if (err) { + DRM_DEV_DEBUG_DRIVER(dev, "Failed to enable VDD18: %d", + err); + return err; + } + } + + if (pdata->ovdd) { + /* time interval between IVDD and OVDD at least be 1ms */ + usleep_range(1000, 2000); + err = regulator_enable(pdata->ovdd); + if (err) { + regulator_disable(pdata->pwr18); + return err; + } + } + /* time interval between OVDD and SYSRSTN at least be 10ms */ + if (pdata->gpiod_reset) { + usleep_range(10000, 20000); + gpiod_set_value_cansleep(pdata->gpiod_reset, 0); + usleep_range(1000, 2000); + gpiod_set_value_cansleep(pdata->gpiod_reset, 1); + usleep_range(10000, 20000); + } + + it6505_reset_logic(it6505); + it6505_int_mask_enable(it6505); + it6505_init(it6505); + it6505_lane_off(it6505); + + it6505->powered = true; + + return 0; +} + +static int it6505_poweroff(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + struct it6505_platform_data *pdata = &it6505->pdata; + int err; + + DRM_DEV_DEBUG_DRIVER(dev, "it6505 start power off"); + + if (!it6505->powered) { + DRM_DEV_DEBUG_DRIVER(dev, "power had been already off"); + return 0; + } + + if (pdata->gpiod_reset) + gpiod_set_value_cansleep(pdata->gpiod_reset, 0); + + if (pdata->pwr18) { + err = regulator_disable(pdata->pwr18); + if (err) + return err; + } + + if (pdata->ovdd) { + err = regulator_disable(pdata->ovdd); + if (err) + return err; + } + + it6505->powered = false; + it6505->sink_count = 0; + + return 0; +} + +static enum drm_connector_status it6505_detect(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + enum drm_connector_status status = connector_status_disconnected; + int dp_sink_count; + + DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count:%d powered:%d", + it6505->sink_count, it6505->powered); + + mutex_lock(&it6505->mode_lock); + + if (!it6505->powered) + goto unlock; + + if (it6505->enable_drv_hold) { + status = it6505_get_sink_hpd_status(it6505) ? + connector_status_connected : + connector_status_disconnected; + goto unlock; + } + + if (it6505_get_sink_hpd_status(it6505)) { + it6505_aux_on(it6505); + it6505_drm_dp_link_probe(&it6505->aux, &it6505->link); + it6505_drm_dp_link_power_up(&it6505->aux, &it6505->link); + it6505->auto_train_retry = AUTO_TRAIN_RETRY; + + if (it6505->dpcd[0] == 0) { + it6505_get_dpcd(it6505, DP_DPCD_REV, it6505->dpcd, + ARRAY_SIZE(it6505->dpcd)); + it6505_variable_config(it6505); + it6505_parse_link_capabilities(it6505); + } + + dp_sink_count = it6505_dpcd_read(it6505, DP_SINK_COUNT); + it6505->sink_count = DP_GET_SINK_COUNT(dp_sink_count); + DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count:%d branch:%d", + it6505->sink_count, it6505->branch_device); + + if (it6505->branch_device) { + status = (it6505->sink_count != 0) ? + connector_status_connected : + connector_status_disconnected; + } else { + status = connector_status_connected; + } + } else { + it6505->sink_count = 0; + memset(it6505->dpcd, 0, sizeof(it6505->dpcd)); + } + +unlock: + if (it6505->connector_status != status) { + it6505->connector_status = status; + it6505_plugged_status_to_codec(it6505); + } + + mutex_unlock(&it6505->mode_lock); + + return status; +} + +static int it6505_extcon_notifier(struct notifier_block *self, + unsigned long event, void *ptr) +{ + struct it6505 *it6505 = container_of(self, struct it6505, event_nb); + + schedule_work(&it6505->extcon_wq); + return NOTIFY_DONE; +} + +static void it6505_extcon_work(struct work_struct *work) +{ + struct it6505 *it6505 = container_of(work, struct it6505, extcon_wq); + struct device *dev = &it6505->client->dev; + int state = extcon_get_state(it6505->extcon, EXTCON_DISP_DP); + unsigned int pwroffretry = 0; + + if (it6505->enable_drv_hold) + return; + + mutex_lock(&it6505->extcon_lock); + + DRM_DEV_DEBUG_DRIVER(dev, "EXTCON_DISP_DP = 0x%02x", state); + if (state > 0) { + DRM_DEV_DEBUG_DRIVER(dev, "start to power on"); + msleep(100); + it6505_poweron(it6505); + } else { + DRM_DEV_DEBUG_DRIVER(dev, "start to power off"); + while (it6505_poweroff(it6505) && pwroffretry++ < 5) { + DRM_DEV_DEBUG_DRIVER(dev, "power off fail %d times", + pwroffretry); + } + + drm_helper_hpd_irq_event(it6505->bridge.dev); + memset(it6505->dpcd, 0, sizeof(it6505->dpcd)); + DRM_DEV_DEBUG_DRIVER(dev, "power off it6505 success!"); + } + + mutex_unlock(&it6505->extcon_lock); +} + +static int it6505_use_notifier_module(struct it6505 *it6505) +{ + int ret; + struct device *dev = &it6505->client->dev; + + it6505->event_nb.notifier_call = it6505_extcon_notifier; + INIT_WORK(&it6505->extcon_wq, it6505_extcon_work); + ret = devm_extcon_register_notifier(&it6505->client->dev, + it6505->extcon, EXTCON_DISP_DP, + &it6505->event_nb); + if (ret) { + dev_err(dev, "failed to register notifier for DP"); + return ret; + } + + schedule_work(&it6505->extcon_wq); + + return 0; +} + +static void it6505_remove_notifier_module(struct it6505 *it6505) +{ + if (it6505->extcon) { + devm_extcon_unregister_notifier(&it6505->client->dev, + it6505->extcon, EXTCON_DISP_DP, + &it6505->event_nb); + + flush_work(&it6505->extcon_wq); + } +} + +static void __maybe_unused it6505_delayed_audio(struct work_struct *work) +{ + struct it6505 *it6505 = container_of(work, struct it6505, + delayed_audio.work); + + DRM_DEV_DEBUG_DRIVER(&it6505->client->dev, "start"); + + if (!it6505->powered) + return; + + if (!it6505->enable_drv_hold) + it6505_enable_audio(it6505); +} + +static int __maybe_unused it6505_audio_setup_hw_params(struct it6505 *it6505, + struct hdmi_codec_params + *params) +{ + struct device *dev = &it6505->client->dev; + int i = 0; + + DRM_DEV_DEBUG_DRIVER(dev, "%s %d Hz, %d bit, %d channels\n", __func__, + params->sample_rate, params->sample_width, + params->cea.channels); + + if (!it6505->bridge.encoder) + return -ENODEV; + + if (params->cea.channels <= 1 || params->cea.channels > 8) { + DRM_DEV_DEBUG_DRIVER(dev, "channel number: %d not support", + it6505->audio.channel_count); + return -EINVAL; + } + + it6505->audio.channel_count = params->cea.channels; + + while (i < ARRAY_SIZE(audio_sample_rate_map) && + params->sample_rate != + audio_sample_rate_map[i].sample_rate_value) { + i++; + } + if (i == ARRAY_SIZE(audio_sample_rate_map)) { + DRM_DEV_DEBUG_DRIVER(dev, "sample rate: %d Hz not support", + params->sample_rate); + return -EINVAL; + } + it6505->audio.sample_rate = audio_sample_rate_map[i].rate; + + switch (params->sample_width) { + case 16: + it6505->audio.word_length = WORD_LENGTH_16BIT; + break; + case 18: + it6505->audio.word_length = WORD_LENGTH_18BIT; + break; + case 20: + it6505->audio.word_length = WORD_LENGTH_20BIT; + break; + case 24: + case 32: + it6505->audio.word_length = WORD_LENGTH_24BIT; + break; + default: + DRM_DEV_DEBUG_DRIVER(dev, "wordlength: %d bit not support", + params->sample_width); + return -EINVAL; + } + + return 0; +} + +static void __maybe_unused it6505_audio_shutdown(struct device *dev, void *data) +{ + struct it6505 *it6505 = dev_get_drvdata(dev); + + if (it6505->powered) + it6505_disable_audio(it6505); +} + +static int __maybe_unused it6505_audio_hook_plugged_cb(struct device *dev, + void *data, + hdmi_codec_plugged_cb fn, + struct device *codec_dev) +{ + struct it6505 *it6505 = data; + + it6505->plugged_cb = fn; + it6505->codec_dev = codec_dev; + it6505_plugged_status_to_codec(it6505); + + return 0; +} + +static inline struct it6505 *bridge_to_it6505(struct drm_bridge *bridge) +{ + return container_of(bridge, struct it6505, bridge); +} + +static int it6505_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct it6505 *it6505 = bridge_to_it6505(bridge); + struct device *dev = &it6505->client->dev; + int ret; + + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { + DRM_ERROR("DRM_BRIDGE_ATTACH_NO_CONNECTOR must be supplied"); + return -EINVAL; + } + + if (!bridge->encoder) { + dev_err(dev, "Parent encoder object not found"); + return -ENODEV; + } + + /* Register aux channel */ + it6505->aux.name = "DP-AUX"; + it6505->aux.dev = dev; + it6505->aux.drm_dev = bridge->dev; + it6505->aux.transfer = it6505_aux_transfer; + + ret = drm_dp_aux_register(&it6505->aux); + + if (ret < 0) { + dev_err(dev, "Failed to register aux: %d", ret); + return ret; + } + + if (it6505->extcon) { + ret = it6505_use_notifier_module(it6505); + if (ret < 0) { + dev_err(dev, "use notifier module failed"); + return ret; + } + } + + return 0; +} + +static void it6505_bridge_detach(struct drm_bridge *bridge) +{ + struct it6505 *it6505 = bridge_to_it6505(bridge); + + flush_work(&it6505->link_works); + it6505_remove_notifier_module(it6505); +} + +static enum drm_mode_status +it6505_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + struct it6505 *it6505 = bridge_to_it6505(bridge); + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + + if (mode->clock > DPI_PIXEL_CLK_MAX) + return MODE_CLOCK_HIGH; + + it6505->video_info.clock = mode->clock; + + return MODE_OK; +} + +static void it6505_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_state) +{ + struct it6505 *it6505 = bridge_to_it6505(bridge); + struct device *dev = &it6505->client->dev; + struct drm_atomic_state *state = old_state->base.state; + struct hdmi_avi_infoframe frame; + struct drm_crtc_state *crtc_state; + struct drm_connector_state *conn_state; + struct drm_display_mode *mode; + struct drm_connector *connector; + int ret; + + DRM_DEV_DEBUG_DRIVER(dev, "start"); + + 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; + + mode = &crtc_state->adjusted_mode; + + if (WARN_ON(!mode)) + return; + + ret = drm_hdmi_avi_infoframe_from_display_mode(&frame, + connector, + mode); + if (ret) + dev_err(dev, "Failed to setup AVI infoframe: %d", ret); + + it6505_update_video_parameter(it6505, mode); + + ret = it6505_send_video_infoframe(it6505, &frame); + + if (ret) + dev_err(dev, "Failed to send AVI infoframe: %d", ret); + + it6505_int_mask_enable(it6505); + it6505_video_reset(it6505); +} + +static void it6505_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_state) +{ + struct it6505 *it6505 = bridge_to_it6505(bridge); + struct device *dev = &it6505->client->dev; + + DRM_DEV_DEBUG_DRIVER(dev, "start"); + + if (it6505->powered) + it6505_video_disable(it6505); +} + +static enum drm_connector_status +it6505_bridge_detect(struct drm_bridge *bridge) +{ + struct it6505 *it6505 = bridge_to_it6505(bridge); + + return it6505_detect(it6505); +} + +static struct edid *it6505_bridge_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct it6505 *it6505 = bridge_to_it6505(bridge); + struct device *dev = &it6505->client->dev; + struct edid *edid; + + edid = drm_do_get_edid(connector, it6505_get_edid_block, it6505); + + if (!edid) { + DRM_DEV_DEBUG_DRIVER(dev, "failed to get edid!"); + return NULL; + } + + return edid; +} + +static const struct drm_bridge_funcs it6505_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 = it6505_bridge_attach, + .detach = it6505_bridge_detach, + .mode_valid = it6505_bridge_mode_valid, + .atomic_enable = it6505_bridge_atomic_enable, + .atomic_disable = it6505_bridge_atomic_disable, + .detect = it6505_bridge_detect, + .get_edid = it6505_bridge_get_edid, +}; + +static __maybe_unused int it6505_bridge_resume(struct device *dev) +{ + struct it6505 *it6505 = dev_get_drvdata(dev); + + return it6505_poweron(it6505); +} + +static __maybe_unused int it6505_bridge_suspend(struct device *dev) +{ + struct it6505 *it6505 = dev_get_drvdata(dev); + + return it6505_poweroff(it6505); +} + +static SIMPLE_DEV_PM_OPS(it6505_bridge_pm_ops, it6505_bridge_suspend, + it6505_bridge_resume); + +static int it6505_init_pdata(struct it6505 *it6505) +{ + struct it6505_platform_data *pdata = &it6505->pdata; + struct device *dev = &it6505->client->dev; + + /* 1.0V digital core power regulator */ + pdata->pwr18 = devm_regulator_get(dev, "pwr18"); + if (IS_ERR(pdata->pwr18)) { + dev_err(dev, "pwr18 regulator not found"); + return PTR_ERR(pdata->pwr18); + } + + pdata->ovdd = devm_regulator_get(dev, "ovdd"); + if (IS_ERR(pdata->ovdd)) { + dev_err(dev, "ovdd regulator not found"); + return PTR_ERR(pdata->ovdd); + } + + pdata->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(pdata->gpiod_reset)) { + dev_err(dev, "gpiod_reset gpio not found"); + return PTR_ERR(pdata->gpiod_reset); + } + + return 0; +} + +static void it6505_parse_dt(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + u32 *afe_setting = &it6505->afe_setting; + + it6505->lane_swap_disabled = + device_property_read_bool(dev, "no-laneswap"); + + if (it6505->lane_swap_disabled) + it6505->lane_swap = false; + + if (device_property_read_u32(dev, "afe-setting", afe_setting) == 0) { + if (*afe_setting >= ARRAY_SIZE(afe_setting_table)) { + dev_err(dev, "afe setting error, use default"); + *afe_setting = 0; + } + } else { + *afe_setting = 0; + } + DRM_DEV_DEBUG_DRIVER(dev, "using afe_setting: %d", *afe_setting); +} + +static ssize_t receive_timing_debugfs_show(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + struct it6505 *it6505 = file->private_data; + struct drm_display_mode *vid = &it6505->video_info; + u8 read_buf[READ_BUFFER_SIZE]; + u8 *str = read_buf, *end = read_buf + READ_BUFFER_SIZE; + ssize_t ret, count; + + if (!it6505) + return -ENODEV; + + it6505_calc_video_info(it6505); + str += scnprintf(str, end - str, "---video timing---\n"); + str += scnprintf(str, end - str, "PCLK:%d.%03dMHz\n", + vid->clock / 1000, vid->clock % 1000); + str += scnprintf(str, end - str, "HTotal:%d\n", vid->htotal); + str += scnprintf(str, end - str, "HActive:%d\n", vid->hdisplay); + str += scnprintf(str, end - str, "HFrontPorch:%d\n", + vid->hsync_start - vid->hdisplay); + str += scnprintf(str, end - str, "HSyncWidth:%d\n", + vid->hsync_end - vid->hsync_start); + str += scnprintf(str, end - str, "HBackPorch:%d\n", + vid->htotal - vid->hsync_end); + str += scnprintf(str, end - str, "VTotal:%d\n", vid->vtotal); + str += scnprintf(str, end - str, "VActive:%d\n", vid->vdisplay); + str += scnprintf(str, end - str, "VFrontPorch:%d\n", + vid->vsync_start - vid->vdisplay); + str += scnprintf(str, end - str, "VSyncWidth:%d\n", + vid->vsync_end - vid->vsync_start); + str += scnprintf(str, end - str, "VBackPorch:%d\n", + vid->vtotal - vid->vsync_end); + + count = str - read_buf; + ret = simple_read_from_buffer(buf, len, ppos, read_buf, count); + + return ret; +} + +static int force_power_on_off_debugfs_write(void *data, u64 value) +{ + struct it6505 *it6505 = data; + + if (!it6505) + return -ENODEV; + + if (value) + it6505_poweron(it6505); + else + it6505_poweroff(it6505); + + return 0; +} + +static int enable_drv_hold_debugfs_show(void *data, u64 *buf) +{ + struct it6505 *it6505 = data; + + if (!it6505) + return -ENODEV; + + *buf = it6505->enable_drv_hold; + + return 0; +} + +static int enable_drv_hold_debugfs_write(void *data, u64 drv_hold) +{ + struct it6505 *it6505 = data; + + if (!it6505) + return -ENODEV; + + it6505->enable_drv_hold = drv_hold; + + if (it6505->enable_drv_hold) { + it6505_int_mask_disable(it6505); + } else { + it6505_clear_int(it6505); + it6505_int_mask_enable(it6505); + + if (it6505->powered) { + it6505->connector_status = + it6505_get_sink_hpd_status(it6505) ? + connector_status_connected : + connector_status_disconnected; + } else { + it6505->connector_status = + connector_status_disconnected; + } + } + + return 0; +} + +static const struct file_operations receive_timing_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = receive_timing_debugfs_show, + .llseek = default_llseek, +}; + +DEFINE_DEBUGFS_ATTRIBUTE(fops_force_power, NULL, + force_power_on_off_debugfs_write, "%llu\n"); + +DEFINE_DEBUGFS_ATTRIBUTE(fops_enable_drv_hold, enable_drv_hold_debugfs_show, + enable_drv_hold_debugfs_write, "%llu\n"); + +static const struct debugfs_entries debugfs_entry[] = { + { "receive_timing", &receive_timing_fops }, + { "force_power_on_off", &fops_force_power }, + { "enable_drv_hold", &fops_enable_drv_hold }, + { NULL, NULL }, +}; + +static void debugfs_create_files(struct it6505 *it6505) +{ + int i = 0; + + while (debugfs_entry[i].name && debugfs_entry[i].fops) { + debugfs_create_file(debugfs_entry[i].name, 0644, + it6505->debugfs, it6505, + debugfs_entry[i].fops); + i++; + } +} + +static void debugfs_init(struct it6505 *it6505) +{ + struct device *dev = &it6505->client->dev; + + it6505->debugfs = debugfs_create_dir(DEBUGFS_DIR_NAME, NULL); + + if (IS_ERR(it6505->debugfs)) { + dev_err(dev, "failed to create debugfs root"); + return; + } + + debugfs_create_files(it6505); +} + +static void it6505_debugfs_remove(struct it6505 *it6505) +{ + debugfs_remove_recursive(it6505->debugfs); +} + +static void it6505_shutdown(struct i2c_client *client) +{ + struct it6505 *it6505 = dev_get_drvdata(&client->dev); + + if (it6505->powered) + it6505_lane_off(it6505); +} + +static int it6505_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct it6505 *it6505; + struct device *dev = &client->dev; + struct extcon_dev *extcon; + int err, intp_irq; + + it6505 = devm_kzalloc(&client->dev, sizeof(*it6505), GFP_KERNEL); + if (!it6505) + return -ENOMEM; + + mutex_init(&it6505->extcon_lock); + mutex_init(&it6505->mode_lock); + mutex_init(&it6505->aux_lock); + + it6505->bridge.of_node = client->dev.of_node; + it6505->connector_status = connector_status_disconnected; + it6505->client = client; + i2c_set_clientdata(client, it6505); + + /* get extcon device from DTS */ + extcon = extcon_get_edev_by_phandle(dev, 0); + if (PTR_ERR(extcon) == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (IS_ERR(extcon)) { + dev_err(dev, "can not get extcon device!"); + return PTR_ERR(extcon); + } + + it6505->extcon = extcon; + + it6505->regmap = devm_regmap_init_i2c(client, &it6505_regmap_config); + if (IS_ERR(it6505->regmap)) { + dev_err(dev, "regmap i2c init failed"); + err = PTR_ERR(it6505->regmap); + return err; + } + + err = it6505_init_pdata(it6505); + if (err) { + dev_err(dev, "Failed to initialize pdata: %d", err); + return err; + } + + it6505_parse_dt(it6505); + + intp_irq = client->irq; + + if (!intp_irq) { + dev_err(dev, "Failed to get INTP IRQ"); + err = -ENODEV; + return err; + } + + err = devm_request_threaded_irq(&client->dev, intp_irq, NULL, + it6505_int_threaded_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "it6505-intp", it6505); + if (err) { + dev_err(dev, "Failed to request INTP threaded IRQ: %d", err); + return err; + } + + INIT_WORK(&it6505->link_works, it6505_link_training_work); + INIT_WORK(&it6505->hdcp_wait_ksv_list, it6505_hdcp_wait_ksv_list); + INIT_DELAYED_WORK(&it6505->hdcp_work, it6505_hdcp_work); + init_completion(&it6505->wait_edid_complete); + memset(it6505->dpcd, 0, sizeof(it6505->dpcd)); + it6505->powered = false; + it6505->enable_drv_hold = DEFAULT_DRV_HOLD; + + if (DEFAULT_PWR_ON) + it6505_poweron(it6505); + + DRM_DEV_DEBUG_DRIVER(dev, "it6505 device name: %s", dev_name(dev)); + debugfs_init(it6505); + + it6505->bridge.funcs = &it6505_bridge_funcs; + it6505->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; + it6505->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | + DRM_BRIDGE_OP_HPD; + drm_bridge_add(&it6505->bridge); + + return 0; +} + +static int it6505_i2c_remove(struct i2c_client *client) +{ + struct it6505 *it6505 = i2c_get_clientdata(client); + + drm_bridge_remove(&it6505->bridge); + drm_dp_aux_unregister(&it6505->aux); + it6505_debugfs_remove(it6505); + it6505_poweroff(it6505); + + return 0; +} + +static const struct i2c_device_id it6505_id[] = { + { "it6505", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, it6505_id); + +static const struct of_device_id it6505_of_match[] = { + { .compatible = "ite,it6505" }, + { } +}; + +static struct i2c_driver it6505_i2c_driver = { + .driver = { + .name = "it6505", + .of_match_table = it6505_of_match, + .pm = &it6505_bridge_pm_ops, + }, + .probe = it6505_i2c_probe, + .remove = it6505_i2c_remove, + .shutdown = it6505_shutdown, + .id_table = it6505_id, +}; + +module_i2c_driver(it6505_i2c_driver); + +MODULE_AUTHOR("Allen Chen <allen.chen@ite.com.tw>"); +MODULE_DESCRIPTION("IT6505 DisplayPort Transmitter driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 06b59b422c69..69288cf894b9 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -936,9 +936,6 @@ static int it66121_probe(struct i2c_client *client, return -EPROBE_DEFER; } - if (!ctx->next_bridge) - return -EPROBE_DEFER; - i2c_set_clientdata(client, ctx); mutex_init(&ctx->lock); diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index dafb1b47c15f..63df2e8a8abc 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -1090,7 +1090,7 @@ static int lt9611_probe(struct i2c_client *client, if (!lt9611) return -ENOMEM; - lt9611->dev = &client->dev; + lt9611->dev = dev; lt9611->client = client; lt9611->sleep = false; @@ -1100,7 +1100,7 @@ static int lt9611_probe(struct i2c_client *client, return PTR_ERR(lt9611->regmap); } - ret = lt9611_parse_dt(&client->dev, lt9611); + ret = lt9611_parse_dt(dev, lt9611); if (ret) { dev_err(dev, "failed to parse device tree\n"); return ret; @@ -1164,7 +1164,11 @@ static int lt9611_probe(struct i2c_client *client, lt9611_enable_hpd_interrupts(lt9611); - return lt9611_audio_init(dev, lt9611); + ret = lt9611_audio_init(dev, lt9611); + if (ret) + goto err_remove_bridge; + + return 0; err_remove_bridge: drm_bridge_remove(<9611->bridge); diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c index 33f9716da0ee..3d62e6bf6892 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c @@ -860,7 +860,7 @@ static int lt9611uxc_probe(struct i2c_client *client, if (!lt9611uxc) return -ENOMEM; - lt9611uxc->dev = &client->dev; + lt9611uxc->dev = dev; lt9611uxc->client = client; mutex_init(<9611uxc->ocm_lock); @@ -870,7 +870,7 @@ static int lt9611uxc_probe(struct i2c_client *client, return PTR_ERR(lt9611uxc->regmap); } - ret = lt9611uxc_parse_dt(&client->dev, lt9611uxc); + ret = lt9611uxc_parse_dt(dev, lt9611uxc); if (ret) { dev_err(dev, "failed to parse device tree\n"); return ret; diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c index af07eeb47ca0..d5945501a5ee 100644 --- a/drivers/gpu/drm/bridge/nwl-dsi.c +++ b/drivers/gpu/drm/bridge/nwl-dsi.c @@ -66,7 +66,6 @@ struct nwl_dsi_transfer { struct nwl_dsi { struct drm_bridge bridge; struct mipi_dsi_host dsi_host; - struct drm_bridge *panel_bridge; struct device *dev; struct phy *phy; union phy_configure_opts phy_cfg; @@ -333,17 +332,13 @@ static int nwl_dsi_config_dpi(struct nwl_dsi *dsi) static int nwl_dsi_init_interrupts(struct nwl_dsi *dsi) { - u32 irq_enable; - - nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK, 0xffffffff); - nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK2, 0x7); - - irq_enable = ~(u32)(NWL_DSI_TX_PKT_DONE_MASK | - NWL_DSI_RX_PKT_HDR_RCVD_MASK | - NWL_DSI_TX_FIFO_OVFLW_MASK | - NWL_DSI_HS_TX_TIMEOUT_MASK); + u32 irq_enable = ~(u32)(NWL_DSI_TX_PKT_DONE_MASK | + NWL_DSI_RX_PKT_HDR_RCVD_MASK | + NWL_DSI_TX_FIFO_OVFLW_MASK | + NWL_DSI_HS_TX_TIMEOUT_MASK); nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK, irq_enable); + nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK2, 0x7); return nwl_dsi_clear_error(dsi); } @@ -861,18 +856,19 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge, memcpy(&dsi->mode, adjusted_mode, sizeof(dsi->mode)); drm_mode_debug_printmodeline(adjusted_mode); - pm_runtime_get_sync(dev); + if (pm_runtime_resume_and_get(dev) < 0) + return; if (clk_prepare_enable(dsi->lcdif_clk) < 0) - return; + goto runtime_put; if (clk_prepare_enable(dsi->core_clk) < 0) - return; + goto runtime_put; /* Step 1 from DSI reset-out instructions */ ret = reset_control_deassert(dsi->rst_pclk); if (ret < 0) { DRM_DEV_ERROR(dev, "Failed to deassert PCLK: %d\n", ret); - return; + goto runtime_put; } /* Step 2 from DSI reset-out instructions */ @@ -882,13 +878,18 @@ nwl_dsi_bridge_mode_set(struct drm_bridge *bridge, ret = reset_control_deassert(dsi->rst_esc); if (ret < 0) { DRM_DEV_ERROR(dev, "Failed to deassert ESC: %d\n", ret); - return; + goto runtime_put; } ret = reset_control_deassert(dsi->rst_byte); if (ret < 0) { DRM_DEV_ERROR(dev, "Failed to deassert BYTE: %d\n", ret); - return; + goto runtime_put; } + + return; + +runtime_put: + pm_runtime_put_sync(dev); } static void @@ -922,13 +923,11 @@ static int nwl_dsi_bridge_attach(struct drm_bridge *bridge, if (IS_ERR(panel_bridge)) return PTR_ERR(panel_bridge); } - dsi->panel_bridge = panel_bridge; - if (!dsi->panel_bridge) + if (!panel_bridge) return -EPROBE_DEFER; - return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge, - flags); + return drm_bridge_attach(bridge->encoder, panel_bridge, bridge, flags); } static void nwl_dsi_bridge_detach(struct drm_bridge *bridge) @@ -1204,6 +1203,7 @@ static int nwl_dsi_probe(struct platform_device *pdev) ret = nwl_dsi_select_input(dsi); if (ret < 0) { + pm_runtime_disable(dev); mipi_dsi_host_unregister(&dsi->dsi_host); return ret; } diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index b32295abd9e7..5be057575183 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -138,6 +138,17 @@ static int panel_bridge_get_modes(struct drm_bridge *bridge, return drm_panel_get_modes(panel_bridge->panel, connector); } +static void panel_bridge_debugfs_init(struct drm_bridge *bridge, + struct dentry *root) +{ + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); + struct drm_panel *panel = panel_bridge->panel; + + root = debugfs_create_dir("panel", root); + if (panel->funcs->debugfs_init) + panel->funcs->debugfs_init(panel, root); +} + static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .attach = panel_bridge_attach, .detach = panel_bridge_detach, @@ -150,6 +161,7 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, + .debugfs_init = panel_bridge_debugfs_init, }; /** diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index 818704bf5e86..3f17337ee389 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -14,8 +14,8 @@ #include <linux/regulator/consumer.h> #include <drm/drm_bridge.h> -#include <drm/drm_dp_aux_bus.h> -#include <drm/drm_dp_helper.h> +#include <drm/dp/drm_dp_aux_bus.h> +#include <drm/dp/drm_dp_helper.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> @@ -102,6 +102,7 @@ struct ps8640 { struct regulator_bulk_data supplies[2]; struct gpio_desc *gpio_reset; struct gpio_desc *gpio_powerdown; + struct device_link *link; bool pre_enabled; }; @@ -456,14 +457,36 @@ static int ps8640_bridge_attach(struct drm_bridge *bridge, return ret; } + ps_bridge->link = device_link_add(bridge->dev->dev, dev, DL_FLAG_STATELESS); + if (!ps_bridge->link) { + dev_err(dev, "failed to create device link"); + ret = -EINVAL; + goto err_devlink; + } + /* Attach the panel-bridge to the dsi bridge */ - return drm_bridge_attach(bridge->encoder, ps_bridge->panel_bridge, - &ps_bridge->bridge, flags); + ret = drm_bridge_attach(bridge->encoder, ps_bridge->panel_bridge, + &ps_bridge->bridge, flags); + if (ret) + goto err_bridge_attach; + + return 0; + +err_bridge_attach: + device_link_del(ps_bridge->link); +err_devlink: + drm_dp_aux_unregister(&ps_bridge->aux); + + return ret; } static void ps8640_bridge_detach(struct drm_bridge *bridge) { - drm_dp_aux_unregister(&bridge_to_ps8640(bridge)->aux); + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); + + drm_dp_aux_unregister(&ps_bridge->aux); + if (ps_bridge->link) + device_link_del(ps_bridge->link); } static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge, diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index 89558e581530..65549fbfdc87 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -166,10 +166,12 @@ struct sii902x { struct i2c_client *i2c; struct regmap *regmap; struct drm_bridge bridge; + struct drm_bridge *next_bridge; struct drm_connector connector; struct gpio_desc *reset_gpio; struct i2c_mux_core *i2cmux; struct regulator_bulk_data supplies[2]; + bool sink_is_hdmi; /* * Mutex protects audio and video functions from interfering * each other, by keeping their i2c command sequences atomic. @@ -245,10 +247,8 @@ static void sii902x_reset(struct sii902x *sii902x) gpiod_set_value(sii902x->reset_gpio, 0); } -static enum drm_connector_status -sii902x_connector_detect(struct drm_connector *connector, bool force) +static enum drm_connector_status sii902x_detect(struct sii902x *sii902x) { - struct sii902x *sii902x = connector_to_sii902x(connector); unsigned int status; mutex_lock(&sii902x->mutex); @@ -261,6 +261,14 @@ sii902x_connector_detect(struct drm_connector *connector, bool force) connector_status_connected : connector_status_disconnected; } +static enum drm_connector_status +sii902x_connector_detect(struct drm_connector *connector, bool force) +{ + struct sii902x *sii902x = connector_to_sii902x(connector); + + return sii902x_detect(sii902x); +} + static const struct drm_connector_funcs sii902x_connector_funcs = { .detect = sii902x_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, @@ -270,42 +278,40 @@ static const struct drm_connector_funcs sii902x_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int sii902x_get_modes(struct drm_connector *connector) +static struct edid *sii902x_get_edid(struct sii902x *sii902x, + struct drm_connector *connector) { - struct sii902x *sii902x = connector_to_sii902x(connector); - u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; - u8 output_mode = SII902X_SYS_CTRL_OUTPUT_DVI; struct edid *edid; - int num = 0, ret; mutex_lock(&sii902x->mutex); edid = drm_get_edid(connector, sii902x->i2cmux->adapter[0]); - drm_connector_update_edid_property(connector, edid); if (edid) { if (drm_detect_hdmi_monitor(edid)) - output_mode = SII902X_SYS_CTRL_OUTPUT_HDMI; - - num = drm_add_edid_modes(connector, edid); - kfree(edid); + sii902x->sink_is_hdmi = true; + else + sii902x->sink_is_hdmi = false; } - ret = drm_display_info_set_bus_formats(&connector->display_info, - &bus_format, 1); - if (ret) - goto error_out; + mutex_unlock(&sii902x->mutex); - ret = regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA, - SII902X_SYS_CTRL_OUTPUT_MODE, output_mode); - if (ret) - goto error_out; + return edid; +} - ret = num; +static int sii902x_get_modes(struct drm_connector *connector) +{ + struct sii902x *sii902x = connector_to_sii902x(connector); + struct edid *edid; + int num = 0; -error_out: - mutex_unlock(&sii902x->mutex); + edid = sii902x_get_edid(sii902x, connector); + drm_connector_update_edid_property(connector, edid); + if (edid) { + num = drm_add_edid_modes(connector, edid); + kfree(edid); + } - return ret; + return num; } static enum drm_mode_status sii902x_mode_valid(struct drm_connector *connector, @@ -354,12 +360,16 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *adj) { struct sii902x *sii902x = bridge_to_sii902x(bridge); + u8 output_mode = SII902X_SYS_CTRL_OUTPUT_DVI; struct regmap *regmap = sii902x->regmap; u8 buf[HDMI_INFOFRAME_SIZE(AVI)]; struct hdmi_avi_infoframe frame; u16 pixel_clock_10kHz = adj->clock / 10; int ret; + if (sii902x->sink_is_hdmi) + output_mode = SII902X_SYS_CTRL_OUTPUT_HDMI; + buf[0] = pixel_clock_10kHz & 0xff; buf[1] = pixel_clock_10kHz >> 8; buf[2] = drm_mode_vrefresh(adj); @@ -375,6 +385,11 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge, mutex_lock(&sii902x->mutex); + ret = regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA, + SII902X_SYS_CTRL_OUTPUT_MODE, output_mode); + if (ret) + goto out; + ret = regmap_bulk_write(regmap, SII902X_TPI_VIDEO_DATA, buf, 10); if (ret) goto out; @@ -405,13 +420,13 @@ static int sii902x_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) { struct sii902x *sii902x = bridge_to_sii902x(bridge); + u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; struct drm_device *drm = bridge->dev; int ret; - if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { - DRM_ERROR("Fix bridge driver to make connector optional!"); - return -EINVAL; - } + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) + return drm_bridge_attach(bridge->encoder, sii902x->next_bridge, + bridge, flags); drm_connector_helper_add(&sii902x->connector, &sii902x_connector_helper_funcs); @@ -433,16 +448,38 @@ static int sii902x_bridge_attach(struct drm_bridge *bridge, else sii902x->connector.polled = DRM_CONNECTOR_POLL_CONNECT; + ret = drm_display_info_set_bus_formats(&sii902x->connector.display_info, + &bus_format, 1); + if (ret) + return ret; + drm_connector_attach_encoder(&sii902x->connector, bridge->encoder); return 0; } +static enum drm_connector_status sii902x_bridge_detect(struct drm_bridge *bridge) +{ + struct sii902x *sii902x = bridge_to_sii902x(bridge); + + return sii902x_detect(sii902x); +} + +static struct edid *sii902x_bridge_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct sii902x *sii902x = bridge_to_sii902x(bridge); + + return sii902x_get_edid(sii902x, connector); +} + static const struct drm_bridge_funcs sii902x_bridge_funcs = { .attach = sii902x_bridge_attach, .mode_set = sii902x_bridge_mode_set, .disable = sii902x_bridge_disable, .enable = sii902x_bridge_enable, + .detect = sii902x_bridge_detect, + .get_edid = sii902x_bridge_get_edid, }; static int sii902x_mute(struct sii902x *sii902x, bool mute) @@ -829,8 +866,12 @@ static irqreturn_t sii902x_interrupt(int irq, void *data) mutex_unlock(&sii902x->mutex); - if ((status & SII902X_HOTPLUG_EVENT) && sii902x->bridge.dev) + if ((status & SII902X_HOTPLUG_EVENT) && sii902x->bridge.dev) { drm_helper_hpd_irq_event(sii902x->bridge.dev); + drm_bridge_hpd_notify(&sii902x->bridge, (status & SII902X_PLUGGED_STATUS) + ? connector_status_connected + : connector_status_disconnected); + } return IRQ_HANDLED; } @@ -1001,6 +1042,11 @@ static int sii902x_init(struct sii902x *sii902x) sii902x->bridge.funcs = &sii902x_bridge_funcs; sii902x->bridge.of_node = dev->of_node; sii902x->bridge.timings = &default_sii902x_timings; + sii902x->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID; + + if (sii902x->i2c->irq > 0) + sii902x->bridge.ops |= DRM_BRIDGE_OP_HPD; + drm_bridge_add(&sii902x->bridge); sii902x_audio_codec_init(sii902x, dev); @@ -1022,6 +1068,7 @@ static int sii902x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; + struct device_node *endpoint; struct sii902x *sii902x; int ret; @@ -1049,6 +1096,28 @@ static int sii902x_probe(struct i2c_client *client, return PTR_ERR(sii902x->reset_gpio); } + endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1); + if (endpoint) { + struct device_node *remote = of_graph_get_remote_port_parent(endpoint); + + of_node_put(endpoint); + if (!remote) { + dev_err(dev, "Endpoint in port@1 unconnected\n"); + return -ENODEV; + } + + if (!of_device_is_available(remote)) { + dev_err(dev, "port@1 remote device is disabled\n"); + of_node_put(remote); + return -ENODEV; + } + + sii902x->next_bridge = of_drm_find_bridge(remote); + of_node_put(remote); + if (!sii902x->next_bridge) + return -EPROBE_DEFER; + } + mutex_init(&sii902x->mutex); sii902x->supplies[0].supply = "iovcc"; diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 843265d7f1b1..ec7745c31da0 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -2120,7 +2120,7 @@ static void sii8620_init_rcp_input_dev(struct sii8620 *ctx) if (ret) { dev_err(ctx->dev, "Failed to register RC device\n"); ctx->error = ret; - rc_free_device(ctx->rc_dev); + rc_free_device(rc_dev); return; } ctx->rc_dev = rc_dev; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 54d8fdad395f..4befc104d220 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -2540,7 +2540,7 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, struct drm_display_mode *mode = &crtc_state->mode; u8 max_bpc = conn_state->max_requested_bpc; bool is_hdmi2_sink = info->hdmi.scdc.supported || - (info->color_formats & DRM_COLOR_FORMAT_YCRCB420); + (info->color_formats & DRM_COLOR_FORMAT_YCBCR420); u32 *output_fmts; unsigned int i = 0; @@ -2551,8 +2551,9 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, if (!output_fmts) return NULL; - /* If dw-hdmi is the only bridge, avoid negociating with ourselves */ - if (list_is_singular(&bridge->encoder->bridge_chain)) { + /* If dw-hdmi is the first or only bridge, avoid negociating with ourselves */ + if (list_is_singular(&bridge->encoder->bridge_chain) || + list_is_first(&bridge->chain_node, &bridge->encoder->bridge_chain)) { *num_output_fmts = 1; output_fmts[0] = MEDIA_BUS_FMT_FIXED; @@ -2594,36 +2595,36 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, */ if (max_bpc >= 16 && info->bpc == 16) { - if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) + 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_YCRCB422) + if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24; - if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) + 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_YCRCB422) + if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20; - if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) + 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_YCRCB422) + if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16; - if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) + if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24; /* Default 8bit RGB fallback */ diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index e44e18a0112a..11d20b8638cd 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -871,7 +871,8 @@ static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi) dsi_write(dsi, DSI_INT_MSK1, 0); } -static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge) +static void dw_mipi_dsi_bridge_post_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops; @@ -978,7 +979,8 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge, dw_mipi_dsi_mode_set(dsi->slave, adjusted_mode); } -static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge) +static void dw_mipi_dsi_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); @@ -998,7 +1000,10 @@ dw_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge, enum drm_mode_status mode_status = MODE_OK; if (pdata->mode_valid) - mode_status = pdata->mode_valid(pdata->priv_data, mode); + mode_status = pdata->mode_valid(pdata->priv_data, mode, + dsi->mode_flags, + dw_mipi_dsi_get_lanes(dsi), + dsi->format); return mode_status; } @@ -1032,11 +1037,14 @@ static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge, } static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = { - .mode_set = dw_mipi_dsi_bridge_mode_set, - .enable = dw_mipi_dsi_bridge_enable, - .post_disable = dw_mipi_dsi_bridge_post_disable, - .mode_valid = dw_mipi_dsi_bridge_mode_valid, - .attach = dw_mipi_dsi_bridge_attach, + .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, + .atomic_enable = dw_mipi_dsi_bridge_atomic_enable, + .atomic_post_disable = dw_mipi_dsi_bridge_post_atomic_disable, + .mode_set = dw_mipi_dsi_bridge_mode_set, + .mode_valid = dw_mipi_dsi_bridge_mode_valid, + .attach = dw_mipi_dsi_bridge_attach, }; #ifdef CONFIG_DEBUG_FS @@ -1199,6 +1207,7 @@ __dw_mipi_dsi_probe(struct platform_device *pdev, ret = mipi_dsi_host_register(&dsi->dsi_host); if (ret) { dev_err(dev, "Failed to register MIPI host: %d\n", ret); + pm_runtime_disable(dev); dw_mipi_dsi_debugfs_remove(dsi); return ERR_PTR(ret); } diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 23a6f90b694b..c23e0abc65e8 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -27,7 +27,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> -#include <drm/drm_dp_helper.h> +#include <drm/dp/drm_dp_helper.h> #include <drm/drm_edid.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> diff --git a/drivers/gpu/drm/bridge/tc358775.c b/drivers/gpu/drm/bridge/tc358775.c index 2c76331b251d..695af3badcc7 100644 --- a/drivers/gpu/drm/bridge/tc358775.c +++ b/drivers/gpu/drm/bridge/tc358775.c @@ -22,7 +22,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_crtc_helper.h> -#include <drm/drm_dp_helper.h> +#include <drm/dp/drm_dp_helper.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> @@ -241,7 +241,7 @@ static inline u32 TC358775_LVCFG_PCLKDIV(uint32_t val) } #define TC358775_LVCFG_LVDLINK__MASK 0x00000002 -#define TC358775_LVCFG_LVDLINK__SHIFT 0 +#define TC358775_LVCFG_LVDLINK__SHIFT 1 static inline u32 TC358775_LVCFG_LVDLINK(uint32_t val) { return ((val) << TC358775_LVCFG_LVDLINK__SHIFT) & diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index 945f08de45f1..19daaddd29a4 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -33,6 +33,7 @@ #include <linux/of_device.h> #include <linux/of_graph.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> @@ -143,6 +144,7 @@ struct sn65dsi83 { struct mipi_dsi_device *dsi; struct drm_bridge *panel_bridge; struct gpio_desc *enable_gpio; + struct regulator *vcc; int dsi_lanes; bool lvds_dual_link; bool lvds_dual_link_even_odd_swap; @@ -337,6 +339,12 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge, u16 val; int ret; + ret = regulator_enable(ctx->vcc); + if (ret) { + dev_err(ctx->dev, "Failed to enable vcc: %d\n", ret); + return; + } + /* Deassert reset */ gpiod_set_value(ctx->enable_gpio, 1); usleep_range(1000, 1100); @@ -486,11 +494,16 @@ static void sn65dsi83_atomic_disable(struct drm_bridge *bridge, struct drm_bridge_state *old_bridge_state) { struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge); + int ret; /* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */ gpiod_set_value(ctx->enable_gpio, 0); usleep_range(10000, 11000); + ret = regulator_disable(ctx->vcc); + if (ret) + dev_err(ctx->dev, "Failed to disable vcc: %d\n", ret); + regcache_mark_dirty(ctx->regmap); } @@ -560,10 +573,14 @@ static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model) ctx->host_node = of_graph_get_remote_port_parent(endpoint); of_node_put(endpoint); - if (ctx->dsi_lanes < 0 || ctx->dsi_lanes > 4) - return -EINVAL; - if (!ctx->host_node) - return -ENODEV; + if (ctx->dsi_lanes < 0 || ctx->dsi_lanes > 4) { + ret = -EINVAL; + goto err_put_node; + } + if (!ctx->host_node) { + ret = -ENODEV; + goto err_put_node; + } ctx->lvds_dual_link = false; ctx->lvds_dual_link_even_odd_swap = false; @@ -590,16 +607,27 @@ static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model) ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, &panel_bridge); if (ret < 0) - return ret; + goto err_put_node; if (panel) { panel_bridge = devm_drm_panel_bridge_add(dev, panel); - if (IS_ERR(panel_bridge)) - return PTR_ERR(panel_bridge); + if (IS_ERR(panel_bridge)) { + ret = PTR_ERR(panel_bridge); + goto err_put_node; + } } ctx->panel_bridge = panel_bridge; + ctx->vcc = devm_regulator_get(dev, "vcc"); + if (IS_ERR(ctx->vcc)) + return dev_err_probe(dev, PTR_ERR(ctx->vcc), + "Failed to get supply 'vcc'\n"); + return 0; + +err_put_node: + of_node_put(ctx->host_node); + return ret; } static int sn65dsi83_host_attach(struct sn65dsi83 *ctx) @@ -662,7 +690,8 @@ static int sn65dsi83_probe(struct i2c_client *client, } /* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */ - ctx->enable_gpio = devm_gpiod_get(ctx->dev, "enable", GPIOD_OUT_LOW); + ctx->enable_gpio = devm_gpiod_get_optional(ctx->dev, "enable", + GPIOD_OUT_LOW); if (IS_ERR(ctx->enable_gpio)) return PTR_ERR(ctx->enable_gpio); @@ -673,8 +702,10 @@ static int sn65dsi83_probe(struct i2c_client *client, return ret; ctx->regmap = devm_regmap_init_i2c(client, &sn65dsi83_regmap_config); - if (IS_ERR(ctx->regmap)) - return PTR_ERR(ctx->regmap); + if (IS_ERR(ctx->regmap)) { + ret = PTR_ERR(ctx->regmap); + goto err_put_node; + } dev_set_drvdata(dev, ctx); i2c_set_clientdata(client, ctx); @@ -691,6 +722,8 @@ static int sn65dsi83_probe(struct i2c_client *client, err_remove_bridge: drm_bridge_remove(&ctx->bridge); +err_put_node: + of_node_put(ctx->host_node); return ret; } diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index 68d8415e6c28..fb6c588b0f71 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -26,8 +26,9 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> -#include <drm/drm_dp_aux_bus.h> -#include <drm/drm_dp_helper.h> +#include <drm/drm_bridge_connector.h> +#include <drm/dp/drm_dp_aux_bus.h> +#include <drm/dp/drm_dp_helper.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> @@ -174,7 +175,7 @@ struct ti_sn65dsi86 { struct regmap *regmap; struct drm_dp_aux aux; struct drm_bridge bridge; - struct drm_connector connector; + struct drm_connector *connector; struct device_node *host_node; struct mipi_dsi_device *dsi; struct clk *refclk; @@ -646,54 +647,6 @@ static struct auxiliary_driver ti_sn_aux_driver = { .id_table = ti_sn_aux_id_table, }; -/* ----------------------------------------------------------------------------- - * DRM Connector Operations - */ - -static struct ti_sn65dsi86 * -connector_to_ti_sn65dsi86(struct drm_connector *connector) -{ - return container_of(connector, struct ti_sn65dsi86, connector); -} - -static int ti_sn_bridge_connector_get_modes(struct drm_connector *connector) -{ - struct ti_sn65dsi86 *pdata = connector_to_ti_sn65dsi86(connector); - - return drm_bridge_get_modes(pdata->next_bridge, connector); -} - -static struct drm_connector_helper_funcs ti_sn_bridge_connector_helper_funcs = { - .get_modes = ti_sn_bridge_connector_get_modes, -}; - -static const struct drm_connector_funcs ti_sn_bridge_connector_funcs = { - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = drm_connector_cleanup, - .reset = drm_atomic_helper_connector_reset, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -}; - -static int ti_sn_bridge_connector_init(struct ti_sn65dsi86 *pdata) -{ - int ret; - - ret = drm_connector_init(pdata->bridge.dev, &pdata->connector, - &ti_sn_bridge_connector_funcs, - DRM_MODE_CONNECTOR_eDP); - if (ret) { - DRM_ERROR("Failed to initialize connector with drm\n"); - return ret; - } - - drm_connector_helper_add(&pdata->connector, - &ti_sn_bridge_connector_helper_funcs); - drm_connector_attach_encoder(&pdata->connector, pdata->bridge.encoder); - - return 0; -} - /*------------------------------------------------------------------------------ * DRM Bridge */ @@ -757,10 +710,6 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge, return ret; } - ret = ti_sn_bridge_connector_init(pdata); - if (ret < 0) - goto err_conn_init; - /* We never want the next bridge to *also* create a connector: */ flags |= DRM_BRIDGE_ATTACH_NO_CONNECTOR; @@ -768,13 +717,20 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge, ret = drm_bridge_attach(bridge->encoder, pdata->next_bridge, &pdata->bridge, flags); if (ret < 0) - goto err_dsi_host; + goto err_initted_aux; + + pdata->connector = drm_bridge_connector_init(pdata->bridge.dev, + pdata->bridge.encoder); + if (IS_ERR(pdata->connector)) { + ret = PTR_ERR(pdata->connector); + goto err_initted_aux; + } + + drm_connector_attach_encoder(pdata->connector, pdata->bridge.encoder); return 0; -err_dsi_host: - drm_connector_cleanup(&pdata->connector); -err_conn_init: +err_initted_aux: drm_dp_aux_unregister(&pdata->aux); return ret; } @@ -824,7 +780,7 @@ static void ti_sn_bridge_set_dsi_rate(struct ti_sn65dsi86 *pdata) static unsigned int ti_sn_bridge_get_bpp(struct ti_sn65dsi86 *pdata) { - if (pdata->connector.display_info.bpc <= 6) + if (pdata->connector->display_info.bpc <= 6) return 18; else return 24; |