From 9a8278f1ddf23996b4c0942225383cdd7b4988da Mon Sep 17 00:00:00 2001 From: Vishnu Saini Date: Wed, 21 Jan 2026 17:21:22 +0530 Subject: [PATCH 1/8] Revert "FROMLIST: arm64: dts: qcom: monaco-evk: add lt8713sx bridge for iq8" This reverts commit b017ba7f45d9b2a059da09d19d90bed0b3731c23. --- arch/arm64/boot/dts/qcom/monaco-evk.dts | 44 ------------------------- 1 file changed, 44 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/monaco-evk.dts b/arch/arm64/boot/dts/qcom/monaco-evk.dts index 697a30a9a4755..0138e295baa82 100644 --- a/arch/arm64/boot/dts/qcom/monaco-evk.dts +++ b/arch/arm64/boot/dts/qcom/monaco-evk.dts @@ -399,20 +399,6 @@ firmware-name = "qcom/qcs8300/a623_zap.mbn"; }; -&i2c0 { - pinctrl-0 = <&qup_i2c0_default>; - pinctrl-names = "default"; - - status = "okay"; - - lt8713sx: lt8713sx@4f { - /*Display bridge chip, DP1.4/HDMI2.0/DP++ hub*/ - compatible = "lontium,lt8713sx"; - reg = <0x4f>; - reset-gpios = <&expander5 6 GPIO_ACTIVE_HIGH>; - }; -}; - &i2c1 { pinctrl-0 = <&qup_i2c1_default>; pinctrl-names = "default"; @@ -502,23 +488,6 @@ }; }; -&mdss { - status = "okay"; -}; - -&mdss_dp0 { - status = "okay"; -}; - -&mdss_dp0_out { - data-lanes = <0 1 2 3>; - link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; -}; - -&mdss_dp0_phy { - status = "okay"; -}; - &iris { status = "okay"; }; @@ -614,12 +583,6 @@ }; &tlmm { - dp_hot_plug_det: dp-hot-plug-det-state { - pins = "gpio94"; - function = "edp0_hot"; - bias-disable; - }; - ethernet0_default: ethernet0-default-state { ethernet0_mdc: ethernet0-mdc-pins { pins = "gpio5"; @@ -682,13 +645,6 @@ }; }; - qup_i2c0_default: qup-i2c0-state { - pins = "gpio17", "gpio18"; - function = "qup0_se0"; - drive-strength = <2>; - bias-pull-up; - }; - qup_i2c1_default: qup-i2c1-state { pins = "gpio19", "gpio20"; function = "qup0_se1"; From 384f60df442cd44cfe729e35e361c16bd7aa54b0 Mon Sep 17 00:00:00 2001 From: Vishnu Saini Date: Wed, 21 Jan 2026 17:21:46 +0530 Subject: [PATCH 2/8] Revert "FROMLIST: defconfig: qcom: Enable lt8713sx bridge driver" This reverts commit b566557de8519d64ae0022c235cbbe51c01e7495. --- arch/arm64/configs/defconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 3ecc20978dc1d..15a7635ce8ac1 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -952,7 +952,6 @@ CONFIG_DRM_PANEL_VISIONOX_VTDR6130=m CONFIG_DRM_DISPLAY_CONNECTOR=m CONFIG_DRM_FSL_LDB=m CONFIG_DRM_ITE_IT6263=m -CONFIG_DRM_LONTIUM_LT8713SX=m CONFIG_DRM_LONTIUM_LT8912B=m CONFIG_DRM_LONTIUM_LT9611=m CONFIG_DRM_LONTIUM_LT9611UXC=m From 4a4af3f68b5e20dccf58712e64fc4d87d8546887 Mon Sep 17 00:00:00 2001 From: Vishnu Saini Date: Wed, 21 Jan 2026 17:22:04 +0530 Subject: [PATCH 3/8] Revert "FROMLIST: drm/bridge: add support for lontium lt8713sx bridge driver" This reverts commit ac23e7e5a8e6f12d4fe3d759ed38a5e0bfcff4bc. --- drivers/gpu/drm/bridge/Kconfig | 10 - drivers/gpu/drm/bridge/Makefile | 1 - drivers/gpu/drm/bridge/lontium-lt8713sx.c | 713 ---------------------- 3 files changed, 724 deletions(-) delete mode 100644 drivers/gpu/drm/bridge/lontium-lt8713sx.c diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 7fef383ed7cb7..a250afd8d6622 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -184,16 +184,6 @@ config DRM_LONTIUM_LT9611UXC HDMI signals Please say Y if you have such hardware. -config DRM_LONTIUM_LT8713SX - tristate "Lontium LT8713SX DP MST bridge" - depends on OF - select REGMAP_I2C - help - Driver for Lontium LT8713SX DP MST bridge - chip firmware upgrade, which converts Type-C/DP1.4 - to 3 configurable Type-C/DP1.4/HDMI2.0 outputs - Please say Y if you have such hardware. - config DRM_ITE_IT66121 tristate "ITE IT66121 HDMI bridge" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 07eeb13fa4978..c7dc03182e592 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o obj-$(CONFIG_DRM_LONTIUM_LT9211) += lontium-lt9211.o obj-$(CONFIG_DRM_LONTIUM_LT9611) += lontium-lt9611.o obj-$(CONFIG_DRM_LONTIUM_LT9611UXC) += lontium-lt9611uxc.o -obj-$(CONFIG_DRM_LONTIUM_LT8713SX) += lontium-lt8713sx.o obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o obj-$(CONFIG_DRM_MICROCHIP_LVDS_SERIALIZER) += microchip-lvds.o diff --git a/drivers/gpu/drm/bridge/lontium-lt8713sx.c b/drivers/gpu/drm/bridge/lontium-lt8713sx.c deleted file mode 100644 index a5aed2474a2e9..0000000000000 --- a/drivers/gpu/drm/bridge/lontium-lt8713sx.c +++ /dev/null @@ -1,713 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define FW_FILE "lt8713sx_fw.bin" - -#define LT8713SX_PAGE_SIZE 256 -#define FW_12K_SIZE (12 * 1024) -#define FW_64K_SIZE (64 * 1024) -#define FW_256K_SIZE (256 * 1024) - -struct crc_config { - u8 width; - u32 poly; - u32 crc_init; - u32 xor_out; - bool ref_in; - bool ref_out; -}; - -struct lt8713sx { - struct device *dev; - - struct regmap *regmap; - /* Protects all accesses to registers by stopping the on-chip MCU */ - struct mutex ocm_lock; - - struct gpio_desc *reset_gpio; - struct gpio_desc *enable_gpio; - - struct regulator_bulk_data supplies[2]; - - struct i2c_client *client; - const struct firmware *fw; - - u8 *fw_buffer; - - u32 main_crc_value; - u32 bank_crc_value[17]; - - int bank_num; -}; - -static void lt8713sx_reset(struct lt8713sx *lt8713sx); - -static const struct regmap_range lt8713sx_ranges[] = { - { - .range_min = 0, - .range_max = 0xffff - }, -}; - -static const struct regmap_access_table lt8713sx_table = { - .yes_ranges = lt8713sx_ranges, - .n_yes_ranges = ARRAY_SIZE(lt8713sx_ranges), -}; - -static const struct regmap_config lt8713sx_regmap_config = { - .reg_bits = 8, - .val_bits = 8, - .volatile_table = <8713sx_table, - .cache_type = REGCACHE_NONE, -}; - -static void lt8713sx_i2c_enable(struct lt8713sx *lt8713sx) -{ - regmap_write(lt8713sx->regmap, 0xff, 0xe0); - regmap_write(lt8713sx->regmap, 0xee, 0x01); -} - -static void lt8713sx_i2c_disable(struct lt8713sx *lt8713sx) -{ - regmap_write(lt8713sx->regmap, 0xff, 0xe0); - regmap_write(lt8713sx->regmap, 0xee, 0x00); -} - -static unsigned int bits_reverse(u32 in_val, u8 bits) -{ - u32 out_val = 0; - u8 i; - - for (i = 0; i < bits; i++) { - if (in_val & (1 << i)) - out_val |= 1 << (bits - 1 - i); - } - - return out_val; -} - -static unsigned int get_crc(struct crc_config crc_cfg, const u8 *buf, u64 buf_len) -{ - u8 width = crc_cfg.width; - u32 poly = crc_cfg.poly; - u32 crc = crc_cfg.crc_init; - u32 xorout = crc_cfg.xor_out; - bool refin = crc_cfg.ref_in; - bool refout = crc_cfg.ref_out; - u8 n; - u32 bits; - u32 data; - u8 i; - - n = (width < 8) ? 0 : (width - 8); - crc = (width < 8) ? (crc << (8 - width)) : crc; - bits = (width < 8) ? 0x80 : (1 << (width - 1)); - poly = (width < 8) ? (poly << (8 - width)) : poly; - - while (buf_len--) { - data = *(buf++); - if (refin) - data = bits_reverse(data, 8); - crc ^= (data << n); - for (i = 0; i < 8; i++) { - if (crc & bits) - crc = (crc << 1) ^ poly; - else - crc = crc << 1; - } - } - crc = (width < 8) ? (crc >> (8 - width)) : crc; - if (refout) - crc = bits_reverse(crc, width); - crc ^= xorout; - - return (crc & ((2 << (width - 1)) - 1)); -} - -static u32 calculate_64K_crc(const u8 *upgrade_data, u64 len) -{ - struct crc_config crc_cfg = { - .width = 8, - .poly = 0x31, - .crc_init = 0, - .xor_out = 0, - .ref_out = false, - .ref_in = false, - }; - u64 crc_size = FW_64K_SIZE - 1; - u8 default_val = 0xFF; - - crc_cfg.crc_init = get_crc(crc_cfg, upgrade_data, len); - - crc_size -= len; - while (crc_size--) - crc_cfg.crc_init = get_crc(crc_cfg, &default_val, 1); - - return crc_cfg.crc_init; -} - -static u32 calculate_12K_crc(const u8 *upgrade_data, u64 len) -{ - struct crc_config crc_cfg = { - .width = 8, - .poly = 0x31, - .crc_init = 0, - .xor_out = 0, - .ref_out = false, - .ref_in = false, - }; - u64 crc_size = FW_12K_SIZE; - u8 default_val = 0xFF; - - crc_cfg.crc_init = get_crc(crc_cfg, upgrade_data, len); - - crc_size -= len; - while (crc_size--) - crc_cfg.crc_init = get_crc(crc_cfg, &default_val, 1); - - return crc_cfg.crc_init; -} - -static int lt8713sx_prepare_firmware_data(struct lt8713sx *lt8713sx) -{ - int ret = 0; - - ret = request_firmware(<8713sx->fw, FW_FILE, lt8713sx->dev); - if (ret < 0) { - pr_err("request firmware failed\n"); - return ret; - } - - pr_debug("Firmware size: %zu bytes\n", lt8713sx->fw->size); - - if (lt8713sx->fw->size > FW_256K_SIZE - 1) { - pr_err("Firmware size exceeds 256KB limit\n"); - release_firmware(lt8713sx->fw); - return -EINVAL; - } - - lt8713sx->fw_buffer = kzalloc(FW_256K_SIZE, GFP_KERNEL); - if (!lt8713sx->fw_buffer) { - release_firmware(lt8713sx->fw); - return -ENOMEM; - } - - memset(lt8713sx->fw_buffer, 0xFF, FW_256K_SIZE); - - if (lt8713sx->fw->size < FW_64K_SIZE) { - /*TODO: CRC should be calculated with 0xff also */ - memcpy(lt8713sx->fw_buffer, lt8713sx->fw->data, lt8713sx->fw->size); - lt8713sx->fw_buffer[FW_64K_SIZE - 1] = - calculate_64K_crc(lt8713sx->fw->data, lt8713sx->fw->size); - lt8713sx->main_crc_value = lt8713sx->fw_buffer[FW_64K_SIZE - 1]; - pr_debug("Main Firmware Data Crc=0x%02X\n", lt8713sx->main_crc_value); - - } else { - //main firmware - memcpy(lt8713sx->fw_buffer, lt8713sx->fw->data, FW_64K_SIZE - 1); - lt8713sx->fw_buffer[FW_64K_SIZE - 1] = - calculate_64K_crc(lt8713sx->fw_buffer, FW_64K_SIZE - 1); - lt8713sx->main_crc_value = lt8713sx->fw_buffer[FW_64K_SIZE - 1]; - pr_debug("Main Firmware Data Crc=0x%02X\n", lt8713sx->main_crc_value); - - //bank firmware - memcpy(lt8713sx->fw_buffer + FW_64K_SIZE, - lt8713sx->fw->data + FW_64K_SIZE, - lt8713sx->fw->size - FW_64K_SIZE); - - lt8713sx->bank_num = (lt8713sx->fw->size - FW_64K_SIZE + FW_12K_SIZE - 1) / - FW_12K_SIZE; - pr_debug("Bank Number Total is %d.\n", lt8713sx->bank_num); - - for (int i = 0; i < lt8713sx->bank_num; i++) { - lt8713sx->bank_crc_value[i] = - calculate_12K_crc(lt8713sx->fw_buffer + FW_64K_SIZE + - i * FW_12K_SIZE, - FW_12K_SIZE); - pr_debug("Bank number:%d; Firmware Data Crc:0x%02X\n", - i, lt8713sx->bank_crc_value[i]); - } - } - return 0; -} - -static void lt8713sx_config_parameters(struct lt8713sx *lt8713sx) -{ - regmap_write(lt8713sx->regmap, 0xFF, 0xE0); - regmap_write(lt8713sx->regmap, 0xEE, 0x01); - regmap_write(lt8713sx->regmap, 0x5E, 0xC1); - regmap_write(lt8713sx->regmap, 0x58, 0x00); - regmap_write(lt8713sx->regmap, 0x59, 0x50); - regmap_write(lt8713sx->regmap, 0x5A, 0x10); - regmap_write(lt8713sx->regmap, 0x5A, 0x00); - regmap_write(lt8713sx->regmap, 0x58, 0x21); -} - -static void lt8713sx_wren(struct lt8713sx *lt8713sx) -{ - regmap_write(lt8713sx->regmap, 0xff, 0xe1); - regmap_write(lt8713sx->regmap, 0x03, 0xbf); - regmap_write(lt8713sx->regmap, 0x03, 0xff); - regmap_write(lt8713sx->regmap, 0xff, 0xe0); - regmap_write(lt8713sx->regmap, 0x5a, 0x04); - regmap_write(lt8713sx->regmap, 0x5a, 0x00); -} - -static void lt8713sx_wrdi(struct lt8713sx *lt8713sx) -{ - regmap_write(lt8713sx->regmap, 0x5A, 0x08); - regmap_write(lt8713sx->regmap, 0x5A, 0x00); -} - -static void lt8713sx_fifo_reset(struct lt8713sx *lt8713sx) -{ - regmap_write(lt8713sx->regmap, 0xff, 0xe1); - regmap_write(lt8713sx->regmap, 0x03, 0xbf); - regmap_write(lt8713sx->regmap, 0x03, 0xff); -} - -static void lt8713sx_disable_sram_write(struct lt8713sx *lt8713sx) -{ - regmap_write(lt8713sx->regmap, 0xff, 0xe0); - regmap_write(lt8713sx->regmap, 0x55, 0x00); -} - -static void lt8713sx_sram_to_flash(struct lt8713sx *lt8713sx) -{ - regmap_write(lt8713sx->regmap, 0x5a, 0x30); - regmap_write(lt8713sx->regmap, 0x5a, 0x00); -} - -static void lt8713sx_i2c_to_sram(struct lt8713sx *lt8713sx) -{ - regmap_write(lt8713sx->regmap, 0x55, 0x80); - regmap_write(lt8713sx->regmap, 0x5e, 0xc0); - regmap_write(lt8713sx->regmap, 0x58, 0x21); -} - -static u8 lt8713sx_read_flash_status(struct lt8713sx *lt8713sx) -{ - u32 flash_status = 0; - - regmap_write(lt8713sx->regmap, 0xFF, 0xE1);//fifo_rst_n - regmap_write(lt8713sx->regmap, 0x03, 0x3F); - regmap_write(lt8713sx->regmap, 0x03, 0xFF); - - regmap_write(lt8713sx->regmap, 0xFF, 0xE0); - regmap_write(lt8713sx->regmap, 0x5e, 0x40); - regmap_write(lt8713sx->regmap, 0x56, 0x05);//opcode=read status register - regmap_write(lt8713sx->regmap, 0x55, 0x25); - regmap_write(lt8713sx->regmap, 0x55, 0x01); - regmap_write(lt8713sx->regmap, 0x58, 0x21); - - regmap_read(lt8713sx->regmap, 0x5f, &flash_status); - pr_debug("flash_status:%x\n", flash_status); - - return flash_status; -} - -static void lt8713sx_block_erase(struct lt8713sx *lt8713sx) -{ - u32 i = 0; - u8 flash_status = 0; - u8 blocknum = 0x00; - u32 flashaddr = 0x00; - - for (blocknum = 0; blocknum < 8; blocknum++) { - flashaddr = blocknum * 0x008000; - regmap_write(lt8713sx->regmap, 0xFF, 0xE0); - regmap_write(lt8713sx->regmap, 0xEE, 0x01); - regmap_write(lt8713sx->regmap, 0x5A, 0x04); - regmap_write(lt8713sx->regmap, 0x5A, 0x00); - regmap_write(lt8713sx->regmap, 0x5B, flashaddr >> 16);//set flash address[23:16] - regmap_write(lt8713sx->regmap, 0x5C, flashaddr >> 8);//set flash address[15:8] - regmap_write(lt8713sx->regmap, 0x5D, flashaddr);//set flash address[7:0] - regmap_write(lt8713sx->regmap, 0x5A, 0x01); - regmap_write(lt8713sx->regmap, 0x5A, 0x00); - msleep(100); //delay 100ms - i = 0; - while (1) { - flash_status = lt8713sx_read_flash_status(lt8713sx); //wait erase finish - if ((flash_status & 0x01) == 0) - break; - - if (i > 50) - break; - - i++; - msleep(50); //delay 50ms - } - } - pr_debug("erase flash done.\n"); -} - -static void lt8713sx_load_main_fw_to_sram(struct lt8713sx *lt8713sx) -{ - regmap_write(lt8713sx->regmap, 0xff, 0xe0); - regmap_write(lt8713sx->regmap, 0xee, 0x01); - regmap_write(lt8713sx->regmap, 0x68, 0x00); - regmap_write(lt8713sx->regmap, 0x69, 0x00); - regmap_write(lt8713sx->regmap, 0x6a, 0x00); - regmap_write(lt8713sx->regmap, 0x65, 0x00); - regmap_write(lt8713sx->regmap, 0x66, 0xff); - regmap_write(lt8713sx->regmap, 0x67, 0xff); - regmap_write(lt8713sx->regmap, 0x6b, 0x00); - regmap_write(lt8713sx->regmap, 0x6c, 0x00); - regmap_write(lt8713sx->regmap, 0x60, 0x01); - msleep(200); - regmap_write(lt8713sx->regmap, 0x60, 0x00); -} - -static void lt8713sx_load_bank_fw_to_sram(struct lt8713sx *lt8713sx, u64 addr) -{ - regmap_write(lt8713sx->regmap, 0xff, 0xe0); - regmap_write(lt8713sx->regmap, 0xee, 0x01); - regmap_write(lt8713sx->regmap, 0x68, ((addr & 0xFF0000) >> 16)); - regmap_write(lt8713sx->regmap, 0x69, ((addr & 0x00FF00) >> 8)); - regmap_write(lt8713sx->regmap, 0x6a, (addr & 0x0000FF)); - regmap_write(lt8713sx->regmap, 0x65, 0x00); - regmap_write(lt8713sx->regmap, 0x66, 0x30); - regmap_write(lt8713sx->regmap, 0x67, 0x00); - regmap_write(lt8713sx->regmap, 0x6b, 0x00); - regmap_write(lt8713sx->regmap, 0x6c, 0x00); - regmap_write(lt8713sx->regmap, 0x60, 0x01); - msleep(50); - regmap_write(lt8713sx->regmap, 0x60, 0x00); -} - -static int lt8713sx_write_data(struct lt8713sx *lt8713sx, const u8 *data, u64 filesize) -{ - int page = 0, num = 0, i = 0, val; - - page = (filesize % LT8713SX_PAGE_SIZE) ? - ((filesize / LT8713SX_PAGE_SIZE) + 1) : (filesize / LT8713SX_PAGE_SIZE); - - pr_debug("Writing to Sram=%u pages, total size = %llu bytes\n", page, filesize); - - for (num = 0; num < page; num++) { - pr_debug("page[%d]\n", num); - lt8713sx_i2c_to_sram(lt8713sx); - - for (i = 0; i < LT8713SX_PAGE_SIZE; i++) { - if ((num * LT8713SX_PAGE_SIZE + i) < filesize) - val = *(data + (num * LT8713SX_PAGE_SIZE + i)); - else - val = 0xFF; - regmap_write(lt8713sx->regmap, 0x59, val); - } - - lt8713sx_wren(lt8713sx); - lt8713sx_sram_to_flash(lt8713sx); - } - - lt8713sx_wrdi(lt8713sx); - lt8713sx_disable_sram_write(lt8713sx); - - return 0; -} - -static void lt8713sx_main_upgrade_result(struct lt8713sx *lt8713sx) -{ - u32 main_crc_result; - - regmap_write(lt8713sx->regmap, 0xff, 0xe0); - regmap_read(lt8713sx->regmap, 0x23, &main_crc_result); - - pr_debug("Main CRC HW: 0x%02X\n", main_crc_result); - pr_debug("Main CRC FW: 0x%02X\n", lt8713sx->main_crc_value); - - if (main_crc_result == lt8713sx->main_crc_value) - pr_debug("Main Firmware Upgrade Success.\n"); - else - pr_err("Main Firmware Upgrade Failed.\n"); -} - -static void lt8713sx_bank_upgrade_result(struct lt8713sx *lt8713sx, u8 banknum) -{ - u32 bank_crc_result; - - regmap_write(lt8713sx->regmap, 0xff, 0xe0); - - regmap_read(lt8713sx->regmap, 0x23, &bank_crc_result); - - pr_debug("Bank %d CRC Result: 0x%02X\n", banknum, bank_crc_result); - - if (bank_crc_result == lt8713sx->bank_crc_value[banknum]) - pr_debug("Bank %d Firmware Upgrade Success.\n", banknum); - else - pr_err("Bank %d Firmware Upgrade Failed.\n", banknum); -} - -static void lt8713sx_bank_result_check(struct lt8713sx *lt8713sx) -{ - int i; - u64 addr = 0x010000; - - for (i = 0; i < lt8713sx->bank_num; i++) { - lt8713sx_load_bank_fw_to_sram(lt8713sx, addr); - lt8713sx_bank_upgrade_result(lt8713sx, i); - addr += 0x3000; - } -} - -static int lt8713sx_firmware_upgrade(struct lt8713sx *lt8713sx) -{ - int ret; - - lt8713sx_config_parameters(lt8713sx); - - lt8713sx_block_erase(lt8713sx); - - if (lt8713sx->fw->size < FW_64K_SIZE) { - ret = lt8713sx_write_data(lt8713sx, lt8713sx->fw_buffer, FW_64K_SIZE); - if (ret < 0) { - pr_err("Failed to write firmware data: %d\n", ret); - return ret; - } - } else { - ret = lt8713sx_write_data(lt8713sx, lt8713sx->fw_buffer, lt8713sx->fw->size); - if (ret < 0) { - pr_err("Failed to write firmware data: %d\n", ret); - return ret; - } - } - - pr_debug("Write Data done.\n"); - - return 0; -} - -static int lt8713sx_firmware_update(struct lt8713sx *lt8713sx) -{ - int ret = 0; - - mutex_lock(<8713sx->ocm_lock); - lt8713sx_i2c_enable(lt8713sx); - - ret = lt8713sx_prepare_firmware_data(lt8713sx); - if (ret < 0) { - pr_err("Failed to prepare firmware data: %d\n", ret); - goto error; - } - - ret = lt8713sx_firmware_upgrade(lt8713sx); - if (ret < 0) { - pr_err("Upgrade failure.\n"); - goto error; - } else { - /* Validate CRC */ - lt8713sx_load_main_fw_to_sram(lt8713sx); - lt8713sx_main_upgrade_result(lt8713sx); - lt8713sx_wrdi(lt8713sx); - lt8713sx_fifo_reset(lt8713sx); - lt8713sx_bank_result_check(lt8713sx); - lt8713sx_wrdi(lt8713sx); - } - -error: - lt8713sx_i2c_disable(lt8713sx); - if (!ret) - lt8713sx_reset(lt8713sx); - - kfree(lt8713sx->fw_buffer); - lt8713sx->fw_buffer = NULL; - - if (lt8713sx->fw) { - release_firmware(lt8713sx->fw); - lt8713sx->fw = NULL; - } - mutex_unlock(<8713sx->ocm_lock); - - return ret; -} - -static void lt8713sx_reset(struct lt8713sx *lt8713sx) -{ - pr_debug("reset bridge.\n"); - gpiod_set_value_cansleep(lt8713sx->reset_gpio, 1); - msleep(20); - - gpiod_set_value_cansleep(lt8713sx->reset_gpio, 0); - msleep(20); - - gpiod_set_value_cansleep(lt8713sx->reset_gpio, 1); - msleep(20); - pr_debug("reset done.\n"); -} - -static int lt8713sx_regulator_init(struct lt8713sx *lt8713sx) -{ - int ret; - - lt8713sx->supplies[0].supply = "vdd"; - lt8713sx->supplies[1].supply = "vcc"; - - ret = devm_regulator_bulk_get(lt8713sx->dev, 2, lt8713sx->supplies); - if (ret < 0) - return dev_err_probe(lt8713sx->dev, ret, "failed to get regulators\n"); - - ret = regulator_set_load(lt8713sx->supplies[0].consumer, 200000); - if (ret < 0) - return dev_err_probe(lt8713sx->dev, ret, "failed to set regulator load\n"); - - return 0; -} - -static int lt8713sx_regulator_enable(struct lt8713sx *lt8713sx) -{ - int ret; - - ret = regulator_enable(lt8713sx->supplies[0].consumer); - if (ret < 0) - return dev_err_probe(lt8713sx->dev, ret, "failed to enable vdd regulator\n"); - - usleep_range(1000, 10000); - - ret = regulator_enable(lt8713sx->supplies[1].consumer); - if (ret < 0) { - regulator_disable(lt8713sx->supplies[0].consumer); - return dev_err_probe(lt8713sx->dev, ret, "failed to enable vcc regulator\n"); - } - return 0; -} - -static int lt8713sx_gpio_init(struct lt8713sx *lt8713sx) -{ - struct device *dev = lt8713sx->dev; - - lt8713sx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(lt8713sx->reset_gpio)) - return dev_err_probe(dev, PTR_ERR(lt8713sx->reset_gpio), - "failed to acquire reset gpio\n"); - - /* power enable gpio */ - lt8713sx->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); - if (IS_ERR(lt8713sx->enable_gpio)) - return dev_err_probe(dev, PTR_ERR(lt8713sx->enable_gpio), - "failed to acquire enable gpio\n"); - return 0; -} - -static ssize_t lt8713sx_firmware_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct lt8713sx *lt8713sx = dev_get_drvdata(dev); - int ret; - - ret = lt8713sx_firmware_update(lt8713sx); - if (ret < 0) - return ret; - return len; -} - -static DEVICE_ATTR_WO(lt8713sx_firmware); - -static struct attribute *lt8713sx_attrs[] = { - &dev_attr_lt8713sx_firmware.attr, - NULL, -}; - -static const struct attribute_group lt8713sx_attr_group = { - .attrs = lt8713sx_attrs, -}; - -static const struct attribute_group *lt8713sx_attr_groups[] = { - <8713sx_attr_group, - NULL, -}; - -static int lt8713sx_probe(struct i2c_client *client) -{ - struct lt8713sx *lt8713sx; - struct device *dev = &client->dev; - int ret; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) - return dev_err_probe(dev, -ENODEV, "device doesn't support I2C\n"); - - lt8713sx = devm_kzalloc(dev, sizeof(*lt8713sx), GFP_KERNEL); - if (!lt8713sx) - return dev_err_probe(dev, -ENOMEM, "failed to allocate lt8713sx struct\n"); - - lt8713sx->dev = dev; - lt8713sx->client = client; - i2c_set_clientdata(client, lt8713sx); - - mutex_init(<8713sx->ocm_lock); - - lt8713sx->regmap = devm_regmap_init_i2c(client, <8713sx_regmap_config); - if (IS_ERR(lt8713sx->regmap)) - return dev_err_probe(dev, PTR_ERR(lt8713sx->regmap), "regmap i2c init failed\n"); - - ret = lt8713sx_gpio_init(lt8713sx); - if (ret < 0) - goto err_of_put; - - ret = lt8713sx_regulator_init(lt8713sx); - if (ret < 0) - goto err_of_put; - - ret = lt8713sx_regulator_enable(lt8713sx); - if (ret) - goto err_of_put; - - lt8713sx_reset(lt8713sx); - - return 0; - -err_of_put: - return ret; -} - -static void lt8713sx_remove(struct i2c_client *client) -{ - struct lt8713sx *lt8713sx = i2c_get_clientdata(client); - - mutex_destroy(<8713sx->ocm_lock); - - regulator_bulk_disable(ARRAY_SIZE(lt8713sx->supplies), lt8713sx->supplies); -} - -static struct i2c_device_id lt8713sx_id[] = { - { "lontium,lt8713sx", 0 }, - { /* sentinel */ } -}; - -static const struct of_device_id lt8713sx_match_table[] = { - { .compatible = "lontium,lt8713sx" }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, lt8713sx_match_table); - -static struct i2c_driver lt8713sx_driver = { - .driver = { - .name = "lt8713sx", - .of_match_table = lt8713sx_match_table, - .dev_groups = lt8713sx_attr_groups, - }, - .probe = lt8713sx_probe, - .remove = lt8713sx_remove, - .id_table = lt8713sx_id, -}; - -module_i2c_driver(lt8713sx_driver); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("lt8713sx drm bridge driver"); -MODULE_AUTHOR("Tony "); -MODULE_FIRMWARE(FW_FILE); From f2bbd306465f8a120be4a389109f8bddfa20be58 Mon Sep 17 00:00:00 2001 From: Vishnu Saini Date: Wed, 21 Jan 2026 17:22:20 +0530 Subject: [PATCH 4/8] Revert "FROMLIST: dt-bindings: bridge: lt8713sx: Add bindings" This reverts commit f562df8e56dedc2e91f268ae1daebc4359bd4e25. --- .../display/bridge/lontium,lt8713sx.yaml | 52 ------------------- 1 file changed, 52 deletions(-) delete mode 100644 Documentation/devicetree/bindings/display/bridge/lontium,lt8713sx.yaml diff --git a/Documentation/devicetree/bindings/display/bridge/lontium,lt8713sx.yaml b/Documentation/devicetree/bindings/display/bridge/lontium,lt8713sx.yaml deleted file mode 100644 index 3fb7e042c6887..0000000000000 --- a/Documentation/devicetree/bindings/display/bridge/lontium,lt8713sx.yaml +++ /dev/null @@ -1,52 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/display/bridge/lontium,lt8713sx.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Lontium LT8713SX Type-C/DP1.4 to Type-C/DP1.4/HDMI2.0/DP++ bridge-hub - -maintainers: - - Tony - -description: - The Lontium LT8713SX is a Type-C/DP1.4 to Type-C/DP1.4/HDMI2.0 converter - that integrates one DP input and up to three configurable output interfaces - (DP1.4 / HDMI2.0 / DP++), with SST/MST functionality and audio support. - -properties: - compatible: - enum: - - lontium,lt8713sx - - reg: - maxItems: 1 - - vcc-supply: - description: Regulator for 3.3V vcc. - - vdd-supply: - description: Regulator for 1.1V vdd. - - reset-gpios: - description: GPIO connected to active high RESET pin. - -required: - - compatible - - reg - -additionalProperties: false - -examples: - - | - #include - - i2c { - #address-cells = <1>; - #size-cells = <0>; - bridge@4f { - compatible = "lontium,lt8713sx"; - reg = <0x4f>; - reset-gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>; - }; - }; From ded7229fb8ee79e57294f44ea04c28cb23facf9e Mon Sep 17 00:00:00 2001 From: Vishnu Saini Date: Sun, 28 Dec 2025 17:10:39 +0530 Subject: [PATCH 5/8] FROMLIST: dt-bindings: bridge: lt8713sx: Add bindings Add bindings for lt8713sx. Link: https://lore.kernel.org/all/20251228-lt8713sx-bridge-driver-v3-1-9169fbef0e5b@oss.qualcomm.com/ Co-developed-by: Prahlad Valluru Signed-off-by: Prahlad Valluru Signed-off-by: Vishnu Saini Reviewed-by: Krzysztof Kozlowski --- .../display/bridge/lontium,lt8713sx.yaml | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/bridge/lontium,lt8713sx.yaml diff --git a/Documentation/devicetree/bindings/display/bridge/lontium,lt8713sx.yaml b/Documentation/devicetree/bindings/display/bridge/lontium,lt8713sx.yaml new file mode 100644 index 0000000000000..0a6dc56e337cb --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/lontium,lt8713sx.yaml @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/lontium,lt8713sx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Lontium LT8713SX Type-C/DP1.4 to Type-C/DP1.4/HDMI2.0/DP++ bridge-hub + +maintainers: + - Tony + +description: + The Lontium LT8713SX is a Type-C/DP1.4 to Type-C/DP1.4/HDMI2.0 converter + that integrates one DP input and up to three configurable output interfaces + (DP1.4 / HDMI2.0 / DP++), with SST/MST functionality and audio support. + +properties: + compatible: + enum: + - lontium,lt8713sx + + reg: + maxItems: 1 + + vcc-supply: + description: Regulator for 3.3V vcc. + + vdd-supply: + description: Regulator for 1.1V vdd. + + reset-gpios: + description: GPIO connected to active low RESET pin. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: + DP port for DP input from soc to bridge chip + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: + DP port for DP output from bridge + + port@2: + $ref: /schemas/graph.yaml#/properties/port + description: + Additional DP port for DP output from bridge + + required: + - port@0 + +required: + - compatible + - reg + - ports + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + bridge@4f { + compatible = "lontium,lt8713sx"; + reg = <0x4f>; + reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lt8713sx_dp_in: endpoint { + remote-endpoint = <&mdss_dp0_out>; + }; + }; + + port@1 { + reg = <1>; + lt8713sx_dp0_out: endpoint { + remote-endpoint = <&dp0_connector_in>; + }; + }; + + port@2 { + reg = <2>; + lt8713sx_dp1_out: endpoint { + remote-endpoint = <&dp1_connector_in>; + }; + }; + }; + }; + }; From e1e7d8f427342304301234a7e8107162de6da198 Mon Sep 17 00:00:00 2001 From: Vishnu Saini Date: Sun, 28 Dec 2025 17:10:40 +0530 Subject: [PATCH 6/8] FROMLIST: drm/bridge: add support for lontium lt8713sx bridge driver The lt8713sx is a Type-C/DP1.4 to Type-C/DP1.4/HDMI2.0 converter, with three configurable DP1.4/HDMI2.0/DP++ output interfaces and audio output interface. Driver is required for firmware upgrade in the bridge chip. Link: https://lore.kernel.org/all/20251228-lt8713sx-bridge-driver-v3-2-9169fbef0e5b@oss.qualcomm.com/ Co-developed-by: Prahlad Valluru Signed-off-by: Prahlad Valluru Signed-off-by: Vishnu Saini --- drivers/gpu/drm/bridge/Kconfig | 10 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/lontium-lt8713sx.c | 682 ++++++++++++++++++++++ 3 files changed, 693 insertions(+) create mode 100644 drivers/gpu/drm/bridge/lontium-lt8713sx.c diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index a250afd8d6622..7fef383ed7cb7 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -184,6 +184,16 @@ config DRM_LONTIUM_LT9611UXC HDMI signals Please say Y if you have such hardware. +config DRM_LONTIUM_LT8713SX + tristate "Lontium LT8713SX DP MST bridge" + depends on OF + select REGMAP_I2C + help + Driver for Lontium LT8713SX DP MST bridge + chip firmware upgrade, which converts Type-C/DP1.4 + to 3 configurable Type-C/DP1.4/HDMI2.0 outputs + Please say Y if you have such hardware. + config DRM_ITE_IT66121 tristate "ITE IT66121 HDMI bridge" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index c7dc03182e592..07eeb13fa4978 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o obj-$(CONFIG_DRM_LONTIUM_LT9211) += lontium-lt9211.o obj-$(CONFIG_DRM_LONTIUM_LT9611) += lontium-lt9611.o obj-$(CONFIG_DRM_LONTIUM_LT9611UXC) += lontium-lt9611uxc.o +obj-$(CONFIG_DRM_LONTIUM_LT8713SX) += lontium-lt8713sx.o obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o obj-$(CONFIG_DRM_MICROCHIP_LVDS_SERIALIZER) += microchip-lvds.o diff --git a/drivers/gpu/drm/bridge/lontium-lt8713sx.c b/drivers/gpu/drm/bridge/lontium-lt8713sx.c new file mode 100644 index 0000000000000..6ea54ff3733d4 --- /dev/null +++ b/drivers/gpu/drm/bridge/lontium-lt8713sx.c @@ -0,0 +1,682 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FW_FILE "lt8713sx_fw.bin" + +#define REG_PAGE_CONTROL 0xff + +#define MAX_OUTPUT_PORTS 3 +#define LT8713SX_PAGE_SIZE 256 + +DECLARE_CRC8_TABLE(lt8713sx_crc_table); + +struct lt8713sx { + struct device *dev; + struct drm_bridge bridge; + struct drm_bridge *next_bridge[MAX_OUTPUT_PORTS]; + int num_outputs; + + struct regmap *regmap; + /* Protects all accesses to registers by stopping the on-chip MCU */ + struct mutex ocm_lock; + + struct gpio_desc *reset_gpio; + struct gpio_desc *enable_gpio; + + struct i2c_client *client; + const struct firmware *fw; + + u8 *fw_buffer; + + u32 main_crc_value; + u32 bank_crc_value[17]; + + int bank_num; +}; + +static void lt8713sx_reset(struct lt8713sx *lt8713sx); + +static const struct regmap_range lt8713sx_ranges[] = { + { + .range_min = 0x0000, + .range_max = 0xffff + }, +}; + +static const struct regmap_access_table lt8713sx_table = { + .yes_ranges = lt8713sx_ranges, + .n_yes_ranges = ARRAY_SIZE(lt8713sx_ranges), +}; + +static const struct regmap_range_cfg lt8713sx_range_cfg = { + .name = "lt8713sx", + .range_min = 0x0000, + .range_max = 0xffff, + .selector_reg = REG_PAGE_CONTROL, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, +}; + +static const struct regmap_config lt8713sx_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_table = <8713sx_table, + .ranges = <8713sx_range_cfg, + .num_ranges = 1, + .cache_type = REGCACHE_NONE, + .max_register = 0xffff, +}; + +static void lt8713sx_i2c_enable(struct lt8713sx *lt8713sx) +{ + regmap_write(lt8713sx->regmap, 0xe0ee, 0x01); +} + +static void lt8713sx_i2c_disable(struct lt8713sx *lt8713sx) +{ + regmap_write(lt8713sx->regmap, 0xe0ee, 0x00); +} + +static u32 calculate_crc(const u8 *upgrade_data, u64 len, u64 crc_size) +{ + u8 crc = 0x00; + u8 pad = 0xff; + + crc = crc8(lt8713sx_crc_table, upgrade_data, len, crc); + + /* pad remaining bytes */ + crc_size -= len; + while (crc_size--) + crc = crc8(lt8713sx_crc_table, &pad, 1, crc); + + return crc; +} + +static int lt8713sx_prepare_firmware_data(struct lt8713sx *lt8713sx) +{ + int ret = 0; + u64 sz_12k = 12 * SZ_1K; + + ret = request_firmware(<8713sx->fw, FW_FILE, lt8713sx->dev); + if (ret < 0) { + dev_err(lt8713sx->dev, "request firmware failed\n"); + return ret; + } + + dev_dbg(lt8713sx->dev, "Firmware size: %zu bytes\n", lt8713sx->fw->size); + + if (lt8713sx->fw->size > SZ_256K - 1) { + dev_err(lt8713sx->dev, "Firmware size exceeds 256KB limit\n"); + release_firmware(lt8713sx->fw); + return -EINVAL; + } + + lt8713sx->fw_buffer = kvmalloc(SZ_256K, GFP_KERNEL); + if (!lt8713sx->fw_buffer) { + release_firmware(lt8713sx->fw); + return -ENOMEM; + } + + memset(lt8713sx->fw_buffer, 0xff, SZ_256K); + + if (lt8713sx->fw->size < SZ_64K) { + memcpy(lt8713sx->fw_buffer, lt8713sx->fw->data, lt8713sx->fw->size); + lt8713sx->fw_buffer[SZ_64K - 1] = + calculate_crc(lt8713sx->fw->data, lt8713sx->fw->size, SZ_64K - 1); + lt8713sx->main_crc_value = lt8713sx->fw_buffer[SZ_64K - 1]; + dev_dbg(lt8713sx->dev, + "Main Firmware Data Crc=0x%02X\n", lt8713sx->main_crc_value); + + } else { + /* main firmware */ + memcpy(lt8713sx->fw_buffer, lt8713sx->fw->data, SZ_64K - 1); + lt8713sx->fw_buffer[SZ_64K - 1] = + calculate_crc(lt8713sx->fw_buffer, SZ_64K - 1, SZ_64K - 1); + lt8713sx->main_crc_value = lt8713sx->fw_buffer[SZ_64K - 1]; + dev_dbg(lt8713sx->dev, + "Main Firmware Data Crc=0x%02X\n", lt8713sx->main_crc_value); + + /* bank firmware */ + memcpy(lt8713sx->fw_buffer + SZ_64K, + lt8713sx->fw->data + SZ_64K, + lt8713sx->fw->size - SZ_64K); + + lt8713sx->bank_num = (lt8713sx->fw->size - SZ_64K + sz_12k - 1) / sz_12k; + dev_dbg(lt8713sx->dev, "Bank Number Total is %d.\n", lt8713sx->bank_num); + + for (int i = 0; i < lt8713sx->bank_num; i++) { + lt8713sx->bank_crc_value[i] = + calculate_crc(lt8713sx->fw_buffer + SZ_64K + i * sz_12k, + sz_12k, sz_12k); + dev_dbg(lt8713sx->dev, "Bank number:%d; Firmware Data Crc:0x%02X\n", + i, lt8713sx->bank_crc_value[i]); + } + } + return 0; +} + +static void lt8713sx_config_parameters(struct lt8713sx *lt8713sx) +{ + regmap_write(lt8713sx->regmap, 0xe0ee, 0x01); + regmap_write(lt8713sx->regmap, 0xe05e, 0xc1); + regmap_write(lt8713sx->regmap, 0xe058, 0x00); + regmap_write(lt8713sx->regmap, 0xe059, 0x50); + regmap_write(lt8713sx->regmap, 0xe05a, 0x10); + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); + regmap_write(lt8713sx->regmap, 0xe058, 0x21); +} + +static void lt8713sx_wren(struct lt8713sx *lt8713sx) +{ + regmap_write(lt8713sx->regmap, 0xe103, 0xbf); + regmap_write(lt8713sx->regmap, 0xe103, 0xff); + regmap_write(lt8713sx->regmap, 0xe05a, 0x04); + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); +} + +static void lt8713sx_wrdi(struct lt8713sx *lt8713sx) +{ + regmap_write(lt8713sx->regmap, 0xe05a, 0x08); + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); +} + +static void lt8713sx_fifo_reset(struct lt8713sx *lt8713sx) +{ + regmap_write(lt8713sx->regmap, 0xe103, 0xbf); + regmap_write(lt8713sx->regmap, 0xe103, 0xff); +} + +static void lt8713sx_disable_sram_write(struct lt8713sx *lt8713sx) +{ + regmap_write(lt8713sx->regmap, 0xe055, 0x00); +} + +static void lt8713sx_sram_to_flash(struct lt8713sx *lt8713sx) +{ + regmap_write(lt8713sx->regmap, 0xe05a, 0x30); + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); +} + +static void lt8713sx_i2c_to_sram(struct lt8713sx *lt8713sx) +{ + regmap_write(lt8713sx->regmap, 0xe055, 0x80); + regmap_write(lt8713sx->regmap, 0xe05e, 0xc0); + regmap_write(lt8713sx->regmap, 0xe058, 0x21); +} + +static u8 lt8713sx_read_flash_status(struct lt8713sx *lt8713sx) +{ + u32 flash_status = 0; + + regmap_write(lt8713sx->regmap, 0xe103, 0x3f); + regmap_write(lt8713sx->regmap, 0xe103, 0xff); + + regmap_write(lt8713sx->regmap, 0xe05e, 0x40); + regmap_write(lt8713sx->regmap, 0xe056, 0x05); /* opcode=read status register */ + regmap_write(lt8713sx->regmap, 0xe055, 0x25); + regmap_write(lt8713sx->regmap, 0xe055, 0x01); + regmap_write(lt8713sx->regmap, 0xe058, 0x21); + + regmap_read(lt8713sx->regmap, 0xe05f, &flash_status); + dev_dbg(lt8713sx->dev, "flash_status:%x\n", flash_status); + + return flash_status; +} + +static void lt8713sx_block_erase(struct lt8713sx *lt8713sx) +{ + u32 i = 0; + u8 flash_status = 0; + u8 blocknum = 0x00; + u32 flashaddr = 0x00; + + for (blocknum = 0; blocknum < 8; blocknum++) { + flashaddr = blocknum * SZ_32K; + regmap_write(lt8713sx->regmap, 0xe0ee, 0x01); + regmap_write(lt8713sx->regmap, 0xe05a, 0x04); + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); + regmap_write(lt8713sx->regmap, 0xe05b, flashaddr >> 16); + regmap_write(lt8713sx->regmap, 0xe05c, flashaddr >> 8); + regmap_write(lt8713sx->regmap, 0xe05d, flashaddr); + regmap_write(lt8713sx->regmap, 0xe05a, 0x01); + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); + msleep(100); + i = 0; + while (1) { + flash_status = lt8713sx_read_flash_status(lt8713sx); + if ((flash_status & 0x01) == 0) + break; + + if (i > 50) + break; + + i++; + msleep(50); + } + } + dev_dbg(lt8713sx->dev, "erase flash done.\n"); +} + +static void lt8713sx_load_main_fw_to_sram(struct lt8713sx *lt8713sx) +{ + regmap_write(lt8713sx->regmap, 0xe0ee, 0x01); + regmap_write(lt8713sx->regmap, 0xe068, 0x00); + regmap_write(lt8713sx->regmap, 0xe069, 0x00); + regmap_write(lt8713sx->regmap, 0xe06a, 0x00); + regmap_write(lt8713sx->regmap, 0xe065, 0x00); + regmap_write(lt8713sx->regmap, 0xe066, 0xff); + regmap_write(lt8713sx->regmap, 0xe067, 0xff); + regmap_write(lt8713sx->regmap, 0xe06b, 0x00); + regmap_write(lt8713sx->regmap, 0xe06c, 0x00); + regmap_write(lt8713sx->regmap, 0xe060, 0x01); + msleep(200); + regmap_write(lt8713sx->regmap, 0xe060, 0x00); +} + +static void lt8713sx_load_bank_fw_to_sram(struct lt8713sx *lt8713sx, u64 addr) +{ + regmap_write(lt8713sx->regmap, 0xe0ee, 0x01); + regmap_write(lt8713sx->regmap, 0xe068, ((addr & 0xff0000) >> 16)); + regmap_write(lt8713sx->regmap, 0xe069, ((addr & 0x00ff00) >> 8)); + regmap_write(lt8713sx->regmap, 0xe06a, (addr & 0x0000ff)); + regmap_write(lt8713sx->regmap, 0xe065, 0x00); + regmap_write(lt8713sx->regmap, 0xe066, 0x30); + regmap_write(lt8713sx->regmap, 0xe067, 0x00); + regmap_write(lt8713sx->regmap, 0xe06b, 0x00); + regmap_write(lt8713sx->regmap, 0xe06c, 0x00); + regmap_write(lt8713sx->regmap, 0xe060, 0x01); + msleep(50); + regmap_write(lt8713sx->regmap, 0xe060, 0x00); +} + +static int lt8713sx_write_data(struct lt8713sx *lt8713sx, const u8 *data, u64 filesize) +{ + int page = 0, num = 0, i = 0, val; + + page = (filesize % LT8713SX_PAGE_SIZE) ? + ((filesize / LT8713SX_PAGE_SIZE) + 1) : (filesize / LT8713SX_PAGE_SIZE); + + dev_dbg(lt8713sx->dev, + "Writing to Sram=%u pages, total size = %llu bytes\n", page, filesize); + + for (num = 0; num < page; num++) { + dev_dbg(lt8713sx->dev, "page[%d]\n", num); + lt8713sx_i2c_to_sram(lt8713sx); + + for (i = 0; i < LT8713SX_PAGE_SIZE; i++) { + if ((num * LT8713SX_PAGE_SIZE + i) < filesize) + val = *(data + (num * LT8713SX_PAGE_SIZE + i)); + else + val = 0xff; + regmap_write(lt8713sx->regmap, 0xe059, val); + } + + lt8713sx_wren(lt8713sx); + lt8713sx_sram_to_flash(lt8713sx); + } + + lt8713sx_wrdi(lt8713sx); + lt8713sx_disable_sram_write(lt8713sx); + + return 0; +} + +static void lt8713sx_main_upgrade_result(struct lt8713sx *lt8713sx) +{ + u32 main_crc_result; + + regmap_read(lt8713sx->regmap, 0xe023, &main_crc_result); + + dev_dbg(lt8713sx->dev, "Main CRC HW: 0x%02X\n", main_crc_result); + dev_dbg(lt8713sx->dev, "Main CRC FW: 0x%02X\n", lt8713sx->main_crc_value); + + if (main_crc_result == lt8713sx->main_crc_value) + dev_dbg(lt8713sx->dev, "Main Firmware Upgrade Success.\n"); + else + dev_err(lt8713sx->dev, "Main Firmware Upgrade Failed.\n"); +} + +static void lt8713sx_bank_upgrade_result(struct lt8713sx *lt8713sx, u8 banknum) +{ + u32 bank_crc_result; + + regmap_read(lt8713sx->regmap, 0xe023, &bank_crc_result); + + dev_dbg(lt8713sx->dev, "Bank %d CRC Result: 0x%02X\n", banknum, bank_crc_result); + + if (bank_crc_result == lt8713sx->bank_crc_value[banknum]) + dev_dbg(lt8713sx->dev, "Bank %d Firmware Upgrade Success.\n", banknum); + else + dev_err(lt8713sx->dev, "Bank %d Firmware Upgrade Failed.\n", banknum); +} + +static void lt8713sx_bank_result_check(struct lt8713sx *lt8713sx) +{ + int i; + u64 addr = 0x010000; + + for (i = 0; i < lt8713sx->bank_num; i++) { + lt8713sx_load_bank_fw_to_sram(lt8713sx, addr); + lt8713sx_bank_upgrade_result(lt8713sx, i); + addr += 0x3000; + } +} + +static int lt8713sx_firmware_upgrade(struct lt8713sx *lt8713sx) +{ + int ret; + + lt8713sx_config_parameters(lt8713sx); + + lt8713sx_block_erase(lt8713sx); + + if (lt8713sx->fw->size < SZ_64K) { + ret = lt8713sx_write_data(lt8713sx, lt8713sx->fw_buffer, SZ_64K); + if (ret < 0) { + dev_err(lt8713sx->dev, "Failed to write firmware data: %d\n", ret); + return ret; + } + } else { + ret = lt8713sx_write_data(lt8713sx, lt8713sx->fw_buffer, lt8713sx->fw->size); + if (ret < 0) { + dev_err(lt8713sx->dev, "Failed to write firmware data: %d\n", ret); + return ret; + } + } + dev_dbg(lt8713sx->dev, "Write Data done.\n"); + + return 0; +} + +static int lt8713sx_firmware_update(struct lt8713sx *lt8713sx) +{ + int ret = 0; + + mutex_lock(<8713sx->ocm_lock); + lt8713sx_i2c_enable(lt8713sx); + + ret = lt8713sx_prepare_firmware_data(lt8713sx); + if (ret < 0) { + dev_err(lt8713sx->dev, "Failed to prepare firmware data: %d\n", ret); + goto error; + } + + ret = lt8713sx_firmware_upgrade(lt8713sx); + if (ret < 0) { + dev_err(lt8713sx->dev, "Upgrade failure.\n"); + goto error; + } else { + /* Validate CRC */ + lt8713sx_load_main_fw_to_sram(lt8713sx); + lt8713sx_main_upgrade_result(lt8713sx); + lt8713sx_wrdi(lt8713sx); + lt8713sx_fifo_reset(lt8713sx); + lt8713sx_bank_result_check(lt8713sx); + lt8713sx_wrdi(lt8713sx); + } + +error: + lt8713sx_i2c_disable(lt8713sx); + if (!ret) + lt8713sx_reset(lt8713sx); + + kvfree(lt8713sx->fw_buffer); + lt8713sx->fw_buffer = NULL; + + if (lt8713sx->fw) { + release_firmware(lt8713sx->fw); + lt8713sx->fw = NULL; + } + mutex_unlock(<8713sx->ocm_lock); + + return ret; +} + +static void lt8713sx_reset(struct lt8713sx *lt8713sx) +{ + dev_dbg(lt8713sx->dev, "reset bridge.\n"); + gpiod_set_value_cansleep(lt8713sx->reset_gpio, 1); + msleep(20); + + gpiod_set_value_cansleep(lt8713sx->reset_gpio, 0); + msleep(20); + + dev_dbg(lt8713sx->dev, "reset done.\n"); +} + +static int lt8713sx_regulator_enable(struct lt8713sx *lt8713sx) +{ + int ret; + + ret = devm_regulator_get_enable(lt8713sx->dev, "vdd"); + if (ret < 0) + return dev_err_probe(lt8713sx->dev, ret, "failed to enable vdd regulator\n"); + + usleep_range(1000, 10000); + + ret = devm_regulator_get_enable(lt8713sx->dev, "vcc"); + if (ret < 0) + return dev_err_probe(lt8713sx->dev, ret, "failed to enable vcc regulator\n"); + return 0; +} + +static int lt8713sx_bridge_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder, + enum drm_bridge_attach_flags flags) +{ + struct lt8713sx *lt8713sx = container_of(bridge, struct lt8713sx, bridge); + int i, ret; + + for (i = 0; i < lt8713sx->num_outputs; i++) { + if (!lt8713sx->next_bridge[i]) + continue; + + ret = drm_bridge_attach(encoder, + lt8713sx->next_bridge[i], + bridge, flags); + if (ret) + return ret; + } + + return 0; +} + +static int lt8713sx_get_ports(struct lt8713sx *lt8713sx) +{ + struct device *dev = lt8713sx->dev; + struct device_node *port, *ports, *ep, *remote; + int i = 0; + u32 reg; + + ports = of_get_child_by_name(dev->of_node, "ports"); + if (!ports) + return -ENODEV; + + for_each_child_of_node(ports, port) { + if (of_property_read_u32(port, "reg", ®)) + continue; + + if (reg == 0) + continue; + + if (i >= ARRAY_SIZE(lt8713sx->next_bridge)) { + of_node_put(port); + break; + } + + ep = of_graph_get_next_endpoint(port, NULL); + if (!ep) + continue; + + remote = of_graph_get_remote_port_parent(ep); + of_node_put(ep); + + if (!remote) + continue; + + lt8713sx->next_bridge[i] = of_drm_find_bridge(remote); + of_node_put(remote); + if (lt8713sx->next_bridge[i]) + i++; + } + lt8713sx->num_outputs = i; + dev_dbg(dev, "Enabled %d output ports", i); + + of_node_put(ports); + return 0; +}; + +static int lt8713sx_gpio_init(struct lt8713sx *lt8713sx) +{ + struct device *dev = lt8713sx->dev; + + lt8713sx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(lt8713sx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(lt8713sx->reset_gpio), + "failed to acquire reset gpio\n"); + + /* power enable gpio */ + lt8713sx->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(lt8713sx->enable_gpio)) + return dev_err_probe(dev, PTR_ERR(lt8713sx->enable_gpio), + "failed to acquire enable gpio\n"); + return 0; +} + +static ssize_t lt8713sx_firmware_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct lt8713sx *lt8713sx = dev_get_drvdata(dev); + int ret; + + ret = lt8713sx_firmware_update(lt8713sx); + if (ret < 0) + return ret; + return len; +} + +static DEVICE_ATTR_WO(lt8713sx_firmware); + +static struct attribute *lt8713sx_attrs[] = { + &dev_attr_lt8713sx_firmware.attr, + NULL, +}; + +static const struct attribute_group lt8713sx_attr_group = { + .attrs = lt8713sx_attrs, +}; + +static const struct attribute_group *lt8713sx_attr_groups[] = { + <8713sx_attr_group, + NULL, +}; + +static const struct drm_bridge_funcs lt8713sx_bridge_funcs = { + .attach = lt8713sx_bridge_attach, +}; + +static int lt8713sx_probe(struct i2c_client *client) +{ + struct lt8713sx *lt8713sx; + struct device *dev = &client->dev; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return dev_err_probe(dev, -ENODEV, "device doesn't support I2C\n"); + + lt8713sx = devm_drm_bridge_alloc(dev, struct lt8713sx, bridge, <8713sx_bridge_funcs); + if (IS_ERR(lt8713sx)) + return PTR_ERR(lt8713sx); + + lt8713sx->dev = dev; + lt8713sx->client = client; + i2c_set_clientdata(client, lt8713sx); + + ret = devm_mutex_init(lt8713sx->dev, <8713sx->ocm_lock); + if (ret) + return ret; + + lt8713sx->regmap = devm_regmap_init_i2c(client, <8713sx_regmap_config); + if (IS_ERR(lt8713sx->regmap)) + return dev_err_probe(dev, PTR_ERR(lt8713sx->regmap), "regmap i2c init failed\n"); + + ret = lt8713sx_get_ports(lt8713sx); + if (ret < 0) + return ret; + + ret = lt8713sx_gpio_init(lt8713sx); + if (ret < 0) + return ret; + + ret = lt8713sx_regulator_enable(lt8713sx); + if (ret) + return ret; + + lt8713sx_reset(lt8713sx); + + lt8713sx->bridge.funcs = <8713sx_bridge_funcs; + lt8713sx->bridge.of_node = dev->of_node; + lt8713sx->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; + drm_bridge_add(<8713sx->bridge); + + crc8_populate_msb(lt8713sx_crc_table, 0x31); + + return 0; +} + +static void lt8713sx_remove(struct i2c_client *client) +{ + struct lt8713sx *lt8713sx = i2c_get_clientdata(client); + + drm_bridge_remove(<8713sx->bridge); +} + +static struct i2c_device_id lt8713sx_id[] = { + { "lontium,lt8713sx", 0 }, + { /* sentinel */ } +}; + +static const struct of_device_id lt8713sx_match_table[] = { + { .compatible = "lontium,lt8713sx" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, lt8713sx_match_table); + +static struct i2c_driver lt8713sx_driver = { + .driver = { + .name = "lt8713sx", + .of_match_table = lt8713sx_match_table, + .dev_groups = lt8713sx_attr_groups, + }, + .probe = lt8713sx_probe, + .remove = lt8713sx_remove, + .id_table = lt8713sx_id, +}; + +module_i2c_driver(lt8713sx_driver); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("lt8713sx drm bridge driver"); +MODULE_AUTHOR("Tony "); +MODULE_FIRMWARE(FW_FILE); From ae25346db3aeaf4e8ee2e15e85660e84069c8271 Mon Sep 17 00:00:00 2001 From: Vishnu Saini Date: Wed, 21 Jan 2026 19:12:57 +0530 Subject: [PATCH 7/8] FROMLIST: arm64: dts: qcom: monaco: add lt8713sx bridge with displayport Monaco-evk has LT8713sx which act as DP to 3 DP output converter. Edp PHY from monaco soc is connected to lt8713sx as input and output of lt8713sx is connected to 3 mini DP ports. Two ports are available in mainboard and one port is available on Mezz board. lt8713sx is connected to soc over i2c0 and with reset gpio connected to pin6 of ioexpander5. Enable the edp nodes from monaco and enable lontium lt8713sx bridge node. Link: https://lore.kernel.org/all/20251228-lt8713sx-bridge-linux-for-next-v3-1-3f77ad84d7d1@oss.qualcomm.com/ Co-developed-by: Prahlad Valluru Signed-off-by: Prahlad Valluru Signed-off-by: Vishnu Saini --- arch/arm64/boot/dts/qcom/monaco-evk.dts | 89 +++++++++++++++++++++++++ arch/arm64/boot/dts/qcom/qcs8300.dtsi | 6 ++ 2 files changed, 95 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/monaco-evk.dts b/arch/arm64/boot/dts/qcom/monaco-evk.dts index 0138e295baa82..a734292802b40 100644 --- a/arch/arm64/boot/dts/qcom/monaco-evk.dts +++ b/arch/arm64/boot/dts/qcom/monaco-evk.dts @@ -28,6 +28,30 @@ stdout-path = "serial0:115200n8"; }; + dp-connector0 { + compatible = "dp-connector"; + label = "DP"; + type = "mini"; + + port { + dp0_connector_in: endpoint { + remote-endpoint = <<8713sx_dp0_out>; + }; + }; + }; + + dp-connector1 { + compatible = "dp-connector"; + label = "DP"; + type = "mini"; + + port { + dp1_connector_in: endpoint { + remote-endpoint = <<8713sx_dp1_out>; + }; + }; + }; + dmic: audio-codec-0 { compatible = "dmic-codec"; #sound-dai-cells = <0>; @@ -399,6 +423,42 @@ firmware-name = "qcom/qcs8300/a623_zap.mbn"; }; +&i2c0 { + status = "okay"; + + bridge@4f { + compatible = "lontium,lt8713sx"; + reg = <0x4f>; + reset-gpios = <&expander5 6 GPIO_ACTIVE_LOW>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lt8713sx_dp_in: endpoint { + remote-endpoint = <&mdss_dp0_out>; + }; + }; + + port@1 { + reg = <1>; + lt8713sx_dp0_out: endpoint { + remote-endpoint = <&dp0_connector_in>; + }; + }; + + port@2 { + reg = <2>; + lt8713sx_dp1_out: endpoint { + remote-endpoint = <&dp1_connector_in>; + }; + }; + }; + }; +}; + &i2c1 { pinctrl-0 = <&qup_i2c1_default>; pinctrl-names = "default"; @@ -492,6 +552,30 @@ status = "okay"; }; +&mdss { + status = "okay"; +}; + +&mdss_dp0 { + pinctrl-0 = <&dp_hot_plug_det>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&mdss_dp0_out { + data-lanes = <0 1 2 3>; + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; + remote-endpoint = <<8713sx_dp_in>; +}; + +&mdss_dp0_phy { + vdda-phy-supply = <&vreg_l5a>; + vdda-pll-supply = <&vreg_l4a>; + + status = "okay"; +}; + &psci { reboot-mode { mode-bootloader = <0x10001 0x2>; @@ -499,6 +583,11 @@ }; }; +&qup_i2c0_data_clk { + drive-strength = <2>; + bias-pull-up; +}; + &qupv3_id_0 { firmware-name = "qcom/qcs8300/qupv3fw.elf"; status = "okay"; diff --git a/arch/arm64/boot/dts/qcom/qcs8300.dtsi b/arch/arm64/boot/dts/qcom/qcs8300.dtsi index 04d1fea53ea1e..2783b3525e43c 100644 --- a/arch/arm64/boot/dts/qcom/qcs8300.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs8300.dtsi @@ -5995,6 +5995,12 @@ }; }; + dp_hot_plug_det: dp-hot-plug-det-state { + pins = "gpio94"; + function = "edp0_hot"; + bias-disable; + }; + hs0_mi2s_active: hs0-mi2s-active-state { pins = "gpio106", "gpio107", "gpio108", "gpio109"; function = "hs0_mi2s"; From 5ea08d16c18e5c2aa585dcf1b59bd451ce0b3716 Mon Sep 17 00:00:00 2001 From: Vishnu Saini Date: Wed, 21 Jan 2026 19:06:10 +0530 Subject: [PATCH 8/8] FROMLIST: arm64: defconfig: Enable Lontium LT8713sx driver Lontium LT8713sx DP bridge hub can be found on a Qualcomm Monaco EVK board for converting 1 DP to 3 DP output. Link: https://lore.kernel.org/all/20251228-lt8713sx-bridge-linux-for-next-v3-2-3f77ad84d7d1@oss.qualcomm.com/ Signed-off-by: Vishnu Saini --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 15a7635ce8ac1..3ecc20978dc1d 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -952,6 +952,7 @@ CONFIG_DRM_PANEL_VISIONOX_VTDR6130=m CONFIG_DRM_DISPLAY_CONNECTOR=m CONFIG_DRM_FSL_LDB=m CONFIG_DRM_ITE_IT6263=m +CONFIG_DRM_LONTIUM_LT8713SX=m CONFIG_DRM_LONTIUM_LT8912B=m CONFIG_DRM_LONTIUM_LT9611=m CONFIG_DRM_LONTIUM_LT9611UXC=m