* [PATCH v3 7/7] arm64: dts: qcom: sc7180: Add QSPI memory interconnect path
From: Viken Dadhaniya @ 2026-04-20 6:12 UTC (permalink / raw)
To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, cros-qcom-dts-watchers
Cc: linux-arm-msm, linux-spi, devicetree, linux-kernel,
Viken Dadhaniya
In-Reply-To: <20260420-spi-nor-v3-0-7de325a29010@oss.qualcomm.com>
Add the missing QSPI-to-memory interconnect path alongside the existing
configuration path. Without this path, the interconnect framework cannot
correctly vote for the bandwidth required by QSPI DMA data transfers.
Signed-off-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/sc7180.dtsi | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/sc7180.dtsi b/arch/arm64/boot/dts/qcom/sc7180.dtsi
index 45b9864e3304..7515d982b38e 100644
--- a/arch/arm64/boot/dts/qcom/sc7180.dtsi
+++ b/arch/arm64/boot/dts/qcom/sc7180.dtsi
@@ -2864,9 +2864,12 @@ qspi: spi@88dc000 {
clocks = <&gcc GCC_QSPI_CNOC_PERIPH_AHB_CLK>,
<&gcc GCC_QSPI_CORE_CLK>;
clock-names = "iface", "core";
- interconnects = <&gem_noc MASTER_APPSS_PROC 0
- &config_noc SLAVE_QSPI_0 0>;
- interconnect-names = "qspi-config";
+ interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY
+ &config_noc SLAVE_QSPI_0 QCOM_ICC_TAG_ACTIVE_ONLY>,
+ <&aggre1_noc MASTER_QSPI QCOM_ICC_TAG_ALWAYS
+ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
+ interconnect-names = "qspi-config",
+ "qspi-memory";
power-domains = <&rpmhpd SC7180_CX>;
operating-points-v2 = <&qspi_opp_table>;
status = "disabled";
--
2.34.1
^ permalink raw reply related
* [PATCH v3 2/2] drm/bridge: This patch add new DRM bridge driver for LT9611C(EX/UXD) chip
From: syyang @ 2026-04-20 6:16 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, andrzej.hajda, neil.armstrong,
dmitry.baryshkov, maarten.lankhorst, rfoss, mripard
Cc: Laurent.pinchart, tzimmermann, jonas, jernej.skrabec, devicetree,
dri-devel, linux-kernel, yangsunyun1993, xmzhu, Sunyun Yang
In-Reply-To: <20260420061644.1251070-1-syyang@lontium.com>
From: Sunyun Yang <syyang@lontium.com>
LT9611C(EX/UXD) is a high performance Single/Dual-Port MIPI to
HDMI 1.4/2.0 converter:
-Single/Dual-port MIPI DSI Receiver
1. Compliantwith D-PHY1.2&DSI-2 1.0
2. 1/2configurable ports
3. 1 clock lane and 1/2/3/4 configurable data lanes per port
4. 80Mbps~2.5Gbps per data lane
5. Support RGB666, loosely RGB666, RGB888, RGB565,16-bit YCbCr4:2:2
-HDMI 1.4/2.0 Transmitter
1.Data rate up to 6Gbps
2.Support HDCP1.4/2.3
3.Support CEC,HDR10
4.Support lane swap
-audio
1.sample rates of 32~192 KHz and sample sizes
of 16~24 bits
2.SPDIF interface supports PCM, Dolbydigital, DTS digital audio
at up to 192KHz frame rate
-Miscellaneous
1.CSC:RGB<->YUV444<->YUV422
Signed-off-by: Sunyun Yang <syyang@lontium.com>
---
drivers/gpu/drm/bridge/Kconfig | 18 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/lontium-lt9611c.c | 1365 ++++++++++++++++++++++
3 files changed, 1384 insertions(+)
create mode 100755 drivers/gpu/drm/bridge/lontium-lt9611c.c
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index c3209b0f4678..32b85a2a65d9 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -177,6 +177,24 @@ config DRM_LONTIUM_LT9611
HDMI signals
Please say Y if you have such hardware.
+config DRM_LONTIUM_LT9611C
+ tristate "Lontium LT9611C DSI/HDMI bridge"
+ select SND_SOC_HDMI_CODEC if SND_SOC
+ depends on OF
+ select CRC8
+ select FW_LOADER
+ select DRM_PANEL_BRIDGE
+ select DRM_KMS_HELPER
+ select DRM_MIPI_DSI
+ select DRM_DISPLAY_HELPER
+ select DRM_DISPLAY_HDMI_STATE_HELPER
+ select REGMAP_I2C
+ help
+ Driver for Lontium DSI to HDMI bridge
+ chip driver that converts dual DSI and I2S to
+ HDMI signals
+ Please say Y if you have such hardware.
+
config DRM_LONTIUM_LT9611UXC
tristate "Lontium LT9611UXC DSI/HDMI bridge"
select SND_SOC_HDMI_CODEC if SND_SOC
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index beab5b695a6e..92688be9692f 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
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_LT9611C) += lontium-lt9611c.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
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611c.c b/drivers/gpu/drm/bridge/lontium-lt9611c.c
new file mode 100755
index 000000000000..a6d11d0bddf5
--- /dev/null
+++ b/drivers/gpu/drm/bridge/lontium-lt9611c.c
@@ -0,0 +1,1365 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Lontium Semiconductor, Inc.
+ */
+
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_of.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include <drm/display/drm_hdmi_audio_helper.h>
+#include <drm/display/drm_hdmi_state_helper.h>
+#include <sound/hdmi-codec.h>
+
+#define FW_SIZE (64 * 1024)
+#define LT_PAGE_SIZE 256
+#define FW_FILE "LT9611C.bin"
+#define LT9611C_CRC_POLYNOMIAL 0x31
+#define LT9611C_PAGE_CONTROL 0xff
+
+struct lt9611c {
+ struct device *dev;
+ struct i2c_client *client;
+ struct drm_bridge bridge;
+ struct drm_bridge *next_bridge;
+ struct regmap *regmap;
+ /* Protects all accesses to registers by stopping the on-chip MCU */
+ struct mutex ocm_lock;
+ struct work_struct work;
+ struct device_node *dsi0_node;
+ struct device_node *dsi1_node;
+ struct mipi_dsi_device *dsi0;
+ struct mipi_dsi_device *dsi1;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data supplies[2];
+ u32 chip_type;
+ const struct firmware *fw;
+ int fw_version;
+ u8 fw_crc;
+ bool hdmi_connected;
+};
+
+DECLARE_CRC8_TABLE(lt9611c_crc8_table);
+
+static const struct regmap_range_cfg lt9611c_ranges[] = {
+ {
+ .name = "register_range",
+ .range_min = 0,
+ .range_max = 0xffff,
+ .selector_reg = LT9611C_PAGE_CONTROL,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 0x100,
+ },
+};
+
+static const struct regmap_config lt9611c_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xffff,
+ .ranges = lt9611c_ranges,
+ .num_ranges = ARRAY_SIZE(lt9611c_ranges),
+};
+
+static int lt9611c_read_write_flow(struct lt9611c *lt9611c, u8 *params,
+ unsigned int param_count, u8 *return_buffer,
+ unsigned int return_count)
+{
+ int count, i;
+ unsigned int temp;
+
+ regmap_write(lt9611c->regmap, 0xe0de, 0x01);
+
+ count = 0;
+ do {
+ regmap_read(lt9611c->regmap, 0xe0ae, &temp);
+ usleep_range(1000, 2000);
+ count++;
+ } while (count < 100 && temp != 0x01);
+
+ if (temp != 0x01)
+ return -1;
+
+ for (i = 0; i < param_count; i++) {
+ if (i > 0xdd - 0xb0)
+ break;
+
+ regmap_write(lt9611c->regmap, 0xe0b0 + i, params[i]);
+ }
+
+ regmap_write(lt9611c->regmap, 0xe0de, 0x02);
+
+ count = 0;
+ do {
+ regmap_read(lt9611c->regmap, 0xe0ae, &temp);
+ usleep_range(1000, 2000);
+ count++;
+ } while (count < 100 && temp != 0x02);
+
+ if (temp != 0x02)
+ return -2;
+
+ regmap_bulk_read(lt9611c->regmap, 0xe085, return_buffer, return_count);
+
+ return 0;
+}
+
+static int lt9611c_prepare_firmware_data(struct lt9611c *lt9611c)
+{
+ struct device *dev = lt9611c->dev;
+ int ret;
+ u8 *buffer;
+ size_t total_size = FW_SIZE - 1;
+
+ ret = request_firmware(<9611c->fw, FW_FILE, dev);
+ if (ret) {
+ dev_err(dev, "failed load file '%s', error type %d\n", FW_FILE, ret);
+ return -EPROBE_DEFER;
+ }
+
+ if (lt9611c->fw->size > total_size) {
+ dev_err(dev, "firmware too large (%zu > %zu)\n", lt9611c->fw->size, total_size);
+ release_firmware(lt9611c->fw);
+ lt9611c->fw = NULL;
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "firmware size: %zu bytes\n", lt9611c->fw->size);
+
+ buffer = kzalloc(total_size, GFP_KERNEL);
+ if (!buffer) {
+ release_firmware(lt9611c->fw);
+ lt9611c->fw = NULL;
+ return -ENOMEM;
+ }
+
+ memset(buffer, 0xff, total_size);
+ memcpy(buffer, lt9611c->fw->data, lt9611c->fw->size);
+
+ lt9611c->fw_crc = crc8(lt9611c_crc8_table, buffer, total_size, 0);
+
+ dev_dbg(dev, "firmware crc: 0x%02x\n", lt9611c->fw_crc);
+
+ kfree(buffer);
+ return 0;
+}
+
+static void lt9611c_config_parameters(struct lt9611c *lt9611c)
+{
+ const struct reg_sequence seq_write_paras[] = {
+ REG_SEQ0(0xe0ee, 0x01),
+ REG_SEQ0(0xe103, 0x3f), //fifo rst
+ REG_SEQ0(0xe103, 0xff),
+ REG_SEQ0(0xe05e, 0xc1),
+ REG_SEQ0(0xe058, 0x00),
+ REG_SEQ0(0xe059, 0x50),
+ REG_SEQ0(0xe05a, 0x10),
+ REG_SEQ0(0xe05a, 0x00),
+ REG_SEQ0(0xe058, 0x21),
+ };
+
+ regmap_multi_reg_write(lt9611c->regmap, seq_write_paras, ARRAY_SIZE(seq_write_paras));
+}
+
+static void lt9611c_wren(struct lt9611c *lt9611c)
+{
+ regmap_write(lt9611c->regmap, 0xe05a, 0x04);
+ regmap_write(lt9611c->regmap, 0xe05a, 0x00);
+}
+
+static void lt9611c_wrdi(struct lt9611c *lt9611c)
+{
+ regmap_write(lt9611c->regmap, 0xe05a, 0x08);
+ regmap_write(lt9611c->regmap, 0xe05a, 0x00);
+}
+
+static void lt9611c_erase_op(struct lt9611c *lt9611c, u32 addr)
+{
+ const struct reg_sequence seq_write[] = {
+ REG_SEQ0(0xe0ee, 0x01),
+ REG_SEQ0(0xe05a, 0x04),
+ REG_SEQ0(0xe05a, 0x00),
+ REG_SEQ0(0xe05b, (addr >> 16) & 0xff),
+ REG_SEQ0(0xe05c, (addr >> 8) & 0xff),
+ REG_SEQ0(0xe05d, addr & 0xff),
+ REG_SEQ0(0xe05a, 0x01),
+ REG_SEQ0(0xe05a, 0x00),
+ };
+
+ regmap_multi_reg_write(lt9611c->regmap, seq_write, ARRAY_SIZE(seq_write));
+}
+
+static void read_flash_reg_status(struct lt9611c *lt9611c, unsigned int *status)
+{
+ const struct reg_sequence seq_write[] = {
+ REG_SEQ0(0xe103, 0x3f),
+ REG_SEQ0(0xe103, 0xff),
+ REG_SEQ0(0xe05e, 0x40),
+ REG_SEQ0(0xe056, 0x05),
+ REG_SEQ0(0xe055, 0x25),
+ REG_SEQ0(0xe055, 0x01),
+ REG_SEQ0(0xe058, 0x21),
+ };
+
+ regmap_multi_reg_write(lt9611c->regmap, seq_write, ARRAY_SIZE(seq_write));
+
+ regmap_read(lt9611c->regmap, 0xe05f, status);
+}
+
+static void lt9611c_crc_to_sram(struct lt9611c *lt9611c)
+{
+ const struct reg_sequence seq_write[] = {
+ REG_SEQ0(0xe051, 0x00),
+ REG_SEQ0(0xe055, 0xc0),
+ REG_SEQ0(0xe055, 0x80),
+ REG_SEQ0(0xe05e, 0xc0),
+ REG_SEQ0(0xe058, 0x21),
+ };
+
+ regmap_multi_reg_write(lt9611c->regmap, seq_write, ARRAY_SIZE(seq_write));
+}
+
+static void lt9611c_data_to_sram(struct lt9611c *lt9611c)
+{
+ const struct reg_sequence seq_write[] = {
+ REG_SEQ0(0xe051, 0xff),
+ REG_SEQ0(0xe055, 0x80),
+ REG_SEQ0(0xe05e, 0xc0),
+ REG_SEQ0(0xe058, 0x21),
+ };
+
+ regmap_multi_reg_write(lt9611c->regmap, seq_write, ARRAY_SIZE(seq_write));
+}
+
+static void lt9611c_sram_to_flash(struct lt9611c *lt9611c, size_t addr)
+{
+ const struct reg_sequence seq_write[] = {
+ REG_SEQ0(0xe05b, (addr >> 16) & 0xff),
+ REG_SEQ0(0xe05c, (addr >> 8) & 0xff),
+ REG_SEQ0(0xe05d, addr & 0xff),
+ REG_SEQ0(0xe05a, 0x30),
+ REG_SEQ0(0xe05a, 0x00),
+ };
+
+ regmap_multi_reg_write(lt9611c->regmap, seq_write, ARRAY_SIZE(seq_write));
+}
+
+static void lt9611c_block_erase(struct lt9611c *lt9611c)
+{
+ struct device *dev = lt9611c->dev;
+ int i;
+ unsigned int block_num;
+ unsigned int flash_status = 0;
+ u32 flash_addr = 0;
+
+ for (block_num = 0; block_num < 2; block_num++) {
+ flash_addr = (block_num * 0x008000);
+ lt9611c_erase_op(lt9611c, flash_addr);
+ msleep(100);
+ i = 0;
+ while (1) {
+ read_flash_reg_status(lt9611c, &flash_status);
+ if ((flash_status & 0x01) == 0)
+ break;
+
+ if (i > 50)
+ break;
+
+ i++;
+ msleep(50);
+ }
+ }
+
+ dev_dbg(dev, "erase flash done.\n");
+}
+
+static int lt9611c_write_data(struct lt9611c *lt9611c, size_t addr)
+{
+ struct device *dev = lt9611c->dev;
+ int ret;
+ unsigned int page = 0, num = 0, i = 0;
+ size_t size, index;
+ const u8 *data;
+ u8 value;
+
+ data = lt9611c->fw->data;
+ size = lt9611c->fw->size;
+ page = (size + LT_PAGE_SIZE - 1) / LT_PAGE_SIZE;
+ if (page * LT_PAGE_SIZE > FW_SIZE) {
+ dev_err(dev, "firmware size out of range\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "%u pages, total size %zu byte\n", page, size);
+
+ for (num = 0; num < page; num++) {
+ lt9611c_data_to_sram(lt9611c);
+
+ for (i = 0; i < LT_PAGE_SIZE; i++) {
+ index = num * LT_PAGE_SIZE + i;
+ value = (index < size) ? data[index] : 0xff;
+
+ ret = regmap_write(lt9611c->regmap, 0xe059, value);
+ if (ret < 0) {
+ dev_err(dev, "write error at page %u, index %u\n", num, i);
+ return ret;
+ }
+ }
+
+ lt9611c_wren(lt9611c);
+ lt9611c_sram_to_flash(lt9611c, addr);
+
+ addr += LT_PAGE_SIZE;
+ }
+
+ lt9611c_wrdi(lt9611c);
+
+ return 0;
+}
+
+static int lt9611c_write_crc(struct lt9611c *lt9611c, size_t addr)
+{
+ struct device *dev = lt9611c->dev;
+ int ret;
+ u8 crc;
+
+ crc = lt9611c->fw_crc;
+ lt9611c_crc_to_sram(lt9611c);
+ ret = regmap_write(lt9611c->regmap, 0xe059, crc);
+ if (ret < 0) {
+ dev_err(dev, "failed to write crc\n");
+ return ret;
+ }
+
+ lt9611c_wren(lt9611c);
+ lt9611c_sram_to_flash(lt9611c, addr);
+ lt9611c_wrdi(lt9611c);
+
+ dev_dbg(dev, "crc 0x%02x written to flash at addr 0x%zx\n", crc, addr);
+
+ return 0;
+}
+
+static int lt9611c_firmware_upgrade(struct lt9611c *lt9611c)
+{
+ struct device *dev = lt9611c->dev;
+ int ret;
+
+ ret = lt9611c_prepare_firmware_data(lt9611c);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "starting firmware upgrade, size: %zu bytes\n", lt9611c->fw->size);
+
+ lt9611c_config_parameters(lt9611c);
+ lt9611c_block_erase(lt9611c);
+
+ ret = lt9611c_write_data(lt9611c, 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to write firmware data\n");
+ return ret;
+ }
+
+ release_firmware(lt9611c->fw);
+ lt9611c->fw = NULL;
+
+ ret = lt9611c_write_crc(lt9611c, FW_SIZE - 1);
+ if (ret < 0) {
+ dev_err(dev, "failed to write firmware crc\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lt9611c_upgrade_result(struct lt9611c *lt9611c)
+{
+ struct device *dev = lt9611c->dev;
+ unsigned int crc_result;
+
+ regmap_write(lt9611c->regmap, 0xe0ee, 0x01);
+ regmap_read(lt9611c->regmap, 0xe021, &crc_result);
+
+ if (crc_result != lt9611c->fw_crc) {
+ dev_err(dev, "lt9611c fw upgrade failed, expected crc=0x%02x, read crc=0x%02x\n",
+ lt9611c->fw_crc, crc_result);
+ return -1;
+ }
+
+ dev_dbg(dev, "lt9611c firmware upgrade success, crc=0x%02x\n", crc_result);
+ return 0;
+}
+
+static struct lt9611c *bridge_to_lt9611c(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct lt9611c, bridge);
+}
+
+/*read only*/
+static const struct lt9611c *bridge_to_lt9611c_const(const struct drm_bridge *bridge)
+{
+ return container_of(bridge, const struct lt9611c, bridge);
+}
+
+static void lt9611c_lock(struct lt9611c *lt9611c)
+{
+ mutex_lock(<9611c->ocm_lock);
+ regmap_write(lt9611c->regmap, 0xe0ee, 0x01);
+}
+
+static void lt9611c_unlock(struct lt9611c *lt9611c)
+{
+ regmap_write(lt9611c->regmap, 0xe0ee, 0x00);
+ mutex_unlock(<9611c->ocm_lock);
+}
+
+static irqreturn_t lt9611c_irq_thread_handler(int irq, void *dev_id)
+{
+ struct lt9611c *lt9611c = dev_id;
+ struct device *dev = lt9611c->dev;
+ int ret;
+ unsigned int irq_status;
+ u8 cmd[5] = {0x52, 0x48, 0x31, 0x3a, 0x00};
+ u8 data[5];
+
+ mutex_lock(<9611c->ocm_lock);
+
+ regmap_read(lt9611c->regmap, 0xe084, &irq_status);
+ if (!(irq_status & BIT(0))) {
+ mutex_unlock(<9611c->ocm_lock);
+ return IRQ_HANDLED;
+ }
+
+ ret = lt9611c_read_write_flow(lt9611c, cmd, 5, data, 5);
+ if (ret) {
+ dev_err(dev, "failed to read HPD status\n");
+ } else {
+ lt9611c->hdmi_connected = (data[4] == 0x02);
+ dev_dbg(dev, "HDMI %s\n", lt9611c->hdmi_connected ? "connected" : "disconnected");
+ }
+
+ schedule_work(<9611c->work);
+
+ /*clear interrupt*/
+ regmap_write(lt9611c->regmap, 0xe0df, irq_status & BIT(0));
+ //hardware need delay
+ usleep_range(10000, 12000);
+ regmap_write(lt9611c->regmap, 0xe0df, irq_status & (~BIT(0)));
+
+ mutex_unlock(<9611c->ocm_lock);
+
+ return IRQ_HANDLED;
+}
+
+static void lt9611c_hpd_work(struct work_struct *work)
+{
+ struct lt9611c *lt9611c = container_of(work, struct lt9611c, work);
+ bool connected;
+
+ mutex_lock(<9611c->ocm_lock);
+ connected = lt9611c->hdmi_connected;
+ mutex_unlock(<9611c->ocm_lock);
+
+ drm_bridge_hpd_notify(<9611c->bridge,
+ connected ? connector_status_connected :
+ connector_status_disconnected);
+}
+
+static void lt9611c_reset(struct lt9611c *lt9611c)
+{
+ gpiod_set_value_cansleep(lt9611c->reset_gpio, 1);
+ msleep(20);
+
+ gpiod_set_value_cansleep(lt9611c->reset_gpio, 0);
+ msleep(20);
+
+ gpiod_set_value_cansleep(lt9611c->reset_gpio, 1);
+ msleep(400);
+
+ dev_dbg(lt9611c->dev, "lt9611c reset");
+}
+
+static int lt9611c_regulator_init(struct lt9611c *lt9611c)
+{
+ struct device *dev = lt9611c->dev;
+ int ret;
+
+ lt9611c->supplies[0].supply = "vcc";
+ lt9611c->supplies[1].supply = "vdd";
+
+ ret = devm_regulator_bulk_get(dev, 2, lt9611c->supplies);
+
+ return ret;
+}
+
+static int lt9611c_regulator_enable(struct lt9611c *lt9611c)
+{
+ int ret;
+
+ ret = regulator_enable(lt9611c->supplies[0].consumer);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(5000, 10000);
+
+ ret = regulator_enable(lt9611c->supplies[1].consumer);
+ if (ret < 0) {
+ regulator_disable(lt9611c->supplies[0].consumer);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lt9611c_regulator_disable(struct lt9611c *lt9611c)
+{
+ int ret;
+
+ ret = regulator_disable(lt9611c->supplies[0].consumer);
+ if (ret < 0)
+ return ret;
+
+ ret = regulator_disable(lt9611c->supplies[1].consumer);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct mipi_dsi_device *lt9611c_attach_dsi(struct lt9611c *lt9611c,
+ struct device_node *dsi_node)
+{
+ const struct mipi_dsi_device_info info = { "lt9611c", 0, NULL };
+ struct mipi_dsi_device *dsi;
+ struct mipi_dsi_host *host;
+ struct device *dev = lt9611c->dev;
+ int ret;
+
+ host = of_find_mipi_dsi_host_by_node(dsi_node);
+ if (!host) {
+ dev_err(dev, "failed to find dsi host\n");
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
+ if (IS_ERR(dsi)) {
+ dev_err(dev, "failed to create dsi device\n");
+ return dsi;
+ }
+
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+ MIPI_DSI_MODE_VIDEO_HSE;
+
+ ret = devm_mipi_dsi_attach(dev, dsi);
+ if (ret < 0) {
+ dev_err(dev, "failed to attach dsi to host\n");
+ return ERR_PTR(ret);
+ }
+
+ return dsi;
+}
+
+static int lt9611c_bridge_attach(struct drm_bridge *bridge,
+ struct drm_encoder *encoder,
+ enum drm_bridge_attach_flags flags)
+{
+ struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
+
+ return drm_bridge_attach(encoder, lt9611c->next_bridge, bridge, flags);
+}
+
+static enum drm_mode_status
+lt9611c_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ unsigned long long tmds_rate)
+{
+ const struct lt9611c *lt9611c = bridge_to_lt9611c_const(bridge);
+
+ if (lt9611c->chip_type == 2) {
+ /*lt9611uxd use*/
+ if (tmds_rate > 600000000)
+ return MODE_CLOCK_HIGH;
+
+ } else {
+ if (tmds_rate > 340000000)
+ return MODE_CLOCK_HIGH;
+ }
+
+ if (tmds_rate < 25000000)
+ return MODE_CLOCK_LOW;
+
+ return MODE_OK;
+}
+
+static void lt9611c_video_setup(struct lt9611c *lt9611c,
+ const struct drm_display_mode *mode)
+{
+ struct device *dev = lt9611c->dev;
+ int ret;
+ u32 h_total, hactive, hsync_len, hfront_porch, hback_porch;
+ u32 v_total, vactive, vsync_len, vfront_porch, vback_porch;
+ u8 video_timing_set_cmd[26] = {0x57, 0x4d, 0x33, 0x3a};
+ u8 return_timing_set_param[3];
+ u8 framerate;
+ u8 vic = 0x00;
+
+ mutex_lock(<9611c->ocm_lock);
+
+ h_total = mode->htotal;
+ hactive = mode->hdisplay;
+ hsync_len = mode->hsync_end - mode->hsync_start;
+ hfront_porch = mode->hsync_start - mode->hdisplay;
+ hback_porch = mode->htotal - mode->hsync_end;
+
+ v_total = mode->vtotal;
+ vactive = mode->vdisplay;
+ vsync_len = mode->vsync_end - mode->vsync_start;
+ vfront_porch = mode->vsync_start - mode->vdisplay;
+ vback_porch = mode->vtotal - mode->vsync_end;
+ framerate = drm_mode_vrefresh(mode);
+ vic = drm_match_cea_mode(mode);
+
+ dev_dbg(dev, "hactive=%d, vactive=%d\n", hactive, vactive);
+ dev_dbg(dev, "framerate=%d\n", framerate);
+ dev_dbg(dev, "vic = 0x%02x\n", vic);
+
+ video_timing_set_cmd[4] = (h_total >> 8) & 0xff;
+ video_timing_set_cmd[5] = h_total & 0xff;
+ video_timing_set_cmd[6] = (hactive >> 8) & 0xff;
+ video_timing_set_cmd[7] = hactive & 0xff;
+ video_timing_set_cmd[8] = (hfront_porch >> 8) & 0xff;
+ video_timing_set_cmd[9] = hfront_porch & 0xff;
+ video_timing_set_cmd[10] = (hsync_len >> 8) & 0xff;
+ video_timing_set_cmd[11] = hsync_len & 0xff;
+ video_timing_set_cmd[12] = (hback_porch >> 8) & 0xff;
+ video_timing_set_cmd[13] = hback_porch & 0xff;
+ video_timing_set_cmd[14] = (v_total >> 8) & 0xff;
+ video_timing_set_cmd[15] = v_total & 0xff;
+ video_timing_set_cmd[16] = (vactive >> 8) & 0xff;
+ video_timing_set_cmd[17] = vactive & 0xFF;
+ video_timing_set_cmd[18] = (vfront_porch >> 8) & 0xff;
+ video_timing_set_cmd[19] = vfront_porch & 0xff;
+ video_timing_set_cmd[20] = (vsync_len >> 8) & 0xff;
+ video_timing_set_cmd[21] = vsync_len & 0xff;
+ video_timing_set_cmd[22] = (vback_porch >> 8) & 0xff;
+ video_timing_set_cmd[23] = vback_porch & 0xff;
+ video_timing_set_cmd[24] = framerate;
+ video_timing_set_cmd[25] = vic;
+
+ ret = lt9611c_read_write_flow(lt9611c,
+ video_timing_set_cmd, 26,
+ return_timing_set_param, 3);
+ if (ret)
+ dev_err(dev, "video set failed\n");
+ mutex_unlock(<9611c->ocm_lock);
+}
+
+static void lt9611c_bridge_atomic_enable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state)
+{
+ struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
+ struct drm_connector *connector;
+ struct drm_connector_state *conn_state;
+ struct drm_crtc_state *crtc_state;
+ struct drm_display_mode *mode;
+
+ 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;
+
+ lt9611c_video_setup(lt9611c, mode);
+}
+
+static enum drm_connector_status
+lt9611c_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
+{
+ struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
+ struct device *dev = lt9611c->dev;
+ int ret;
+ bool connected = false;
+ u8 cmd[5] = {0x52, 0x48, 0x31, 0x3a, 0x00};
+ u8 data[5];
+
+ mutex_lock(<9611c->ocm_lock);
+
+ ret = lt9611c_read_write_flow(lt9611c, cmd, 5, data, 5);
+ if (ret)
+ dev_err(dev, "failed to read HPD status (err=%d)\n", ret);
+ else
+ connected = (data[4] == 0x02);
+
+ lt9611c->hdmi_connected = connected;
+
+ mutex_unlock(<9611c->ocm_lock);
+
+ return connected ? connector_status_connected :
+ connector_status_disconnected;
+}
+
+static int lt9611c_get_edid_block(void *data, u8 *buf,
+ unsigned int block, size_t len)
+{
+ struct lt9611c *lt9611c = data;
+ struct device *dev = lt9611c->dev;
+ u8 cmd[5] = {0x52, 0x48, 0x33, 0x3a, 0x00};
+ u8 packet[37];
+ int ret, i, offset = 0;
+
+ if (len != 128)
+ return -EINVAL;
+ mutex_lock(<9611c->ocm_lock);
+
+ for (i = 0; i < 4; i++) {
+ cmd[4] = block * 4 + i;
+ ret = lt9611c_read_write_flow(lt9611c, cmd, sizeof(cmd),
+ packet, sizeof(packet));
+ if (ret) {
+ dev_err(dev, "Failed to read EDID block %u packet %d\n",
+ block, i);
+ mutex_unlock(<9611c->ocm_lock);
+ return ret;
+ }
+
+ memcpy(buf + offset, &packet[5], 32);
+ offset += 32;
+ }
+
+ mutex_unlock(<9611c->ocm_lock);
+
+ return 0;
+}
+
+static const struct drm_edid *lt9611c_bridge_edid_read(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
+
+ return drm_edid_read_custom(connector, lt9611c_get_edid_block, lt9611c);
+}
+
+static void lt9611c_bridge_hpd_notify(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ enum drm_connector_status status)
+{
+ const struct drm_edid *drm_edid;
+
+ if (status == connector_status_disconnected) {
+ drm_connector_hdmi_audio_plugged_notify(connector, false);
+ drm_edid_connector_update(connector, NULL);
+ return;
+ }
+
+ drm_edid = lt9611c_bridge_edid_read(bridge, connector);
+ drm_edid_connector_update(connector, drm_edid);
+ drm_edid_free(drm_edid);
+
+ if (status == connector_status_connected)
+ drm_connector_hdmi_audio_plugged_notify(connector, true);
+}
+
+static int lt9611c_hdmi_write_audio_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
+ u8 audio_infoframe_cmd[16] = {0x57, 0x48, 0x35, 0x3a, 0x02};
+ u8 data[5];
+ int i, ret;
+
+ for (i = 0; i < len; i++)
+ audio_infoframe_cmd[i + 5] = buffer[i];
+
+ mutex_lock(<9611c->ocm_lock);
+
+ ret = lt9611c_read_write_flow(lt9611c, audio_infoframe_cmd, sizeof(audio_infoframe_cmd),
+ data, sizeof(data));
+ mutex_unlock(<9611c->ocm_lock);
+
+ if (ret < 0) {
+ dev_err(lt9611c->dev, "write audio infoframe failed!\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lt9611c_hdmi_clear_audio_infoframe(struct drm_bridge *bridge)
+{
+ return 0;
+}
+
+static int lt9611c_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
+ u8 avi_infoframe_cmd[16] = {0x57, 0x48, 0x35, 0x3a, 0x01};
+ u8 data[5];
+ int i, ret;
+
+ for (i = 0; i < len; i++)
+ avi_infoframe_cmd[i + 5] = buffer[i];
+
+ mutex_lock(<9611c->ocm_lock);
+
+ ret = lt9611c_read_write_flow(lt9611c, avi_infoframe_cmd, sizeof(avi_infoframe_cmd),
+ data, sizeof(data));
+ mutex_unlock(<9611c->ocm_lock);
+
+ if (ret < 0) {
+ dev_err(lt9611c->dev, "write avi infoframe failed!\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lt9611c_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
+{
+ return 0;
+}
+
+static int lt9611c_hdmi_write_spd_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
+ u8 spd_infoframe_cmd[16] = {0x57, 0x48, 0x35, 0x3a, 0x04};
+ u8 data[5];
+ int i, ret;
+
+ for (i = 0; i < len; i++)
+ spd_infoframe_cmd[i + 5] = buffer[i];
+
+ mutex_lock(<9611c->ocm_lock);
+
+ ret = lt9611c_read_write_flow(lt9611c, spd_infoframe_cmd, sizeof(spd_infoframe_cmd),
+ data, sizeof(data));
+ mutex_unlock(<9611c->ocm_lock);
+
+ if (ret < 0) {
+ dev_err(lt9611c->dev, "write spd infoframe failed!\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lt9611c_hdmi_clear_spd_infoframe(struct drm_bridge *bridge)
+{
+ return 0;
+}
+
+static int lt9611c_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
+ u8 spd_infoframe_cmd[16] = {0x57, 0x48, 0x35, 0x3a, 0x05};
+ u8 data[5];
+ int i, ret;
+
+ for (i = 0; i < len; i++)
+ spd_infoframe_cmd[i + 5] = buffer[i];
+
+ mutex_lock(<9611c->ocm_lock);
+
+ ret = lt9611c_read_write_flow(lt9611c, spd_infoframe_cmd, sizeof(spd_infoframe_cmd),
+ data, sizeof(data));
+ mutex_unlock(<9611c->ocm_lock);
+ if (ret < 0) {
+ dev_err(lt9611c->dev, "write hdmi infoframe failed!\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lt9611c_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
+{
+ return 0;
+}
+
+static int lt9611c_hdmi_audio_prepare(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ struct hdmi_codec_daifmt *fmt,
+ struct hdmi_codec_params *hparms)
+{
+ struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
+ u8 audio_cmd[6] = {0x57, 0x48, 0x36, 0x3a};
+ u8 data[5];
+ int ret;
+
+ /* Validate sample rate and width (LT9611C auto-detects but we still check) */
+ switch (hparms->sample_rate) {
+ case 32000:
+ case 44100:
+ case 48000:
+ case 88200:
+ case 96000:
+ case 176400:
+ case 192000:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (hparms->sample_width) {
+ case 16:
+ case 18:
+ case 20:
+ case 24:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt->fmt) {
+ case HDMI_I2S:
+ audio_cmd[4] = 0x01;
+ break;
+ case HDMI_SPDIF:
+ audio_cmd[4] = 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ audio_cmd[5] = hparms->channels;
+
+ mutex_lock(<9611c->ocm_lock);
+ ret = lt9611c_read_write_flow(lt9611c, audio_cmd, sizeof(audio_cmd),
+ data, sizeof(data));
+ mutex_unlock(<9611c->ocm_lock);
+
+ if (ret < 0) {
+ dev_err(lt9611c->dev, "set audio info failed!\n");
+ return ret;
+ }
+
+ return drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector,
+ &hparms->cea);
+}
+
+static void lt9611c_hdmi_audio_shutdown(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ drm_atomic_helper_connector_hdmi_clear_audio_infoframe(connector);
+}
+
+static int lt9611c_hdmi_audio_startup(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ return 0;
+}
+
+static const struct drm_bridge_funcs lt9611c_bridge_funcs = {
+ .attach = lt9611c_bridge_attach,
+ .detect = lt9611c_bridge_detect,
+ .edid_read = lt9611c_bridge_edid_read,
+ .hpd_notify = lt9611c_bridge_hpd_notify,
+ .atomic_enable = lt9611c_bridge_atomic_enable,
+
+ .hdmi_tmds_char_rate_valid = lt9611c_hdmi_tmds_char_rate_valid,
+ .hdmi_write_audio_infoframe = lt9611c_hdmi_write_audio_infoframe,
+ .hdmi_clear_audio_infoframe = lt9611c_hdmi_clear_audio_infoframe,
+ .hdmi_write_avi_infoframe = lt9611c_hdmi_write_avi_infoframe,
+ .hdmi_clear_avi_infoframe = lt9611c_hdmi_clear_avi_infoframe,
+ .hdmi_write_spd_infoframe = lt9611c_hdmi_write_spd_infoframe,
+ .hdmi_clear_spd_infoframe = lt9611c_hdmi_clear_spd_infoframe,
+ .hdmi_write_hdmi_infoframe = lt9611c_hdmi_write_hdmi_infoframe,
+ .hdmi_clear_hdmi_infoframe = lt9611c_hdmi_clear_hdmi_infoframe,
+ .hdmi_audio_startup = lt9611c_hdmi_audio_startup,
+ .hdmi_audio_prepare = lt9611c_hdmi_audio_prepare,
+ .hdmi_audio_shutdown = lt9611c_hdmi_audio_shutdown,
+};
+
+static int lt9611c_parse_dt(struct device *dev,
+ struct lt9611c *lt9611c)
+{
+ lt9611c->dsi0_node = of_graph_get_remote_node(dev->of_node, 0, -1);
+ if (!lt9611c->dsi0_node) {
+ dev_err(dev, "failed to get remote node for primary dsi\n");
+ return -ENODEV;
+ }
+
+ lt9611c->dsi1_node = of_graph_get_remote_node(dev->of_node, 1, -1);
+
+ return drm_of_find_panel_or_bridge(dev->of_node, 2, -1, NULL, <9611c->next_bridge);
+}
+
+static int lt9611c_gpio_init(struct lt9611c *lt9611c)
+{
+ struct device *dev = lt9611c->dev;
+
+ lt9611c->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(lt9611c->reset_gpio)) {
+ dev_err(dev, "failed to acquire reset gpio\n");
+ return PTR_ERR(lt9611c->reset_gpio);
+ }
+
+ return 0;
+}
+
+static int lt9611c_read_version(struct lt9611c *lt9611c)
+{
+ u8 buf[2];
+ int ret;
+
+ ret = regmap_write(lt9611c->regmap, 0xe0ee, 0x01);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(lt9611c->regmap, 0xe080, buf, 2);
+ if (ret)
+ return ret;
+
+ return (buf[0] << 8) | buf[1];
+}
+
+static int lt9611c_read_chipid(struct lt9611c *lt9611c)
+{
+ struct device *dev = lt9611c->dev;
+ u8 chipid[2];
+ int ret;
+
+ ret = regmap_write(lt9611c->regmap, 0xe0ee, 0x01);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(lt9611c->regmap, 0xe100, chipid, 2);
+ if (ret)
+ return ret;
+
+ if (chipid[0] != 0x23 || chipid[1] != 0x06) {
+ dev_err(dev, "ChipID: 0x%02x 0x%02x\n", chipid[0], chipid[1]);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static ssize_t lt9611c_firmware_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lt9611c *lt9611c = dev_get_drvdata(dev);
+ int ret;
+
+ lt9611c_lock(lt9611c);
+
+ ret = lt9611c_firmware_upgrade(lt9611c);
+ if (ret < 0) {
+ dev_err(dev, "upgrade failure\n");
+ goto out;
+ }
+ lt9611c_reset(lt9611c);
+ ret = lt9611c_upgrade_result(lt9611c);
+ if (ret < 0)
+ goto out;
+
+out:
+ lt9611c_unlock(lt9611c);
+ lt9611c_reset(lt9611c);
+ if (lt9611c->fw) {
+ release_firmware(lt9611c->fw);
+ lt9611c->fw = NULL;
+ }
+
+ return ret < 0 ? ret : len;
+}
+
+static ssize_t lt9611c_firmware_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct lt9611c *lt9611c = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "0x%04x\n", lt9611c->fw_version);
+}
+
+static DEVICE_ATTR_RW(lt9611c_firmware);
+
+static struct attribute *lt9611c_attrs[] = {
+ &dev_attr_lt9611c_firmware.attr,
+ NULL,
+};
+
+static const struct attribute_group lt9611c_attr_group = {
+ .attrs = lt9611c_attrs,
+};
+
+static const struct attribute_group *lt9611c_attr_groups[] = {
+ <9611c_attr_group,
+ NULL,
+};
+
+static int lt9611c_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ struct lt9611c *lt9611c;
+ struct device *dev = &client->dev;
+ bool fw_updated = false;
+ int ret;
+
+ crc8_populate_msb(lt9611c_crc8_table, LT9611C_CRC_POLYNOMIAL);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(dev, "device doesn't support I2C\n");
+ return -ENODEV;
+ }
+
+ lt9611c = devm_drm_bridge_alloc(dev, struct lt9611c, bridge, <9611c_bridge_funcs);
+ if (IS_ERR(lt9611c))
+ return PTR_ERR(lt9611c);
+
+ lt9611c->dev = dev;
+ lt9611c->client = client;
+ lt9611c->chip_type = id->driver_data;
+ mutex_init(<9611c->ocm_lock);
+
+ lt9611c->regmap = devm_regmap_init_i2c(client, <9611c_regmap_config);
+ if (IS_ERR(lt9611c->regmap)) {
+ dev_err(dev, "regmap i2c init failed\n");
+ return PTR_ERR(lt9611c->regmap);
+ }
+
+ ret = lt9611c_parse_dt(dev, lt9611c);
+ if (ret) {
+ dev_err(dev, "failed to parse device tree\n");
+ return ret;
+ }
+
+ ret = lt9611c_gpio_init(lt9611c);
+ if (ret < 0)
+ goto err_of_put;
+
+ ret = lt9611c_regulator_init(lt9611c);
+ if (ret < 0)
+ goto err_of_put;
+
+ ret = lt9611c_regulator_enable(lt9611c);
+ if (ret)
+ goto err_of_put;
+
+ lt9611c_reset(lt9611c);
+
+ lt9611c_lock(lt9611c);
+
+ ret = lt9611c_read_chipid(lt9611c);
+ if (ret < 0) {
+ dev_err(dev, "failed to read chip id.\n");
+ lt9611c_unlock(lt9611c);
+ goto err_disable_regulators;
+ }
+
+retry:
+ ret = lt9611c_read_version(lt9611c);
+ if (ret < 0) {
+ dev_err(dev, "failed to read fw version\n");
+ lt9611c_unlock(lt9611c);
+ goto err_disable_regulators;
+
+ } else if (ret == 0) {
+ if (!fw_updated) {
+ fw_updated = true;
+ ret = lt9611c_firmware_upgrade(lt9611c);
+ if (ret < 0) {
+ lt9611c_unlock(lt9611c);
+ goto err_disable_regulators;
+ }
+
+ lt9611c_reset(lt9611c);
+
+ ret = lt9611c_upgrade_result(lt9611c);
+ if (ret < 0) {
+ lt9611c_unlock(lt9611c);
+ goto err_disable_regulators;
+ }
+
+ goto retry;
+
+ } else {
+ dev_err(dev, "fw version 0x%04x, update failed\n", ret);
+ ret = -EOPNOTSUPP;
+ lt9611c_unlock(lt9611c);
+ goto err_disable_regulators;
+ }
+ }
+
+ lt9611c_unlock(lt9611c);
+ lt9611c->fw_version = ret;
+
+ dev_dbg(dev, "current version:0x%04x", lt9611c->fw_version);
+
+ INIT_WORK(<9611c->work, lt9611c_hpd_work);
+
+ ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ lt9611c_irq_thread_handler,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT |
+ IRQF_NO_AUTOEN,
+ "lt9611c", lt9611c);
+ if (ret) {
+ dev_err(dev, "failed to request irq\n");
+ goto err_disable_regulators;
+ }
+
+ lt9611c->bridge.of_node = client->dev.of_node;
+ lt9611c->bridge.ops = DRM_BRIDGE_OP_DETECT |
+ DRM_BRIDGE_OP_EDID |
+ DRM_BRIDGE_OP_HPD |
+ DRM_BRIDGE_OP_HDMI_AUDIO;
+ lt9611c->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+
+ lt9611c->bridge.hdmi_audio_dev = dev;
+ lt9611c->bridge.hdmi_audio_max_i2s_playback_channels = 8;
+ lt9611c->bridge.hdmi_audio_dai_port = 2;
+
+ drm_bridge_add(<9611c->bridge);
+
+ /* Attach primary DSI */
+ lt9611c->dsi0 = lt9611c_attach_dsi(lt9611c, lt9611c->dsi0_node);
+ if (IS_ERR(lt9611c->dsi0)) {
+ ret = PTR_ERR(lt9611c->dsi0);
+ goto err_remove_bridge;
+ }
+
+ /* Attach secondary DSI, if specified */
+ if (lt9611c->dsi1_node) {
+ lt9611c->dsi1 = lt9611c_attach_dsi(lt9611c, lt9611c->dsi1_node);
+ if (IS_ERR(lt9611c->dsi1)) {
+ ret = PTR_ERR(lt9611c->dsi1);
+ goto err_remove_bridge;
+ }
+ }
+
+ lt9611c->hdmi_connected = false;
+ i2c_set_clientdata(client, lt9611c);
+ enable_irq(client->irq);
+ lt9611c_reset(lt9611c);
+
+ return 0;
+
+err_remove_bridge:
+ free_irq(client->irq, lt9611c);
+ cancel_work_sync(<9611c->work);
+ drm_bridge_remove(<9611c->bridge);
+
+err_disable_regulators:
+ regulator_bulk_disable(ARRAY_SIZE(lt9611c->supplies), lt9611c->supplies);
+
+err_of_put:
+ of_node_put(lt9611c->dsi1_node);
+ of_node_put(lt9611c->dsi0_node);
+ if (lt9611c->fw) {
+ release_firmware(lt9611c->fw);
+ lt9611c->fw = NULL;
+ }
+
+ return ret;
+}
+
+static void lt9611c_remove(struct i2c_client *client)
+{
+ struct lt9611c *lt9611c = i2c_get_clientdata(client);
+
+ free_irq(client->irq, lt9611c);
+ cancel_work_sync(<9611c->work);
+ drm_bridge_remove(<9611c->bridge);
+ mutex_destroy(<9611c->ocm_lock);
+ regulator_bulk_disable(ARRAY_SIZE(lt9611c->supplies), lt9611c->supplies);
+ of_node_put(lt9611c->dsi1_node);
+ of_node_put(lt9611c->dsi0_node);
+}
+
+static int lt9611c_bridge_suspend(struct device *dev)
+{
+ struct lt9611c *lt9611c = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(lt9611c->dev, "suspend\n");
+ disable_irq(lt9611c->client->irq);
+ ret = lt9611c_regulator_disable(lt9611c);
+ gpiod_set_value_cansleep(lt9611c->reset_gpio, 0);
+
+ return ret;
+}
+
+static int lt9611c_bridge_resume(struct device *dev)
+{
+ struct lt9611c *lt9611c = dev_get_drvdata(dev);
+ int ret;
+
+ ret = lt9611c_regulator_enable(lt9611c);
+ enable_irq(lt9611c->client->irq);
+ lt9611c_reset(lt9611c);
+ dev_dbg(lt9611c->dev, "resume\n");
+
+ return ret;
+}
+
+static const struct dev_pm_ops lt9611c_bridge_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(lt9611c_bridge_suspend,
+ lt9611c_bridge_resume)
+};
+
+static struct i2c_device_id lt9611c_id[] = {
+ /* chip_type */
+ { "lontium,lt9611c", 0 },
+ { "lontium,lt9611ex", 1 },
+ { "lontium,lt9611uxd", 2 },
+ { /* sentinel */ }
+};
+
+static const struct of_device_id lt9611c_match_table[] = {
+ { .compatible = "lontium,lt9611c" },
+ { .compatible = "lontium,lt9611ex" },
+ { .compatible = "lontium,lt9611uxd" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lt9611c_match_table);
+
+static struct i2c_driver lt9611c_driver = {
+ .driver = {
+ .name = "lt9611c",
+ .of_match_table = lt9611c_match_table,
+ .pm = <9611c_bridge_pm_ops,
+ .dev_groups = lt9611c_attr_groups,
+ },
+ .probe = lt9611c_probe,
+ .remove = lt9611c_remove,
+ .id_table = lt9611c_id,
+};
+module_i2c_driver(lt9611c_driver);
+
+MODULE_AUTHOR("SunYun Yang <syyang@lontium.com>");
+MODULE_DESCRIPTION("Lontium lt9611c mipi-dsi to hdmi driver");
+MODULE_LICENSE("GPL v2");
+
--
2.34.1
^ permalink raw reply related
* [PATCH v3 0/2] Add LT9611C(EX/UXD) DRM bridge driver and device tree
From: syyang @ 2026-04-20 6:16 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, andrzej.hajda, neil.armstrong,
dmitry.baryshkov, maarten.lankhorst, rfoss, mripard
Cc: Laurent.pinchart, tzimmermann, jonas, jernej.skrabec, devicetree,
dri-devel, linux-kernel, yangsunyun1993, xmzhu, Sunyun Yang
From: Sunyun Yang <syyang@lontium.com>
This series adds support for the Lontium LT9611C(EX/UXD) MIPI DSI to HDMI
chip:
-dt-bindings: bridge: This patch adds new content to the lontium,lt9611.yaml
binding file.
-drm/bridge: This patch add new DRM bridge driver for LT9611C(EX/UXD) chip.
Signed-off-by: Sunyun Yang<syyang@lontium.com>
---
Changes in v3:
-dt-binding:
1. lt9611c(ex/uxd) content merged into lontium,lt9611.yaml
-drm/bridge:
1. Drop the licence text, only use SPDX header
2. Sort the headers
3. Use library functions for crc8
4. Drop i2c_read_byte and i2c_write_byte
5. Lowercase all hex values
6. Use paged writes as implemented for LT9611C(EX/UXD)
7. Drop dev_info, use dev_dbg
8. Modify lt9611c_get_edid_block, don't store EDID in the long-term structures
9. Use HDMI audio helpers.
10.Remove unnecessary flags,Implement proper cleanup path, unwinding resources
one by one.
11.Replace devm_kzalloc with devm_drm_bridge_alloc.
12.Remove extra kthread.
-Link to v1: https://lore.kernel.org/lkml/20250903123825.1721443-1-syyang@lontium.com/
Changes in v2:
1. Forget modify code, operation error, Please disregard this submit.
Changes in v1:
-dt-binding:
1. Submit the first version of the code.
-drm/bridge:
1. Submit the first version of the code.
---
Sunyun Yang (2):
dt-bindings: bridge: This patch adds new content to the
lontium,lt9611.yaml binding file
drm/bridge: This patch add new DRM bridge driver for LT9611C chip
.../display/bridge/lontium,lt9611.yaml | 8 +-
drivers/gpu/drm/bridge/Kconfig | 18 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/lontium-lt9611c.c | 1365 +++++++++++++++++
4 files changed, 1390 insertions(+), 2 deletions(-)
create mode 100755 drivers/gpu/drm/bridge/lontium-lt9611c.c
--
2.34.1
^ permalink raw reply
* [PATCH] dt-bindings: timer: Remove sifive,fine-ctr-bits property
From: Nick Hu @ 2026-04-20 6:18 UTC (permalink / raw)
To: Daniel Lezcano, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Paul Walmsley, Samuel Holland, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Anup Patel
Cc: Conor Dooley, linux-kernel, devicetree, linux-riscv, Nick Hu
The counter width can be inferred from the compatible string, making the
explicit "sifive,fine-ctr-bits" property redundant. Remove the property
to simplify the bindings.
Fixes: 0f920690a82c ("dt-bindings: timer: Add SiFive CLINT2")
Suggested-by: Conor Dooley <conor+dt@kernel.org>
Link: https://lore.kernel.org/linux-riscv/20260330-relative-hardened-5ce35fe1ef57@spud/
Signed-off-by: Nick Hu <nick.hu@sifive.com>
---
.../devicetree/bindings/timer/sifive,clint.yaml | 16 ----------------
1 file changed, 16 deletions(-)
diff --git a/Documentation/devicetree/bindings/timer/sifive,clint.yaml b/Documentation/devicetree/bindings/timer/sifive,clint.yaml
index 3c16b260db04..051edb1da0d7 100644
--- a/Documentation/devicetree/bindings/timer/sifive,clint.yaml
+++ b/Documentation/devicetree/bindings/timer/sifive,clint.yaml
@@ -72,22 +72,6 @@ properties:
minItems: 1
maxItems: 4095
- sifive,fine-ctr-bits:
- maximum: 15
- description: The width in bits of the fine counter.
-
-if:
- properties:
- compatible:
- contains:
- const: sifive,clint2
-then:
- required:
- - sifive,fine-ctr-bits
-else:
- properties:
- sifive,fine-ctr-bits: false
-
additionalProperties: false
required:
---
base-commit: c1f49dea2b8f335813d3b348fd39117fb8efb428
change-id: 20260409-clintv2-remove-fine-ctr-d5caeda27863
Best regards,
--
Nick Hu <nick.hu@sifive.com>
^ permalink raw reply related
* [PATCH v2 2/2] arm64: dts: amlogic: add support for X98Q
From: christian.koever-draxl @ 2026-04-20 6:18 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, neil.armstrong, khilman
Cc: jbrunet, martin.blumenstingl, funderscore, devicetree,
linux-kernel, linux-arm-kernel, linux-amlogic,
christian.koever-draxl
In-Reply-To: <20260420061854.5421-1-christian.koever-draxl@student.uibk.ac.at>
From: Christian Stefan Kövér-Draxl <christian.koever-draxl@student.uibk.ac.at>
Signed-off-by: Christian Stefan Kövér-Draxl <christian.koever-draxl@student.uibk.ac.at>
---
arch/arm64/boot/dts/amlogic/Makefile | 1 +
.../boot/dts/amlogic/meson-s4-s905w2-x98q.dts | 250 ++++++++++++++++++
2 files changed, 251 insertions(+)
create mode 100644 arch/arm64/boot/dts/amlogic/meson-s4-s905w2-x98q.dts
diff --git a/arch/arm64/boot/dts/amlogic/Makefile b/arch/arm64/boot/dts/amlogic/Makefile
index 15f9c817e502..c7752684dea6 100644
--- a/arch/arm64/boot/dts/amlogic/Makefile
+++ b/arch/arm64/boot/dts/amlogic/Makefile
@@ -85,6 +85,7 @@ dtb-$(CONFIG_ARCH_MESON) += meson-gxm-ugoos-am3.dtb
dtb-$(CONFIG_ARCH_MESON) += meson-gxm-vega-s96.dtb
dtb-$(CONFIG_ARCH_MESON) += meson-gxm-wetek-core2.dtb
dtb-$(CONFIG_ARCH_MESON) += meson-s4-s805x2-aq222.dtb
+dtb-$(CONFIG_ARCH_MESON) += meson-s4-s905w2-x98q.dtb
dtb-$(CONFIG_ARCH_MESON) += meson-s4-s905y4-khadas-vim1s.dtb
dtb-$(CONFIG_ARCH_MESON) += meson-sm1-a95xf3-air-gbit.dtb
dtb-$(CONFIG_ARCH_MESON) += meson-sm1-a95xf3-air.dtb
diff --git a/arch/arm64/boot/dts/amlogic/meson-s4-s905w2-x98q.dts b/arch/arm64/boot/dts/amlogic/meson-s4-s905w2-x98q.dts
new file mode 100644
index 000000000000..26c60a4c2a43
--- /dev/null
+++ b/arch/arm64/boot/dts/amlogic/meson-s4-s905w2-x98q.dts
@@ -0,0 +1,250 @@
+
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2026 Christian Stefan Köver-Draxl
+ * Based on meson-s4-s905y4-khadas-vim1s.dts:
+ * - Copyright (c) 2026 Khadas Technology Co., Ltd.
+ */
+
+/dts-v1/;
+
+#include "meson-s4.dtsi"
+
+/ {
+ model = "Shenzhen Amediatech Technology Co., Ltd X98Q";
+ compatible = "amediatech,x98q", "amlogic,s905w2", "amlogic,s4";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ aliases {
+ mmc0 = &emmc; /* eMMC */
+ mmc1 = &sd; /* SD card */
+ mmc2 = &sdio; /* SDIO */
+ serial0 = &uart_b;
+ };
+
+ memory@0 {
+ device_type = "memory";
+ reg = <0x0 0x0 0x0 0x40000000>;
+ };
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ /* 52 MiB reserved for ARM Trusted Firmware */
+ secmon_reserved: secmon@5000000 {
+ reg = <0x0 0x05000000 0x0 0x3400000>;
+ no-map;
+ };
+ };
+
+ emmc_pwrseq: emmc-pwrseq {
+ compatible = "mmc-pwrseq-emmc";
+ reset-gpios = <&gpio GPIOB_9 GPIO_ACTIVE_LOW>;
+ };
+
+ sdio_32k: sdio-32k {
+ compatible = "pwm-clock";
+ #clock-cells = <0>;
+ clock-frequency = <32768>;
+ pwms = <&pwm_ef 0 30518 0>; /* PWM_E at 32.768KHz */
+ };
+
+ sdio_pwrseq: sdio-pwrseq {
+ compatible = "mmc-pwrseq-simple";
+ reset-gpios = <&gpio GPIOX_6 GPIO_ACTIVE_LOW>;
+ clocks = <&sdio_32k>;
+ clock-names = "ext_clock";
+ };
+
+ main_5v: regulator-main-5v {
+ compatible = "regulator-fixed";
+ regulator-name = "5V";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
+
+ sd_3v3: regulator-sd-3v3 {
+ compatible = "regulator-fixed";
+ regulator-name = "SD_3V3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&gpio GPIOD_4 GPIO_ACTIVE_LOW>;
+ regulator-always-on;
+ };
+
+ vddio_sd: regulator-vddio-sd {
+ compatible = "regulator-gpio";
+ regulator-name = "VDDIO_SD";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ gpios = <&gpio GPIOD_9 GPIO_ACTIVE_HIGH>;
+ gpios-states = <1>;
+ states = <1800000 1 3300000 0>;
+ };
+
+ vddao_3v3: regulator-vddao-3v3 {
+ compatible = "regulator-fixed";
+ regulator-name = "VDDAO_3V3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ vin-supply = <&main_5v>;
+ regulator-always-on;
+ };
+
+ vddio_ao1v8: regulator-vddio-ao1v8 {
+ compatible = "regulator-fixed";
+ regulator-name = "VDDIO_AO1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ vin-supply = <&vddao_3v3>;
+ regulator-always-on;
+ };
+
+ /* SY8120B1ABC DC/DC Regulator. */
+ vddcpu: regulator-vddcpu {
+ compatible = "pwm-regulator";
+
+ regulator-name = "VDDCPU";
+ regulator-min-microvolt = <689000>;
+ regulator-max-microvolt = <1049000>;
+
+ vin-supply = <&main_5v>;
+
+ pwms = <&pwm_ij 1 1500 0>;
+ pwm-dutycycle-range = <100 0>;
+
+ regulator-boot-on;
+ regulator-always-on;
+ /* Voltage Duty-Cycle */
+ voltage-table = <1049000 0>,
+ <1039000 3>,
+ <1029000 6>,
+ <1019000 9>,
+ <1009000 12>,
+ <999000 14>,
+ <989000 17>,
+ <979000 20>,
+ <969000 23>,
+ <959000 26>,
+ <949000 29>,
+ <939000 31>,
+ <929000 34>,
+ <919000 37>,
+ <909000 40>,
+ <899000 43>,
+ <889000 45>,
+ <879000 48>,
+ <869000 51>,
+ <859000 54>,
+ <849000 56>,
+ <839000 59>,
+ <829000 62>,
+ <819000 65>,
+ <809000 68>,
+ <799000 70>,
+ <789000 73>,
+ <779000 76>,
+ <769000 79>,
+ <759000 81>,
+ <749000 84>,
+ <739000 87>,
+ <729000 89>,
+ <719000 92>,
+ <709000 95>,
+ <699000 98>,
+ <689000 100>;
+ };
+};
+
+&emmc {
+ status = "okay";
+ pinctrl-0 = <&emmc_pins>, <&emmc_ds_pins>;
+ pinctrl-1 = <&emmc_clk_gate_pins>;
+ pinctrl-names = "default", "clk-gate";
+
+ bus-width = <8>;
+ cap-mmc-highspeed;
+ mmc-ddr-1_8v;
+ mmc-hs200-1_8v;
+ max-frequency = <200000000>;
+ non-removable;
+ disable-wp;
+
+ mmc-pwrseq = <&emmc_pwrseq>;
+ vmmc-supply = <&vddao_3v3>;
+ vqmmc-supply = <&vddio_ao1v8>;
+};
+
+ðmac {
+ status = "okay";
+ phy-handle = <&internal_ephy>;
+ phy-mode = "rmii";
+};
+
+&ir {
+ status = "okay";
+ pinctrl-0 = <&remote_pins>;
+ pinctrl-names = "default";
+};
+
+&pwm_ef {
+ status = "okay";
+ pinctrl-0 = <&pwm_e_pins1>;
+ pinctrl-names = "default";
+};
+
+&pwm_ij {
+ status = "okay";
+};
+
+&sd {
+ status = "okay";
+ pinctrl-0 = <&sdcard_pins>;
+ pinctrl-1 = <&sdcard_clk_gate_pins>;
+ pinctrl-names = "default", "clk-gate";
+ bus-width = <4>;
+ cap-sd-highspeed;
+ max-frequency = <50000000>;
+ disable-wp;
+
+ cd-gpios = <&gpio GPIOC_6 GPIO_ACTIVE_LOW>;
+
+ vmmc-supply = <&vddao_3v3>;
+ vqmmc-supply = <&vddao_3v3>;
+};
+
+/*
+* Wireless SDIO Module (Amlogic W150S1)
+* Note: There is no driver for this at the moment.
+*/
+
+&sdio {
+ status = "okay";
+ pinctrl-0 = <&sdio_pins>;
+ pinctrl-1 = <&sdio_clk_gate_pins>;
+ pinctrl-names = "default", "clk-gate";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ bus-width = <4>;
+ cap-sd-highspeed;
+ sd-uhs-sdr50;
+ sd-uhs-sdr104;
+ max-frequency = <200000000>;
+ non-removable;
+ disable-wp;
+
+ no-sd;
+ no-mmc;
+ mmc-pwrseq = <&sdio_pwrseq>;
+ vmmc-supply = <&vddao_3v3>;
+ vqmmc-supply = <&vddio_ao1v8>;
+};
+
+&uart_b {
+ status = "okay";
+};
--
2.53.0
^ permalink raw reply related
* [PATCH v2 1/2] dt-bindings: arm: amlogic: add X98Q compatible
From: christian.koever-draxl @ 2026-04-20 6:18 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, neil.armstrong, khilman
Cc: jbrunet, martin.blumenstingl, funderscore, devicetree,
linux-kernel, linux-arm-kernel, linux-amlogic,
christian.koever-draxl
In-Reply-To: <20260420061854.5421-1-christian.koever-draxl@student.uibk.ac.at>
From: Christian Stefan Kövér-Draxl <christian.koever-draxl@student.uibk.ac.at>
Signed-off-by: Christian Stefan Kövér-Draxl <christian.koever-draxl@student.uibk.ac.at>
---
Documentation/devicetree/bindings/arm/amlogic.yaml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/amlogic.yaml b/Documentation/devicetree/bindings/arm/amlogic.yaml
index a885278bc4e2..82671d58d1da 100644
--- a/Documentation/devicetree/bindings/arm/amlogic.yaml
+++ b/Documentation/devicetree/bindings/arm/amlogic.yaml
@@ -254,6 +254,13 @@ properties:
- khadas,vim1s
- const: amlogic,s905y4
- const: amlogic,s4
+
+ - description: Boards with the Amlogic Meson S4 S905W2 SoC
+ items:
+ - enum:
+ - amediatech,x98q
+ - const: amlogic,s905w2
+ - const: amlogic,s4
- description: Boards with the Amlogic S6 S905X5 SoC
items:
--
2.53.0
^ permalink raw reply related
* [PATCH v2 0/2] Add support for Amediatech X98Q (Amlogic S905W2)
From: christian.koever-draxl @ 2026-04-20 6:18 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, neil.armstrong, khilman
Cc: jbrunet, martin.blumenstingl, funderscore, devicetree,
linux-kernel, linux-arm-kernel, linux-amlogic,
christian.koever-draxl
From: Christian Stefan Kövér-Draxl <christian.koever-draxl@student.uibk.ac.at>
Supported features:
- 1GB/2GB RAM (via U-Boot memory fixup)
- 10/100 Ethernet (Internal PHY)
- eMMC and SD card storage
- PWM-based CPU voltage regulation
- UART (Serial console)
Changes in v2:
- Split dt-bindings and dts changes into separate patches.
- Updated model string to match documented vendor prefix.
- Put vddio_sd states array in a single line.
- Added a clarifying comment for the unsupported Amlogic W150S1 Wi-Fi module.
Notes:
- The console uses uart_b at 921600 baud.
- Verified memory via /proc/device-tree; U-Boot patches the node to around 2GB.
- Tested on the 2GB RAM plus 16GB eMMC variant.
Christian Stefan Kövér-Draxl (2):
dt-bindings: arm: amlogic: add X98Q compatible
arm64: dts: amlogic: add support for X98Q
.../devicetree/bindings/arm/amlogic.yaml | 7 +
arch/arm64/boot/dts/amlogic/Makefile | 1 +
.../boot/dts/amlogic/meson-s4-s905w2-x98q.dts | 250 ++++++++++++++++++
3 files changed, 258 insertions(+)
create mode 100644 arch/arm64/boot/dts/amlogic/meson-s4-s905w2-x98q.dts
--
2.53.0
^ permalink raw reply
* [PATCH v3 1/2] dt-bindings: bridge: This patch adds new content to the lontium,lt9611.yaml binding file
From: syyang @ 2026-04-20 6:16 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, andrzej.hajda, neil.armstrong,
dmitry.baryshkov, maarten.lankhorst, rfoss, mripard
Cc: Laurent.pinchart, tzimmermann, jonas, jernej.skrabec, devicetree,
dri-devel, linux-kernel, yangsunyun1993, xmzhu, Sunyun Yang
In-Reply-To: <20260420061644.1251070-1-syyang@lontium.com>
From: Sunyun Yang <syyang@lontium.com>
Add lt9611c,lt9611ex,lt9611uxd to the lontium,lt9611.yaml file.
LT9611C(EX/UXD) is a high performance Single/Dual-Port MIPI to
HDMI 1.4/2.0 converter:
-Single/Dual-port MIPI DSI Receiver
1. Compliantwith D-PHY1.2&DSI-2 1.0
2. 1/2configurable ports
3. 1 clock lane and 1/2/3/4 configurable data lanes per port
4. 80Mbps~2.5Gbps per data lane
5. Support RGB666, loosely RGB666, RGB888, RGB565,16-bit YCbCr4:2:2
-HDMI 1.4/2.0 Transmitter
1.Data rate up to 6Gbps
2.Support HDCP1.4/2.3
3.Support CEC,HDR10
4.Support lane swap
-audio
1.sample rates of 32~192 KHz and sample sizes
of 16~24 bits
2.SPDIF interface supports PCM, Dolbydigital, DTS digital audio
at up to 192KHz frame rate
-Miscellaneous
1.CSC:RGB<->YUV444<->YUV422
Signed-off-by: Sunyun Yang <syyang@lontium.com>
---
.../bindings/display/bridge/lontium,lt9611.yaml | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/display/bridge/lontium,lt9611.yaml b/Documentation/devicetree/bindings/display/bridge/lontium,lt9611.yaml
index 429a06057ae8..a67e63a5c87e 100644
--- a/Documentation/devicetree/bindings/display/bridge/lontium,lt9611.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/lontium,lt9611.yaml
@@ -4,19 +4,23 @@
$id: http://devicetree.org/schemas/display/bridge/lontium,lt9611.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Lontium LT9611(UXC) 2 Port MIPI to HDMI Bridge
+title: Lontium LT9611(UXC/C/EX/UXD) 2 Port MIPI to HDMI Bridge
maintainers:
- Vinod Koul <vkoul@kernel.org>
description: |
- The LT9611 and LT9611UXC are bridge devices which convert DSI to HDMI
+ The LT9611、LT9611UXC、LT9611C、LT9611EX and LT9611UXD
+ are bridge devices which convert DSI to HDMI
properties:
compatible:
enum:
- lontium,lt9611
- lontium,lt9611uxc
+ - lontium,lt9611c
+ - lontium,lt9611ex
+ - lontium,lt9611uxd
reg:
maxItems: 1
--
2.34.1
^ permalink raw reply related
* [PATCH] dt-bindings: qcom: geni-se-qup: Add compatible for Nord SoC
From: Shawn Guo @ 2026-04-20 6:44 UTC (permalink / raw)
To: Bjorn Andersson
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Praveen Talari,
Konrad Dybcio, Dmitry Baryshkov, Bartosz Golaszewski,
Deepti Jaggi, linux-serial, devicetree, linux-arm-msm,
linux-kernel, Shawn Guo
From: Deepti Jaggi <deepti.jaggi@oss.qualcomm.com>
Add compatibles for GENI Serial Engine QUP Wrapper Controller on Nord SoC
with fallback on SA8255P compatibles.
Signed-off-by: Deepti Jaggi <deepti.jaggi@oss.qualcomm.com>
Signed-off-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
---
.../soc/qcom/qcom,sa8255p-geni-se-qup.yaml | 20 +++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,sa8255p-geni-se-qup.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,sa8255p-geni-se-qup.yaml
index 352af3426d34..d73f9edcbbdb 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,sa8255p-geni-se-qup.yaml
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,sa8255p-geni-se-qup.yaml
@@ -19,7 +19,12 @@ description:
properties:
compatible:
- const: qcom,sa8255p-geni-se-qup
+ oneOf:
+ - enum:
+ - qcom,sa8255p-geni-se-qup
+ - items:
+ - const: qcom,nord-auto-geni-se-qup
+ - const: qcom,sa8255p-geni-se-qup
reg:
description: QUP wrapper common register address and length.
@@ -67,9 +72,16 @@ patternProperties:
properties:
compatible:
- enum:
- - qcom,sa8255p-geni-uart
- - qcom,sa8255p-geni-debug-uart
+ oneOf:
+ - enum:
+ - qcom,sa8255p-geni-uart
+ - qcom,sa8255p-geni-debug-uart
+ - items:
+ - const: qcom,nord-auto-geni-uart
+ - const: qcom,sa8255p-geni-uart
+ - items:
+ - const: qcom,nord-auto-geni-debug-uart
+ - const: qcom,sa8255p-geni-debug-uart
required:
- compatible
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v2 1/3] MAINTAINERS: Move Peter De Schrijver to CREDITS
From: Geert Uytterhoeven @ 2026-04-20 6:50 UTC (permalink / raw)
To: Thierry Reding
Cc: Aaro Koskinen, linux-tegra, linux-arm-kernel, linux-pm,
linux-omap, linux-m68k, devicetree, linux-kernel, Paul Walmsley
In-Reply-To: <20260417131549.3154534-1-thierry.reding@kernel.org>
Hi Thierry,
On Fri, 17 Apr 2026 at 15:15, Thierry Reding <thierry.reding@kernel.org> wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> Peter sadly passed away a while back. Paul did a much better job at
> finding the right words to mourn this loss than I ever could, so I will
> leave this link here:
>
> https://lore.kernel.org/lkml/alpine.DEB.2.21.999.2407240345480.11116@utopia.booyaka.com/T/#u
>
> Co-developed-by: Paul Walmsley <pjw@kernel.org>
> Co-developed-by: Aaro Koskinen <aaro.koskinen@iki.fi>
> Co-developed-by: Geert Uytterhoeven <geert@linux-m68k.org>
"every Co-developed-by: must be immediately
followed by a Signed-off-by: of the associated co-author."
https://elixir.bootlin.com/linux/v7.0/source/Documentation/process/submitting-patches.rst#L506
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> Changes in v2:
> - add more missing entries
Thanks!
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* [PATCH] dt-bindings: thermal: qcom-tsens: Document Nord Temperature Sensor
From: Shawn Guo @ 2026-04-20 6:54 UTC (permalink / raw)
To: Daniel Lezcano
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio,
Dmitry Baryshkov, Bartosz Golaszewski, Deepti Jaggi, linux-pm,
devicetree, linux-arm-msm, linux-kernel, Shawn Guo
From: Deepti Jaggi <deepti.jaggi@oss.qualcomm.com>
Add compatible for Temperature Sensor (TSENS) of Nord SoC with
a fallback on qcom,tsens-v2.
Signed-off-by: Deepti Jaggi <deepti.jaggi@oss.qualcomm.com>
Signed-off-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
---
Documentation/devicetree/bindings/thermal/qcom-tsens.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
index 7d34ba00e684..e65ebc6f1698 100644
--- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
+++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
@@ -58,6 +58,7 @@ properties:
- qcom,glymur-tsens
- qcom,kaanapali-tsens
- qcom,milos-tsens
+ - qcom,nord-tsens
- qcom,msm8953-tsens
- qcom,msm8996-tsens
- qcom,msm8998-tsens
--
2.43.0
^ permalink raw reply related
* Re: [PATCH 2/2] arm64: dts: rockchip: Replace deprecated snps,* props for NanoPi R5S
From: Tianling Shen @ 2026-04-20 6:58 UTC (permalink / raw)
To: Diederik de Haas, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Heiko Stuebner
Cc: Arnd Bergmann, devicetree, linux-arm-kernel, linux-rockchip,
linux-kernel, Quentin Schulz, Jonas Karlman
In-Reply-To: <DHTSOV43O2EX.38TGASN7SQEZL@cknow-tech.com>
On 2026/4/15 22:23, Diederik de Haas wrote:
> On Wed Apr 1, 2026 at 3:11 PM CEST, Diederik de Haas wrote:
>> The various snps,reset-* properties are deprecated, so convert them into
>> their replacements.
>>
>> Signed-off-by: Diederik de Haas <diederik@cknow-tech.com>
>> ---
>> arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts | 7 +++----
>> 1 file changed, 3 insertions(+), 4 deletions(-)
>>
>> diff --git a/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts b/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts
>> index 90ce6f0e1dcf..92d044ec696b 100644
>> --- a/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts
>> +++ b/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts
>> @@ -85,10 +85,6 @@ &gmac0_tx_bus2
>> &gmac0_rx_bus2
>> &gmac0_rgmii_clk
>> &gmac0_rgmii_bus>;
>> - snps,reset-gpio = <&gpio0 RK_PC5 GPIO_ACTIVE_LOW>;
>> - snps,reset-active-low;
>> - /* Reset time is 15ms, 50ms for rtl8211f */
>> - snps,reset-delays-us = <0 15000 50000>;
>> tx_delay = <0x3c>;
>> rx_delay = <0x2f>;
>> status = "okay";
>> @@ -100,6 +96,9 @@ rgmii_phy0: ethernet-phy@1 {
>> reg = <1>;
>> pinctrl-0 = <&gmac0_rstn_gpio0_c5_pin>;
>> pinctrl-names = "default";
>> + reset-assert-us = <15000>;
>> + reset-deassert-us = <50000>;
>> + reset-gpios = <&gpio0 RK_PC5 GPIO_ACTIVE_LOW>;
>> };
>> };
>>
>
> Please disregard/drop this patch.
>
> I was recently made aware of 'sashiko.dev' and checked whether it had
> also checked my patch, which it did:
> https://sashiko.dev/#/patchset/20260401131551.734456-1-diederik%40cknow-tech.com
>
> And it turns out that the concern raised is valid (thanks Quentin!), so
> this patch could introduce a regression.
> So it looks like staying with the deprecated properties is actually
> better (in this case?).
Well actually we more or less rely on U-Boot to reset the PHY first now.
Many rockchip boards in tree require a reset before the PHY can be
recognized, but we just use the generic "ethernet-phy-ieee802.3-c22"
compatible.
Another option is to move the reset props to mdio node instead of PHY
node, though.
Thanks,
Tianling.
>
> Cheers,
> Diederik
^ permalink raw reply
* Re: [PATCH v2 3/4] gpio: realtek: Add driver for Realtek DHC RTD1625 SoC
From: Michael Walle @ 2026-04-20 7:22 UTC (permalink / raw)
To: Linus Walleij, Yu-Chun Lin [林祐君]
Cc: Bartosz Golaszewski, linux-gpio@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-realtek-soc@lists.infradead.org,
CY_Huang[黃鉦晏],
Stanley Chang[昌育德],
James Tai [戴志峰], robh@kernel.org,
krzk+dt@kernel.org, conor+dt@kernel.org, afaerber@suse.com,
TY_Chang[張子逸]
In-Reply-To: <CAD++jLkpS-T9yK=ctSwpLvXkj7s7ivmwu1KKwzy4KS40LVYeyA@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 3744 bytes --]
Hi,
On Sun Apr 19, 2026 at 11:19 PM CEST, Linus Walleij wrote:
> Hi Yu-Chun,
>
> On Fri, Apr 10, 2026 at 11:39 AM Yu-Chun Lin [林祐君]
> <eleanor.lin@realtek.com> wrote:
>
>> We did look into gpio-mmio and gpio-regmap, but they are not quite suitable for
>> our platform due to the specific hardware design:
>>
>> 1. Per-GPIO Dedicated Registers: Unlike typical GPIO controllers that pack 32 pins
>> into a single 32-bit register (1 bit per pin), our hardware uses a dedicated 32-bit
>> register for each individual GPIO. This single register controls the
>> input/output state, direction, and interrupt trigger type for that specific pin.
>
> Isn't that attainable by:
>
> - setting .ngpio_per_reg to 1 in struct gpio_regmap_config
Which is just used by the gpio_regmap_simple_xlate() anyway. So it
doesn't really matter. But yeah, 1 would be the correct value here,
assuming that the registers are consecutive.
> - extend .reg_mask_xlate callback with an enum for each operation
> (need to change all users of the .reg_mask_xlate callback but
> who cares, they are not many):
>
> e.g.
>
> enum gpio_regmap_operation {
> GPIO_REGMAP_GET_OP,
> GPIO_REGMAP_SET_OP,
> GPIO_REGMAP_SET_WITH_CLEAR_OP,
> GPIO_REGMAP_GET_DIR_OP,
> GPIO_REGMAP_SET_DIR_OP,
> };
>
> int (*reg_mask_xlate)(struct gpio_regmap *gpio,
> enum_gpio_regmap_operation op,
> unsigned int base,
> unsigned int offset, unsigned int *reg,
> unsigned int *mask);
>
> This way .reg_mask_xlate() can hit different bits in the returned
> *mask depending on operation and it will be find to pack all of
> the bits into one 32bit register.
>
> Added Michael Walle to the the thread, he will know if this is a
> good idea.
Nice idea, though the information is then redundant in the usual
case, i.e. drivers which need to translate specific registers
will do a "switch (base)" at the moment. These should be converted
to "switch (op)" just to keep all the drivers aligned and prevent
new drivers from using the old method. You'd need to touch them
anyway.
I was briefly thinking about making it somewhat possible to embed
the op into the base, if it would otherwise be all the same. That
way, you could gpio-regmap as is. A special case like
GPIO_REGMAP_ADDR_ZERO, that could be used by these kind of drivers,
but that is probably too hacky.
I'm fine with either way.
>> 2. Write-Enable (WREN) Mask Mechanism: Our hardware requires a specific Write-Enable
>> mask to be written simultaneously when updating the register values.
>
> Which is to just set bit 31.
>
> With the above scheme your .reg_mask_xlate callback can just set bit 31
> no matter what operating you're doing. Piece of cake.
Keep in mind, that this will make reading and writing somewhat
different. reading assumes there is only one bit set in mask,
because of the "!!(val & mask)" op, which is hardcoded. I'm not
against using the write like that though.
-michael
>> 3. Hardware Debounce: We also need to support hardware debounce settings per pin,
>> which requires custom configuration via set_config mapped to these specific per-pin
>> registers.
>
> Just add a version of an optional .set_config() call to gpio-regmap.c
> to handle this using .reg_mask_xlate() per above and add a new
> GPIO_REGMAP_CONFIG_OP to the above enum, problem solved.
>
> If it seems too hard I can write patch 1 & 2 adding this infrastructure
> but I bet you can easily see what can be done with gpio-regmap.c
> here provided Michael W approves the idea.
>
> Yours,
> Linus Walleij
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 297 bytes --]
^ permalink raw reply
* Re: [PATCH v7 1/3] dt-bindings: pinctrl: Add aspeed,ast2700-soc0-pinctrl
From: Billy Tsai @ 2026-04-20 7:22 UTC (permalink / raw)
To: Conor Dooley
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Joel Stanley, Andrew Jeffery, Linus Walleij, Bartosz Golaszewski,
Ryan Chen, Andrew Jeffery, devicetree@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
openbmc@lists.ozlabs.org, linux-gpio@vger.kernel.org,
linux-clk@vger.kernel.org
In-Reply-To: <20260417-anemia-borrower-fb90ac02b417@spud>
> > > > + properties:
> > > > + function:
> > > > + enum:
> > > > + - EMMC
> > > > + - JTAGDDR
> > > > + - JTAGM0
> > > > + - JTAGPCIEA
> > > > + - JTAGPCIEB
> > > > + - JTAGPSP
> > > > + - JTAGSSP
> > > > + - JTAGTSP
> > > > + - JTAGUSB3A
> > > > + - JTAGUSB3B
> > > > + - PCIERC0PERST
> > > > + - PCIERC1PERST
> > > > + - TSPRSTN
> > > > + - UFSCLKI
> > > > + - USB2AD0
> > > > + - USB2AD1
> > > > + - USB2AH
> > > > + - USB2AHP
> > > > + - USB2AHPD0
> > > > + - USB2AXH
> > > > + - USB2AXH2B
> > > > + - USB2AXHD1
> > > > + - USB2AXHP
> > > > + - USB2AXHP2B
> > > > + - USB2AXHPD1
> > > > + - USB2BD0
> > > > + - USB2BD1
> > > > + - USB2BH
> > > > + - USB2BHP
> > > > + - USB2BHPD0
> > > > + - USB2BXH
> > > > + - USB2BXH2A
> > > > + - USB2BXHD1
> > > > + - USB2BXHP
> > > > + - USB2BXHP2A
> > > > + - USB2BXHPD1
> > > > + - USB3AXH
> > > > + - USB3AXH2B
> > > > + - USB3AXHD
> > > > + - USB3AXHP
> > > > + - USB3AXHP2B
> > > > + - USB3AXHPD
> > > > + - USB3BXH
> > > > + - USB3BXH2A
> > > > + - USB3BXHD
> > > > + - USB3BXHP
> > > > + - USB3BXHP2A
> > > > + - USB3BXHPD
> > > > + - VB
> > > > + - VGADDC
> > > > +
> > > > + groups:
> > > > + enum:
> > > > + - EMMCCDN
> > > > + - EMMCG1
> > > > + - EMMCG4
> > > > + - EMMCG8
> > > > + - EMMCWPN
> > > > + - JTAG0
> > > > + - PCIERC0PERST
> > > > + - PCIERC1PERST
> > > > + - TSPRSTN
> > > > + - UFSCLKI
> > > > + - USB2A
> > > > + - USB2AAP
> > > > + - USB2ABP
> > > > + - USB2ADAP
> > > > + - USB2AH
> > > > + - USB2AHAP
> > > > + - USB2B
> > > > + - USB2BAP
> > > > + - USB2BBP
> > > > + - USB2BDBP
> > > > + - USB2BH
> > > > + - USB2BHBP
> > > > + - USB3A
> > > > + - USB3AAP
> > > > + - USB3ABP
> > > > + - USB3B
> > > > + - USB3BAP
> > > > + - USB3BBP
> > > > + - VB0
> > > > + - VB1
> > > > + - VGADDC
> > > > + pins:
> > > > + enum:
> > > > + - AB13
> > > > + - AB14
> > > > + - AC13
> > > > + - AC14
> > > > + - AD13
> > > > + - AD14
> > > > + - AE13
> > > > + - AE14
> > > > + - AE15
> > > > + - AF13
> > > > + - AF14
> > > > + - AF15
> > > Why do you have groups and pins?
> > > Is it valid in your device to have groups and pins in the same node?
> > The intent is to support both group-based mux selection and
> > configuration, as well as per-pin configuration.
> > In our hardware:
> > - `function` + `groups` are used for pinmux selection.
> > - `pins` is used for per-pin configuration (e.g. drive strength,
> > bias settings).
> > - `groups` may also be used for group-level configuration.
> > As a result, both `groups` and `pins` may appear in the same node,
> > but they serve different purposes and do not conflict:
> > - `groups` selects the mux function and may apply configuration to
> > the entire group.
> > - `pins` allows overriding or specifying configuration for individual
> > pins.
> > In most cases, only one of them is needed, but both are allowed when
> > both group-level and per-pin configuration are required.
> To be honest, that sounds like your groups are not sufficiently
> granular and should be reduced such that you can use them for pin
> settings.
The intent was to keep the binding flexible, but in practice the mixed
use of `groups` and `pins` in the same node is not expected to be used.
Given that, I agree this flexibility is unnecessary and makes the
binding semantics less clear. I'll rework the binding to make the
expected usage explicit rather than allowing combinations that do not
correspond to a real use case.
In particular, I'll split the constraints as follows:
- For pinmux, the presence of `function` will require `groups`, and
`pins` will not be allowed. This reflects the hardware design, where
the groups are defined by the pins affected by a given mux expression
- For pin configuration, exactly one of `groups` or `pins` will be
required (using oneOf), so that configuration is applied either at
group level or per-pin, but not both.
- if:
required:
- function
then:
required:
- groups
not:
required:
- pins
else:
oneOf:
- required:
- groups
not:
required:
- pins
- required:
- pins
not:
required:
- groups
Does this match what you had in mind?
Thanks
Billy Tsai
^ permalink raw reply
* [PATCH 0/2] Add PMAU0102 RPMH regulator support
From: Shawn Guo @ 2026-04-20 7:26 UTC (permalink / raw)
To: Mark Brown
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio,
Dmitry Baryshkov, Kamal Wadhwa, Bartosz Golaszewski, Deepti Jaggi,
devicetree, linux-arm-msm, linux-kernel, Shawn Guo
This series adds RPMH regulator support for PMAU0102 PMIC found on
Qualcomm Nord SoC based devices.
Patch 1 adds the PMAU0102 compatible to the dt-bindings for
qcom,rpmh-regulator, documenting its 8 SMPSes and 3 LDOs.
Patch 2 adds the corresponding vreg data to the rpmh-regulator driver.
Kamal Wadhwa (1):
regulator: rpmh-regulator: Add RPMH regulator support for Nord
Shawn Guo (1):
regulator: dt-bindings: qcom,rpmh: Add PMAU0102 support
.../regulator/qcom,rpmh-regulator.yaml | 11 +++++++++++
drivers/regulator/qcom-rpmh-regulator.c | 19 +++++++++++++++++++
2 files changed, 30 insertions(+)
--
2.43.0
^ permalink raw reply
* [PATCH 1/2] regulator: dt-bindings: qcom,rpmh: Add PMAU0102 support
From: Shawn Guo @ 2026-04-20 7:26 UTC (permalink / raw)
To: Mark Brown
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio,
Dmitry Baryshkov, Kamal Wadhwa, Bartosz Golaszewski, Deepti Jaggi,
devicetree, linux-arm-msm, linux-kernel, Shawn Guo
In-Reply-To: <20260420072639.1249984-1-shengchao.guo@oss.qualcomm.com>
Add support for PMAU0102 PMIC used on Nord boards.
Signed-off-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
---
.../bindings/regulator/qcom,rpmh-regulator.yaml | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml
index 58bb0ad5dda4..cd5391892fc6 100644
--- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml
+++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml
@@ -51,6 +51,7 @@ description: |
For PM8450, smps1 - smps6, ldo1 - ldo4
For PM8550, smps1 - smps6, ldo1 - ldo17, bob1 - bob2
For PM8998, smps1 - smps13, ldo1 - ldo28, lvs1 - lvs2
+ For PMAU0102, smps1 - smps8, ldo1 - ldo3
For PMH0101, ldo1 - ldo18, bob1 - bob2
For PMH0104, smps1 - smps4
For PMH0110, smps1 - smps10, ldo1 - ldo4
@@ -87,6 +88,7 @@ properties:
- qcom,pm8550ve-rpmh-regulators
- qcom,pm8550vs-rpmh-regulators
- qcom,pm8998-rpmh-regulators
+ - qcom,pmau0102-rpmh-regulators
- qcom,pmc8180-rpmh-regulators
- qcom,pmc8180c-rpmh-regulators
- qcom,pmc8380-rpmh-regulators
@@ -413,6 +415,15 @@ allOf:
patternProperties:
"^vdd-s([1-9]|1[0-3])-supply$": true
+ - if:
+ properties:
+ compatible:
+ enum:
+ - qcom,pmau0102-rpmh-regulators
+ then:
+ patternProperties:
+ "^vdd-s[1-8]-supply$": true
+
- if:
properties:
compatible:
--
2.43.0
^ permalink raw reply related
* [PATCH 2/2] regulator: rpmh-regulator: Add RPMH regulator support for Nord
From: Shawn Guo @ 2026-04-20 7:26 UTC (permalink / raw)
To: Mark Brown
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio,
Dmitry Baryshkov, Kamal Wadhwa, Bartosz Golaszewski, Deepti Jaggi,
devicetree, linux-arm-msm, linux-kernel, Shawn Guo
In-Reply-To: <20260420072639.1249984-1-shengchao.guo@oss.qualcomm.com>
From: Kamal Wadhwa <kamal.wadhwa@oss.qualcomm.com>
Add support for PMAU0102 PMIC voltage regulators which are present on
Nord boards.
Signed-off-by: Kamal Wadhwa <kamal.wadhwa@oss.qualcomm.com>
Signed-off-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
---
drivers/regulator/qcom-rpmh-regulator.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c
index 6e4cb2871fca..9545300d7b03 100644
--- a/drivers/regulator/qcom-rpmh-regulator.c
+++ b/drivers/regulator/qcom-rpmh-regulator.c
@@ -1100,6 +1100,21 @@ static const struct rpmh_vreg_init_data pm8998_vreg_data[] = {
{}
};
+static const struct rpmh_vreg_init_data pmau0102_vreg_data[] = {
+ RPMH_VREG("smps1", SMPS, 1, &pmic5_ftsmps527, "vdd-s1"),
+ RPMH_VREG("smps2", SMPS, 2, &pmic5_ftsmps527, "vdd-s2"),
+ RPMH_VREG("smps3", SMPS, 3, &pmic5_ftsmps527, "vdd-s3"),
+ RPMH_VREG("smps4", SMPS, 4, &pmic5_ftsmps527, "vdd-s4"),
+ RPMH_VREG("smps5", SMPS, 5, &pmic5_ftsmps527, "vdd-s5"),
+ RPMH_VREG("smps6", SMPS, 6, &pmic5_ftsmps527, "vdd-s6"),
+ RPMH_VREG("smps7", SMPS, 7, &pmic5_ftsmps527, "vdd-s7"),
+ RPMH_VREG("smps8", SMPS, 8, &pmic5_ftsmps527, "vdd-s8"),
+ RPMH_VREG("ldo1", LDO, 1, &pmic5_nldo515, NULL),
+ RPMH_VREG("ldo2", LDO, 2, &pmic5_nldo515, NULL),
+ RPMH_VREG("ldo3", LDO, 3, &pmic5_pldo515_mv, NULL),
+ {}
+};
+
static const struct rpmh_vreg_init_data pmg1110_vreg_data[] = {
RPMH_VREG("smps1", SMPS, 1, &pmic5_ftsmps510, "vdd-s1"),
{}
@@ -1877,6 +1892,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = {
.compatible = "qcom,pm8998-rpmh-regulators",
.data = pm8998_vreg_data,
},
+ {
+ .compatible = "qcom,pmau0102-rpmh-regulators",
+ .data = pmau0102_vreg_data,
+ },
{
.compatible = "qcom,pmg1110-rpmh-regulators",
.data = pmg1110_vreg_data,
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v3 5/5] arch: arm64: dts: qcom: Add support for PCIe3a
From: Qiang Yu @ 2026-04-20 7:30 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Bjorn Andersson, Konrad Dybcio,
linux-arm-msm, linux-phy, devicetree, linux-kernel
In-Reply-To: <d486616b-ef21-4933-aaf5-dcba339dd8cc@kernel.org>
On Thu, Apr 16, 2026 at 08:19:09AM +0200, Krzysztof Kozlowski wrote:
> On 16/04/2026 05:24, Qiang Yu wrote:
> > On Wed, Apr 15, 2026 at 09:44:15AM +0200, Krzysztof Kozlowski wrote:
> >> On Sun, Apr 12, 2026 at 11:26:00PM -0700, Qiang Yu wrote:
> >>> Describe PCIe3a controller and PHY. Also add required system resources
> >>> like regulators, clocks, interrupts and registers configuration for PCIe3a.
> >>>
> >>> Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
> >>
> >> subject: drop arch.
> >>
> >> Please use subject prefixes matching the subsystem. You can get them for
> >> example with 'git log --oneline -- DIRECTORY_OR_FILE' on the directory
> >> your patch is touching. For bindings, the preferred subjects are
> >> explained here:
> >> https://www.kernel.org/doc/html/latest/devicetree/bindings/submitting-patches.html#i-for-patch-submitters
> >>
> >
> > Thanks for pointing me the link. I’ll drop arch: in next version.
> >
> >>> ---
> >>> arch/arm64/boot/dts/qcom/glymur.dtsi | 316 ++++++++++++++++++++++++++++++++++-
> >>> 1 file changed, 315 insertions(+), 1 deletion(-)
> >>>
> >>> diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi
> >>> index f23cf81ddb77a4138deeb4e00dd8b316930a2feb..c15f87c37ecbad72076a6c731f4959a1a8bd8425 100644
> >>> --- a/arch/arm64/boot/dts/qcom/glymur.dtsi
> >>> +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi
> >>> @@ -736,7 +736,7 @@ gcc: clock-controller@100000 {
> >>> <0>, /* USB 2 Phy PCIE PIPEGMUX */
> >>> <0>, /* USB 2 Phy PIPEGMUX */
> >>> <0>, /* USB 2 Phy SYS PCIE PIPEGMUX */
> >>> - <0>, /* PCIe 3a */
> >>> + <&pcie3a_phy>, /* PCIe 3a */
> >>> <&pcie3b_phy>, /* PCIe 3b */
> >>> <&pcie4_phy>, /* PCIe 4 */
> >>> <&pcie5_phy>, /* PCIe 5 */
> >>> @@ -3640,6 +3640,320 @@ pcie3b_port0: pcie@0 {
> >>> };
> >>
> >> ...
> >>
> >>>> + pcie3a_phy: phy@f00000 {
> >>
> >> Same comment as before.
> >>
> >
> > The existing PCIe/PHY nodes are not strictly ordered by address. Current
> > order is:
>
> Obviously we cannot even keep order of nodes when creating a new DTSI
> file from scratch.
>
> But adding @f00000 after @1c10000 makes even less sense, regardless how
> bad existing code is. Don't make it worse!
>
> This goes before phy@fa0000
Okay, will move phy@f00000 before phy@fa0000
- Qiang Yu
>
> >
> > - pcie4: pci@1bf0000
> > - pcie4_phy: phy@1bf6000
> > - pcie5: pci@1b40000
> > - pcie5_phy: phy@1b50000
> > - pcie6: pci@1c00000
> > - pcie6_phy: phy@1c06000
> > - pcie3b: pci@1b80000
> > - pcie3a: pci@1c10000 (added in this patch)
> > - pcie3a_phy: phy@f00000 (added in this patch)
> > - pcie3b_phy: phy@f10000
> >
> > Do you want me to reorder these nodes to follow strict address order?
>
> No, but don't add nodes randomly or following the previous broken order.
>
> Best regards,
> Krzysztof
^ permalink raw reply
* [PATCH] dt-bindings: crypto: qcom,inline-crypto-engine: Document Nord ICE
From: Shawn Guo @ 2026-04-20 7:33 UTC (permalink / raw)
To: Herbert Xu
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
Konrad Dybcio, Dmitry Baryshkov, Bartosz Golaszewski,
Deepti Jaggi, linux-crypto, devicetree, linux-arm-msm,
linux-kernel, Shawn Guo
Add compatible for Inline Crypto Engine (ICE) on Qualcomm Nord SoC
witha fallback on qcom,inline-crypto-engine.
Signed-off-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
---
.../devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml b/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml
index 876bf90ed96e..9251db2b8fcd 100644
--- a/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml
+++ b/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml
@@ -16,6 +16,7 @@ properties:
- qcom,eliza-inline-crypto-engine
- qcom,kaanapali-inline-crypto-engine
- qcom,milos-inline-crypto-engine
+ - qcom,nord-inline-crypto-engine
- qcom,qcs8300-inline-crypto-engine
- qcom,sa8775p-inline-crypto-engine
- qcom,sc7180-inline-crypto-engine
--
2.43.0
^ permalink raw reply related
* [PATCH v2 0/4] clk: qcom: Add common clkref support and migrate Glymur
From: Qiang Yu @ 2026-04-20 7:42 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Taniya Das, Konrad Dybcio
Cc: linux-arm-msm, linux-clk, devicetree, linux-kernel, Qiang Yu
This series adds a common clkref_en implementation and converts glymur to
use it, along with the related binding and DTS updates.
The PCIe clkref clocks on Glymur gate the QREF block which provides
reference clocks to the PCIe PHYs. QREF requires LDO supplies and a
reference voltage from the refgen block to operate. The refgen block
itself requires vdda-refgen_0p9 and vdda-refgen_1p2 LDOs to function.
Previously, these QREF votes were done in PHY drivers. In earlier
discussion [1], the feedback was that this is the wrong ownership point:
those supplies are for the QREF controlled by clkref registers, not for
PHY directly. Based on that feedback, this series keeps the regulator
handling with the clkref control path.
Another reason for this series is reuse. clkref_en registers may live in
different blocks across platforms (for example TCSR on Glymur, TLMM on
SM8750 [2]), while the behavior is the same. The common helper lets each
driver provide simple descriptors (name, offset, optional supplies) and
reuse shared registration and runtime logic.
[1] https://lore.kernel.org/lkml/aEBfV2M-ZqDF7aRz@hovoldconsulting.com/
[2] https://lore.kernel.org/linux-arm-msm/20260202-topic-8750_tcsr-v1-0-cd7e6648c64f@oss.qualcomm.com/
Changes in v2:
- RFC tag dropped
- Changed back to additionalProperties: false
- Moved all Glymur supply properties into top-level properties so they are explicitly defined.
- Link to v1: https://lore.kernel.org/all/20260331-qref_vote-v1-0-3fd7fbf87864@oss.qualcomm.com/
Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
---
Qiang Yu (4):
dt-bindings: clock: qcom: Add QREF regulator supplies for glymur
clk: qcom: Add generic clkref_en support
clk: qcom: tcsrcc-glymur: Migrate tcsr_pcie_N_clkref_en to clk_ref common helper
arm64: dts: qcom: glymur: Add QREF regulator supplies to TCSR
.../bindings/clock/qcom,sm8550-tcsr.yaml | 40 +++
arch/arm64/boot/dts/qcom/glymur-crd.dts | 19 ++
drivers/clk/qcom/Makefile | 1 +
drivers/clk/qcom/clk-ref.c | 202 ++++++++++++
drivers/clk/qcom/tcsrcc-glymur.c | 340 ++++++---------------
include/linux/clk/qcom.h | 69 +++++
6 files changed, 424 insertions(+), 247 deletions(-)
---
base-commit: c7275b05bc428c7373d97aa2da02d3a7fa6b9f66
change-id: 20260419-vote_qref_in_tcsrcc-c015dc0c2ae8
Best regards,
--
Qiang Yu <qiang.yu@oss.qualcomm.com>
^ permalink raw reply
* [PATCH v2 1/4] dt-bindings: clock: qcom: Add QREF regulator supplies for glymur
From: Qiang Yu @ 2026-04-20 7:42 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Taniya Das, Konrad Dybcio
Cc: linux-arm-msm, linux-clk, devicetree, linux-kernel, Qiang Yu
In-Reply-To: <20260420-vote_qref_in_tcsrcc-v2-0-589a23ae640a@oss.qualcomm.com>
Add regulator supply properties for the Glymur TCSR QREF/REFGEN blocks
required by clkref clocks.
The vdda-qreftx*, vdda-qrefrpt*, and vdda-qrefrx* supplies map to common
QREF TX/RPT/RX components, while SoC-specific topology and instance count
differ. Document them here for qcom,glymur-tcsr.
Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
---
.../bindings/clock/qcom,sm8550-tcsr.yaml | 40 ++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml
index 1ccdf4b0f5dd..0cf612e6d7ee 100644
--- a/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml
+++ b/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml
@@ -51,6 +51,46 @@ properties:
'#reset-cells':
const: 1
+ vdda-refgen-0p9-supply: true
+ vdda-refgen-1p2-supply: true
+ vdda-qrefrx0-0p9-supply: true
+ vdda-qrefrx1-0p9-supply: true
+ vdda-qrefrx2-0p9-supply: true
+ vdda-qrefrx4-0p9-supply: true
+ vdda-qrefrx5-0p9-supply: true
+ vdda-qreftx0-0p9-supply: true
+ vdda-qreftx0-1p2-supply: true
+ vdda-qreftx1-0p9-supply: true
+ vdda-qrefrpt0-0p9-supply: true
+ vdda-qrefrpt1-0p9-supply: true
+ vdda-qrefrpt2-0p9-supply: true
+ vdda-qrefrpt3-0p9-supply: true
+ vdda-qrefrpt4-0p9-supply: true
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: qcom,glymur-tcsr
+ else:
+ properties:
+ vdda-refgen-0p9-supply: false
+ vdda-refgen-1p2-supply: false
+ vdda-qrefrx0-0p9-supply: false
+ vdda-qrefrx1-0p9-supply: false
+ vdda-qrefrx2-0p9-supply: false
+ vdda-qrefrx4-0p9-supply: false
+ vdda-qrefrx5-0p9-supply: false
+ vdda-qreftx0-0p9-supply: false
+ vdda-qreftx0-1p2-supply: false
+ vdda-qreftx1-0p9-supply: false
+ vdda-qrefrpt0-0p9-supply: false
+ vdda-qrefrpt1-0p9-supply: false
+ vdda-qrefrpt2-0p9-supply: false
+ vdda-qrefrpt3-0p9-supply: false
+ vdda-qrefrpt4-0p9-supply: false
+
required:
- compatible
- clocks
--
2.34.1
^ permalink raw reply related
* [PATCH v2 2/4] clk: qcom: Add generic clkref_en support
From: Qiang Yu @ 2026-04-20 7:42 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Taniya Das, Konrad Dybcio
Cc: linux-arm-msm, linux-clk, devicetree, linux-kernel, Qiang Yu
In-Reply-To: <20260420-vote_qref_in_tcsrcc-v2-0-589a23ae640a@oss.qualcomm.com>
Before XO refclk is distributed to PCIe/USB/eDP PHYs, it passes through
a QREF block. QREF is powered by dedicated LDO rails, and the clkref_en
register controls whether refclk is gated through to the PHY side.
These clkref controls are different from typical GCC branch clocks:
- only a single enable bit is present, without branch-style config bits
- regulators must be voted before enable and unvoted after disable
Model this as a dedicated clk_ref clock type with custom clk_ops instead
of reusing struct clk_branch semantics.
Also provide a common registration/probe API so the same clkref model
can be reused regardless of where clkref_en registers are placed, e.g.
TCSR on glymur and TLMM on SM8750.
Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
---
drivers/clk/qcom/Makefile | 1 +
drivers/clk/qcom/clk-ref.c | 202 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/clk/qcom.h | 69 ++++++++++++++++
3 files changed, 272 insertions(+)
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 89d07c35e4d9..1659e9d9afa9 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -8,6 +8,7 @@ clk-qcom-y += clk-pll.o
clk-qcom-y += clk-rcg.o
clk-qcom-y += clk-rcg2.o
clk-qcom-y += clk-branch.o
+clk-qcom-y += clk-ref.o
clk-qcom-y += clk-regmap-divider.o
clk-qcom-y += clk-regmap-mux.o
clk-qcom-y += clk-regmap-mux-div.o
diff --git a/drivers/clk/qcom/clk-ref.c b/drivers/clk/qcom/clk-ref.c
new file mode 100644
index 000000000000..ea2ed03460f2
--- /dev/null
+++ b/drivers/clk/qcom/clk-ref.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk/qcom.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#define QCOM_CLK_REF_EN_MASK BIT(0)
+
+struct qcom_clk_ref_provider {
+ struct qcom_clk_ref *refs;
+ size_t num_refs;
+};
+
+static inline struct qcom_clk_ref *to_qcom_clk_ref(struct clk_hw *hw)
+{
+ return container_of(hw, struct qcom_clk_ref, hw);
+}
+
+static const struct clk_parent_data qcom_clk_ref_parent_data = {
+ .index = 0,
+};
+
+static int qcom_clk_ref_prepare(struct clk_hw *hw)
+{
+ struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
+ int ret;
+
+ if (!rclk->desc.num_regulators)
+ return 0;
+
+ ret = regulator_bulk_enable(rclk->desc.num_regulators, rclk->regulators);
+ if (ret)
+ pr_err("Failed to enable regulators for %s: %d\n",
+ clk_hw_get_name(hw), ret);
+
+ return ret;
+}
+
+static void qcom_clk_ref_unprepare(struct clk_hw *hw)
+{
+ struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
+
+ if (rclk->desc.num_regulators)
+ regulator_bulk_disable(rclk->desc.num_regulators, rclk->regulators);
+}
+
+static int qcom_clk_ref_enable(struct clk_hw *hw)
+{
+ struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
+ int ret;
+
+ ret = regmap_update_bits(rclk->regmap, rclk->desc.offset, QCOM_CLK_REF_EN_MASK,
+ QCOM_CLK_REF_EN_MASK);
+ if (ret)
+ return ret;
+
+ udelay(10);
+
+ return 0;
+}
+
+static void qcom_clk_ref_disable(struct clk_hw *hw)
+{
+ struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
+
+ regmap_update_bits(rclk->regmap, rclk->desc.offset, QCOM_CLK_REF_EN_MASK, 0);
+ udelay(10);
+}
+
+static int qcom_clk_ref_is_enabled(struct clk_hw *hw)
+{
+ struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
+ u32 val;
+ int ret;
+
+ ret = regmap_read(rclk->regmap, rclk->desc.offset, &val);
+ if (ret)
+ return ret;
+
+ return !!(val & QCOM_CLK_REF_EN_MASK);
+}
+
+static const struct clk_ops qcom_clk_ref_ops = {
+ .prepare = qcom_clk_ref_prepare,
+ .unprepare = qcom_clk_ref_unprepare,
+ .enable = qcom_clk_ref_enable,
+ .disable = qcom_clk_ref_disable,
+ .is_enabled = qcom_clk_ref_is_enabled,
+};
+
+static int qcom_clk_ref_register(struct device *dev, struct regmap *regmap,
+ struct qcom_clk_ref *clk_refs,
+ const struct qcom_clk_ref_desc *descs,
+ size_t num_clk_refs)
+{
+ const struct qcom_clk_ref_desc *desc;
+ struct qcom_clk_ref *clk_ref;
+ size_t clk_idx;
+ unsigned int i;
+ int ret;
+
+ for (clk_idx = 0; clk_idx < num_clk_refs; clk_idx++) {
+ clk_ref = &clk_refs[clk_idx];
+ desc = &descs[clk_idx];
+
+ if (!desc->name)
+ return -EINVAL;
+
+ clk_ref->regmap = regmap;
+ clk_ref->desc = *desc;
+
+ if (clk_ref->desc.num_regulators) {
+ clk_ref->regulators = devm_kcalloc(dev, clk_ref->desc.num_regulators,
+ sizeof(*clk_ref->regulators),
+ GFP_KERNEL);
+ if (!clk_ref->regulators)
+ return -ENOMEM;
+
+ for (i = 0; i < clk_ref->desc.num_regulators; i++)
+ clk_ref->regulators[i].supply =
+ clk_ref->desc.regulator_names[i];
+
+ ret = devm_regulator_bulk_get(dev, clk_ref->desc.num_regulators,
+ clk_ref->regulators);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get regulators for %s\n",
+ clk_ref->desc.name);
+ }
+
+ clk_ref->init_data.name = clk_ref->desc.name;
+ clk_ref->init_data.parent_data = &qcom_clk_ref_parent_data;
+ clk_ref->init_data.num_parents = 1;
+ clk_ref->init_data.ops = &qcom_clk_ref_ops;
+ clk_ref->hw.init = &clk_ref->init_data;
+
+ ret = devm_clk_hw_register(dev, &clk_ref->hw);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct clk_hw *qcom_clk_ref_provider_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct qcom_clk_ref_provider *provider = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= provider->num_refs)
+ return ERR_PTR(-EINVAL);
+
+ return &provider->refs[idx].hw;
+}
+
+int qcom_clk_ref_probe(struct platform_device *pdev,
+ const struct regmap_config *config,
+ const struct qcom_clk_ref_desc *descs,
+ size_t num_clk_refs)
+{
+ struct qcom_clk_ref_provider *provider;
+ struct device *dev = &pdev->dev;
+ struct regmap *regmap;
+ void __iomem *base;
+ int ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(dev, base, config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ provider = devm_kzalloc(dev, sizeof(*provider), GFP_KERNEL);
+ if (!provider)
+ return -ENOMEM;
+
+ provider->refs = devm_kcalloc(dev, num_clk_refs, sizeof(*provider->refs),
+ GFP_KERNEL);
+ if (!provider->refs)
+ return -ENOMEM;
+
+ provider->num_refs = num_clk_refs;
+
+ ret = qcom_clk_ref_register(dev, regmap, provider->refs, descs,
+ provider->num_refs);
+ if (ret)
+ return ret;
+
+ return devm_of_clk_add_hw_provider(dev, qcom_clk_ref_provider_get, provider);
+}
+EXPORT_SYMBOL_GPL(qcom_clk_ref_probe);
diff --git a/include/linux/clk/qcom.h b/include/linux/clk/qcom.h
new file mode 100644
index 000000000000..09e2e3178cfb
--- /dev/null
+++ b/include/linux/clk/qcom.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __LINUX_CLK_QCOM_H
+#define __LINUX_CLK_QCOM_H
+
+#include <linux/clk-provider.h>
+#include <linux/errno.h>
+#include <linux/kconfig.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+struct device;
+struct platform_device;
+struct regulator_bulk_data;
+
+/**
+ * struct qcom_clk_ref_desc - descriptor for a clkref_en gate clock
+ * @name: clock name exposed to the common clock framework
+ * @offset: clkref_en register offset from the block base
+ * @regulator_names: optional supply names enabled while preparing the clock
+ * @num_regulators: number of entries in @regulator_names
+ */
+struct qcom_clk_ref_desc {
+ const char *name;
+ u32 offset;
+ const char * const *regulator_names;
+ unsigned int num_regulators;
+};
+
+/**
+ * struct qcom_clk_ref - per-clock data for a clkref_en gate clock
+ * @hw: common clock framework hardware clock handle
+ * @init_data: common clock framework registration data
+ * @regmap: register map backing the clkref_en register
+ * @desc: clock descriptor copied at registration time
+ * @regulators: optional bulk regulator handles for @desc.regulator_names
+ */
+struct qcom_clk_ref {
+ struct clk_hw hw;
+ struct clk_init_data init_data;
+ struct regmap *regmap;
+ struct qcom_clk_ref_desc desc;
+ struct regulator_bulk_data *regulators;
+};
+
+#if IS_ENABLED(CONFIG_COMMON_CLK_QCOM)
+
+int qcom_clk_ref_probe(struct platform_device *pdev,
+ const struct regmap_config *config,
+ const struct qcom_clk_ref_desc *descs,
+ size_t num_clk_refs);
+
+#else
+
+static inline int
+qcom_clk_ref_probe(struct platform_device *pdev,
+ const struct regmap_config *config,
+ const struct qcom_clk_ref_desc *descs,
+ size_t num_clk_refs)
+{
+ return -EOPNOTSUPP;
+}
+
+#endif
+
+#endif
--
2.34.1
^ permalink raw reply related
* [PATCH v2 3/4] clk: qcom: tcsrcc-glymur: Migrate tcsr_pcie_N_clkref_en to clk_ref common helper
From: Qiang Yu @ 2026-04-20 7:42 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Taniya Das, Konrad Dybcio
Cc: linux-arm-msm, linux-clk, devicetree, linux-kernel, Qiang Yu
In-Reply-To: <20260420-vote_qref_in_tcsrcc-v2-0-589a23ae640a@oss.qualcomm.com>
Replace local clk_branch-based clkref definitions with descriptor-based
registration via qcom_clk_ref_probe().
This keeps the glymur driver focused on clock metadata and reuses common
runtime logic for regulator handling, enable/disable sequencing, and OF
provider wiring.
Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
---
drivers/clk/qcom/tcsrcc-glymur.c | 340 +++++++++++----------------------------
1 file changed, 93 insertions(+), 247 deletions(-)
diff --git a/drivers/clk/qcom/tcsrcc-glymur.c b/drivers/clk/qcom/tcsrcc-glymur.c
index 9c0edebcdbb1..585f87b23af2 100644
--- a/drivers/clk/qcom/tcsrcc-glymur.c
+++ b/drivers/clk/qcom/tcsrcc-glymur.c
@@ -4,265 +4,115 @@
*/
#include <linux/clk-provider.h>
+#include <linux/clk/qcom.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <dt-bindings/clock/qcom,glymur-tcsr.h>
-#include "clk-alpha-pll.h"
-#include "clk-branch.h"
-#include "clk-pll.h"
-#include "clk-rcg.h"
-#include "clk-regmap.h"
-#include "clk-regmap-divider.h"
-#include "clk-regmap-mux.h"
-#include "common.h"
-#include "gdsc.h"
-#include "reset.h"
-
-enum {
- DT_BI_TCXO_PAD,
-};
-
-static struct clk_branch tcsr_edp_clkref_en = {
- .halt_reg = 0x60,
- .halt_check = BRANCH_HALT_DELAY,
- .clkr = {
- .enable_reg = 0x60,
- .enable_mask = BIT(0),
- .hw.init = &(const struct clk_init_data) {
- .name = "tcsr_edp_clkref_en",
- .parent_data = &(const struct clk_parent_data){
- .index = DT_BI_TCXO_PAD,
- },
- .num_parents = 1,
- .ops = &clk_branch2_ops,
- },
+static const char * const tcsr_pcie_1_regulators[] = {
+ "vdda-refgen-0p9",
+ "vdda-refgen-1p2",
+ "vdda-qrefrx5-0p9",
+ "vdda-qreftx0-0p9",
+ "vdda-qreftx0-1p2",
+};
+
+static const char * const tcsr_pcie_2_regulators[] = {
+ "vdda-refgen-0p9",
+ "vdda-refgen-1p2",
+ "vdda-qreftx1-0p9",
+ "vdda-qrefrpt0-0p9",
+ "vdda-qrefrpt1-0p9",
+ "vdda-qrefrpt2-0p9",
+ "vdda-qrefrx2-0p9",
+};
+
+static const char * const tcsr_pcie_3_regulators[] = {
+ "vdda-refgen-0p9",
+ "vdda-refgen-1p2",
+ "vdda-qreftx1-0p9",
+ "vdda-qrefrpt0-0p9",
+ "vdda-qrefrpt1-0p9",
+ "vdda-qrefrx1-0p9",
+};
+
+static const char * const tcsr_pcie_4_regulators[] = {
+ "vdda-refgen-0p9",
+ "vdda-refgen-1p2",
+ "vdda-qreftx1-0p9",
+ "vdda-qrefrpt0-0p9",
+ "vdda-qrefrpt1-0p9",
+ "vdda-qrefrpt2-0p9",
+ "vdda-qrefrx2-0p9",
+};
+
+static const struct qcom_clk_ref_desc tcsr_cc_glymur_clk_descs[] = {
+ [TCSR_EDP_CLKREF_EN] = {
+ .name = "tcsr_edp_clkref_en",
+ .offset = 0x60,
},
-};
-
-static struct clk_branch tcsr_pcie_1_clkref_en = {
- .halt_reg = 0x48,
- .halt_check = BRANCH_HALT_DELAY,
- .clkr = {
- .enable_reg = 0x48,
- .enable_mask = BIT(0),
- .hw.init = &(const struct clk_init_data) {
- .name = "tcsr_pcie_1_clkref_en",
- .parent_data = &(const struct clk_parent_data){
- .index = DT_BI_TCXO_PAD,
- },
- .num_parents = 1,
- .ops = &clk_branch2_ops,
- },
+ [TCSR_PCIE_1_CLKREF_EN] = {
+ .name = "tcsr_pcie_1_clkref_en",
+ .offset = 0x48,
+ .regulator_names = tcsr_pcie_1_regulators,
+ .num_regulators = ARRAY_SIZE(tcsr_pcie_1_regulators),
},
-};
-
-static struct clk_branch tcsr_pcie_2_clkref_en = {
- .halt_reg = 0x4c,
- .halt_check = BRANCH_HALT_DELAY,
- .clkr = {
- .enable_reg = 0x4c,
- .enable_mask = BIT(0),
- .hw.init = &(const struct clk_init_data) {
- .name = "tcsr_pcie_2_clkref_en",
- .parent_data = &(const struct clk_parent_data){
- .index = DT_BI_TCXO_PAD,
- },
- .num_parents = 1,
- .ops = &clk_branch2_ops,
- },
+ [TCSR_PCIE_2_CLKREF_EN] = {
+ .name = "tcsr_pcie_2_clkref_en",
+ .offset = 0x4c,
+ .regulator_names = tcsr_pcie_2_regulators,
+ .num_regulators = ARRAY_SIZE(tcsr_pcie_2_regulators),
},
-};
-
-static struct clk_branch tcsr_pcie_3_clkref_en = {
- .halt_reg = 0x54,
- .halt_check = BRANCH_HALT_DELAY,
- .clkr = {
- .enable_reg = 0x54,
- .enable_mask = BIT(0),
- .hw.init = &(const struct clk_init_data) {
- .name = "tcsr_pcie_3_clkref_en",
- .parent_data = &(const struct clk_parent_data){
- .index = DT_BI_TCXO_PAD,
- },
- .num_parents = 1,
- .ops = &clk_branch2_ops,
- },
+ [TCSR_PCIE_3_CLKREF_EN] = {
+ .name = "tcsr_pcie_3_clkref_en",
+ .offset = 0x54,
+ .regulator_names = tcsr_pcie_3_regulators,
+ .num_regulators = ARRAY_SIZE(tcsr_pcie_3_regulators),
},
-};
-
-static struct clk_branch tcsr_pcie_4_clkref_en = {
- .halt_reg = 0x58,
- .halt_check = BRANCH_HALT_DELAY,
- .clkr = {
- .enable_reg = 0x58,
- .enable_mask = BIT(0),
- .hw.init = &(const struct clk_init_data) {
- .name = "tcsr_pcie_4_clkref_en",
- .parent_data = &(const struct clk_parent_data){
- .index = DT_BI_TCXO_PAD,
- },
- .num_parents = 1,
- .ops = &clk_branch2_ops,
- },
+ [TCSR_PCIE_4_CLKREF_EN] = {
+ .name = "tcsr_pcie_4_clkref_en",
+ .offset = 0x58,
+ .regulator_names = tcsr_pcie_4_regulators,
+ .num_regulators = ARRAY_SIZE(tcsr_pcie_4_regulators),
},
-};
-
-static struct clk_branch tcsr_usb2_1_clkref_en = {
- .halt_reg = 0x6c,
- .halt_check = BRANCH_HALT_DELAY,
- .clkr = {
- .enable_reg = 0x6c,
- .enable_mask = BIT(0),
- .hw.init = &(const struct clk_init_data) {
- .name = "tcsr_usb2_1_clkref_en",
- .parent_data = &(const struct clk_parent_data){
- .index = DT_BI_TCXO_PAD,
- },
- .num_parents = 1,
- .ops = &clk_branch2_ops,
- },
+ [TCSR_USB2_1_CLKREF_EN] = {
+ .name = "tcsr_usb2_1_clkref_en",
+ .offset = 0x6c,
},
-};
-
-static struct clk_branch tcsr_usb2_2_clkref_en = {
- .halt_reg = 0x70,
- .halt_check = BRANCH_HALT_DELAY,
- .clkr = {
- .enable_reg = 0x70,
- .enable_mask = BIT(0),
- .hw.init = &(const struct clk_init_data) {
- .name = "tcsr_usb2_2_clkref_en",
- .parent_data = &(const struct clk_parent_data){
- .index = DT_BI_TCXO_PAD,
- },
- .num_parents = 1,
- .ops = &clk_branch2_ops,
- },
+ [TCSR_USB2_2_CLKREF_EN] = {
+ .name = "tcsr_usb2_2_clkref_en",
+ .offset = 0x70,
},
-};
-
-static struct clk_branch tcsr_usb2_3_clkref_en = {
- .halt_reg = 0x74,
- .halt_check = BRANCH_HALT_DELAY,
- .clkr = {
- .enable_reg = 0x74,
- .enable_mask = BIT(0),
- .hw.init = &(const struct clk_init_data) {
- .name = "tcsr_usb2_3_clkref_en",
- .parent_data = &(const struct clk_parent_data){
- .index = DT_BI_TCXO_PAD,
- },
- .num_parents = 1,
- .ops = &clk_branch2_ops,
- },
+ [TCSR_USB2_3_CLKREF_EN] = {
+ .name = "tcsr_usb2_3_clkref_en",
+ .offset = 0x74,
},
-};
-
-static struct clk_branch tcsr_usb2_4_clkref_en = {
- .halt_reg = 0x88,
- .halt_check = BRANCH_HALT_DELAY,
- .clkr = {
- .enable_reg = 0x88,
- .enable_mask = BIT(0),
- .hw.init = &(const struct clk_init_data) {
- .name = "tcsr_usb2_4_clkref_en",
- .parent_data = &(const struct clk_parent_data){
- .index = DT_BI_TCXO_PAD,
- },
- .num_parents = 1,
- .ops = &clk_branch2_ops,
- },
+ [TCSR_USB2_4_CLKREF_EN] = {
+ .name = "tcsr_usb2_4_clkref_en",
+ .offset = 0x88,
},
-};
-
-static struct clk_branch tcsr_usb3_0_clkref_en = {
- .halt_reg = 0x64,
- .halt_check = BRANCH_HALT_DELAY,
- .clkr = {
- .enable_reg = 0x64,
- .enable_mask = BIT(0),
- .hw.init = &(const struct clk_init_data) {
- .name = "tcsr_usb3_0_clkref_en",
- .parent_data = &(const struct clk_parent_data){
- .index = DT_BI_TCXO_PAD,
- },
- .num_parents = 1,
- .ops = &clk_branch2_ops,
- },
+ [TCSR_USB3_0_CLKREF_EN] = {
+ .name = "tcsr_usb3_0_clkref_en",
+ .offset = 0x64,
},
-};
-
-static struct clk_branch tcsr_usb3_1_clkref_en = {
- .halt_reg = 0x68,
- .halt_check = BRANCH_HALT_DELAY,
- .clkr = {
- .enable_reg = 0x68,
- .enable_mask = BIT(0),
- .hw.init = &(const struct clk_init_data) {
- .name = "tcsr_usb3_1_clkref_en",
- .parent_data = &(const struct clk_parent_data){
- .index = DT_BI_TCXO_PAD,
- },
- .num_parents = 1,
- .ops = &clk_branch2_ops,
- },
+ [TCSR_USB3_1_CLKREF_EN] = {
+ .name = "tcsr_usb3_1_clkref_en",
+ .offset = 0x68,
},
-};
-
-static struct clk_branch tcsr_usb4_1_clkref_en = {
- .halt_reg = 0x44,
- .halt_check = BRANCH_HALT_DELAY,
- .clkr = {
- .enable_reg = 0x44,
- .enable_mask = BIT(0),
- .hw.init = &(const struct clk_init_data) {
- .name = "tcsr_usb4_1_clkref_en",
- .parent_data = &(const struct clk_parent_data){
- .index = DT_BI_TCXO_PAD,
- },
- .num_parents = 1,
- .ops = &clk_branch2_ops,
- },
+ [TCSR_USB4_1_CLKREF_EN] = {
+ .name = "tcsr_usb4_1_clkref_en",
+ .offset = 0x44,
},
-};
-
-static struct clk_branch tcsr_usb4_2_clkref_en = {
- .halt_reg = 0x5c,
- .halt_check = BRANCH_HALT_DELAY,
- .clkr = {
- .enable_reg = 0x5c,
- .enable_mask = BIT(0),
- .hw.init = &(const struct clk_init_data) {
- .name = "tcsr_usb4_2_clkref_en",
- .parent_data = &(const struct clk_parent_data){
- .index = DT_BI_TCXO_PAD,
- },
- .num_parents = 1,
- .ops = &clk_branch2_ops,
- },
+ [TCSR_USB4_2_CLKREF_EN] = {
+ .name = "tcsr_usb4_2_clkref_en",
+ .offset = 0x5c,
},
};
-static struct clk_regmap *tcsr_cc_glymur_clocks[] = {
- [TCSR_EDP_CLKREF_EN] = &tcsr_edp_clkref_en.clkr,
- [TCSR_PCIE_1_CLKREF_EN] = &tcsr_pcie_1_clkref_en.clkr,
- [TCSR_PCIE_2_CLKREF_EN] = &tcsr_pcie_2_clkref_en.clkr,
- [TCSR_PCIE_3_CLKREF_EN] = &tcsr_pcie_3_clkref_en.clkr,
- [TCSR_PCIE_4_CLKREF_EN] = &tcsr_pcie_4_clkref_en.clkr,
- [TCSR_USB2_1_CLKREF_EN] = &tcsr_usb2_1_clkref_en.clkr,
- [TCSR_USB2_2_CLKREF_EN] = &tcsr_usb2_2_clkref_en.clkr,
- [TCSR_USB2_3_CLKREF_EN] = &tcsr_usb2_3_clkref_en.clkr,
- [TCSR_USB2_4_CLKREF_EN] = &tcsr_usb2_4_clkref_en.clkr,
- [TCSR_USB3_0_CLKREF_EN] = &tcsr_usb3_0_clkref_en.clkr,
- [TCSR_USB3_1_CLKREF_EN] = &tcsr_usb3_1_clkref_en.clkr,
- [TCSR_USB4_1_CLKREF_EN] = &tcsr_usb4_1_clkref_en.clkr,
- [TCSR_USB4_2_CLKREF_EN] = &tcsr_usb4_2_clkref_en.clkr,
-};
-
static const struct regmap_config tcsr_cc_glymur_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
@@ -271,11 +121,12 @@ static const struct regmap_config tcsr_cc_glymur_regmap_config = {
.fast_io = true,
};
-static const struct qcom_cc_desc tcsr_cc_glymur_desc = {
- .config = &tcsr_cc_glymur_regmap_config,
- .clks = tcsr_cc_glymur_clocks,
- .num_clks = ARRAY_SIZE(tcsr_cc_glymur_clocks),
-};
+static int tcsr_cc_glymur_probe(struct platform_device *pdev)
+{
+ return qcom_clk_ref_probe(pdev, &tcsr_cc_glymur_regmap_config,
+ tcsr_cc_glymur_clk_descs,
+ ARRAY_SIZE(tcsr_cc_glymur_clk_descs));
+}
static const struct of_device_id tcsr_cc_glymur_match_table[] = {
{ .compatible = "qcom,glymur-tcsr" },
@@ -283,11 +134,6 @@ static const struct of_device_id tcsr_cc_glymur_match_table[] = {
};
MODULE_DEVICE_TABLE(of, tcsr_cc_glymur_match_table);
-static int tcsr_cc_glymur_probe(struct platform_device *pdev)
-{
- return qcom_cc_probe(pdev, &tcsr_cc_glymur_desc);
-}
-
static struct platform_driver tcsr_cc_glymur_driver = {
.probe = tcsr_cc_glymur_probe,
.driver = {
--
2.34.1
^ permalink raw reply related
* [PATCH v2 4/4] arm64: dts: qcom: glymur: Add QREF regulator supplies to TCSR
From: Qiang Yu @ 2026-04-20 7:42 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Taniya Das, Konrad Dybcio
Cc: linux-arm-msm, linux-clk, devicetree, linux-kernel, Qiang Yu
In-Reply-To: <20260420-vote_qref_in_tcsrcc-v2-0-589a23ae640a@oss.qualcomm.com>
The TCSR clkref clocks gate the QREF block which provides reference
clocks to the PCIe PHYs. Wire up the LDO supplies required by the QREF
and refgen blocks on the CRD board:
- vdda-refgen_0p9/1p2: LDOs for the refgen block that generates the
reference voltage for QREF
- vdda-qrefrx/tx/rpt: LDOs for the QREF receiver, transmitter
and repeater circuits
Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/glymur-crd.dts | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/glymur-crd.dts b/arch/arm64/boot/dts/qcom/glymur-crd.dts
index 35aaf09e4e2b..382398e44296 100644
--- a/arch/arm64/boot/dts/qcom/glymur-crd.dts
+++ b/arch/arm64/boot/dts/qcom/glymur-crd.dts
@@ -358,6 +358,25 @@ &usb_1 {
status = "okay";
};
+&tcsr {
+ vdda-refgen-0p9-supply = <&vreg_l1f_e1_0p82>;
+ vdda-refgen-1p2-supply = <&vreg_l4f_e1_1p08>;
+
+ vdda-qrefrx5-0p9-supply = <&vreg_l3f_e0_0p72>;
+ vdda-qreftx0-0p9-supply = <&vreg_l3f_e0_0p72>;
+ vdda-qreftx0-1p2-supply = <&vreg_l4h_e0_1p2>;
+ vdda-qrefrpt0-0p9-supply = <&vreg_l2f_e1_0p83>;
+ vdda-qrefrpt1-0p9-supply = <&vreg_l2f_e1_0p83>;
+ vdda-qrefrpt2-0p9-supply = <&vreg_l2f_e1_0p83>;
+ vdda-qrefrpt3-0p9-supply = <&vreg_l2h_e0_0p72>;
+ vdda-qrefrpt4-0p9-supply = <&vreg_l2h_e0_0p72>;
+ vdda-qrefrx0-0p9-supply = <&vreg_l2f_e1_0p83>;
+ vdda-qrefrx1-0p9-supply = <&vreg_l2f_e1_0p83>;
+ vdda-qrefrx2-0p9-supply = <&vreg_l2f_e1_0p83>;
+ vdda-qrefrx4-0p9-supply = <&vreg_l2h_e0_0p72>;
+ vdda-qreftx1-0p9-supply = <&vreg_l1f_e1_0p82>;
+};
+
&usb_1_dwc3_hs {
remote-endpoint = <&pmic_glink_hs_in1>;
};
--
2.34.1
^ permalink raw reply related
* [PATCH] dt-bindings: phy: qcom,sc8280xp-qmp-ufs-phy: Document Nord QMP UFS PHY
From: Shawn Guo @ 2026-04-20 7:49 UTC (permalink / raw)
To: Vinod Koul
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio,
Dmitry Baryshkov, Bartosz Golaszewski, Deepti Jaggi, linux-phy,
devicetree, linux-arm-msm, linux-kernel, Shawn Guo
Add compatible for QMP UFS PHY on Qualcomm Nord SoC with a fallback
on qcom,sm8650-qmp-ufs-phy.
Signed-off-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
---
.../devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml
index 9616c736b6d4..cc3457d6aa3b 100644
--- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml
@@ -36,6 +36,10 @@ properties:
- enum:
- qcom,kaanapali-qmp-ufs-phy
- const: qcom,sm8750-qmp-ufs-phy
+ - items:
+ - enum:
+ - qcom,nord-qmp-ufs-phy
+ - const: qcom,sm8650-qmp-ufs-phy
- enum:
- qcom,milos-qmp-ufs-phy
- qcom,msm8996-qmp-ufs-phy
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox