From 5ff7994a0693188329506b9e941565c381a63f14 Mon Sep 17 00:00:00 2001 From: Jiali Chen Date: Sun, 28 Jun 2026 05:52:40 +0800 Subject: [PATCH 01/10] dt-bindings: display: bridge: add LT8712SX document the Lontium LT8712SX and LT8713SX eDP to HDMI bridge. Signed-off-by: Jiali Chen --- .../display/bridge/lontium,lt8712sx.yaml | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/bridge/lontium,lt8712sx.yaml diff --git a/Documentation/devicetree/bindings/display/bridge/lontium,lt8712sx.yaml b/Documentation/devicetree/bindings/display/bridge/lontium,lt8712sx.yaml new file mode 100644 index 0000000000000..e4c00f6916107 --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/lontium,lt8712sx.yaml @@ -0,0 +1,109 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/lontium,lt8712sx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Lontium LT8712SX/LT8713SX eDP to HDMI Bridge + +maintainers: + - Chen Jiali + +properties: + compatible: + enum: + - lontium,lt8712sx + - lontium,lt8713sx + + reg: + maxItems: 1 + + cec-port-names: + $ref: /schemas/types.yaml#/definitions/string-array + description: + HDMI connector port names used to create named CEC notifiers for boards + that route multiple HDMI CEC lines to GPIO CEC adapters. + minItems: 1 + maxItems: 2 + uniqueItems: true + + enable-gpios: + maxItems: 1 + + power-gpios: + maxItems: 1 + + pinctrl-0: true + + pinctrl-names: + items: + - const: default + + reset-gpios: + maxItems: 1 + + vdd-supply: + description: Power supply for the bridge. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: eDP input port. + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: HDMI output port. + + required: + - port@0 + - port@1 + +required: + - compatible + - reg + - ports + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + hdmi-bridge@4f { + compatible = "lontium,lt8712sx"; + reg = <0x4f>; + cec-port-names = "HDMI0", "HDMI1"; + vdd-supply = <&vcc_3v3>; + reset-gpios = <&gpio 1 GPIO_ACTIVE_LOW>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + bridge_in: endpoint { + remote-endpoint = <&edp_out>; + }; + }; + + port@1 { + reg = <1>; + + bridge_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; + }; + }; + }; + }; + +... From 7ea0bb48e54208909fd0937d90b7bba05d002ec5 Mon Sep 17 00:00:00 2001 From: Jiali Chen Date: Sun, 28 Jun 2026 05:53:41 +0800 Subject: [PATCH 02/10] drm/bridge: lontium-lt8712sx: fix reset polarity drive the optional reset GPIO with the active-low polarity used by the bridge and leave it deasserted after reset. Signed-off-by: Jiali Chen --- drivers/gpu/drm/bridge/lontium-lt8712sx.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/bridge/lontium-lt8712sx.c b/drivers/gpu/drm/bridge/lontium-lt8712sx.c index a2da205c7f4e7..bd86d2b3a8588 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8712sx.c +++ b/drivers/gpu/drm/bridge/lontium-lt8712sx.c @@ -231,7 +231,7 @@ static int lt8712sx_power_on(struct lt8712sx *lt8712sx) gpiod_set_value_cansleep(lt8712sx->power_gpio, 1); if (lt8712sx->reset_gpio) - gpiod_set_value_cansleep(lt8712sx->reset_gpio, 1); + gpiod_set_value_cansleep(lt8712sx->reset_gpio, 0); lt8712sx->powered = true; @@ -250,7 +250,7 @@ static void lt8712sx_power_off_action(void *data) gpiod_set_value_cansleep(lt8712sx->enable_gpio, 0); if (lt8712sx->reset_gpio) - gpiod_set_value_cansleep(lt8712sx->reset_gpio, 0); + gpiod_set_value_cansleep(lt8712sx->reset_gpio, 1); if (lt8712sx->power_gpio) gpiod_set_value_cansleep(lt8712sx->power_gpio, 0); @@ -268,12 +268,12 @@ static int lt8712sx_hw_reset(struct lt8712sx *lt8712sx) if (!lt8712sx->reset_gpio) return 0; - gpiod_set_value_cansleep(lt8712sx->reset_gpio, 1); - msleep(5); gpiod_set_value_cansleep(lt8712sx->reset_gpio, 0); - msleep(5); + msleep(100); gpiod_set_value_cansleep(lt8712sx->reset_gpio, 1); - msleep(5); + msleep(100); + gpiod_set_value_cansleep(lt8712sx->reset_gpio, 0); + msleep(100); return 0; } @@ -1005,7 +1005,7 @@ static int lt8712sx_probe(struct i2c_client *client) "failed to get power GPIO\n"); lt8712sx->reset_gpio = devm_gpiod_get_optional(dev, "reset", - GPIOD_OUT_LOW); + GPIOD_OUT_HIGH); if (IS_ERR(lt8712sx->reset_gpio)) return dev_err_probe(dev, PTR_ERR(lt8712sx->reset_gpio), "failed to get reset GPIO\n"); From c95b3a727f9d7e3ec2d087769f9fd7f024b82c05 Mon Sep 17 00:00:00 2001 From: Jiali Chen Date: Sun, 28 Jun 2026 05:53:57 +0800 Subject: [PATCH 03/10] drm/bridge: lontium-lt8712sx: add CEC notifier support register named CEC notifiers for LT8712SX bridges and update the physical address on HPD and EDID changes. Signed-off-by: Jiali Chen --- drivers/gpu/drm/bridge/Kconfig | 1 + drivers/gpu/drm/bridge/lontium-lt8712sx.c | 165 +++++++++++++++++++++- 2 files changed, 165 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index b270ee1992189..b722992f0c5a7 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -194,6 +194,7 @@ config DRM_LONTIUM_LT9611UXC config DRM_LONTIUM_LT8712SX tristate "Lontium LT8712SX DP/eDP to HDMI bridge" depends on OF + select CEC_CORE if CEC_NOTIFIER select DRM_KMS_HELPER select REGMAP_I2C help diff --git a/drivers/gpu/drm/bridge/lontium-lt8712sx.c b/drivers/gpu/drm/bridge/lontium-lt8712sx.c index bd86d2b3a8588..d028f12335885 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8712sx.c +++ b/drivers/gpu/drm/bridge/lontium-lt8712sx.c @@ -15,9 +15,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -25,6 +27,8 @@ #include #include +#include + #define LT8712SX_PAGE_SIZE 256 #define LT8712SX_MAIN_FW_SIZE SZ_64K #define LT8712SX_MAIN_FW_PAYLOAD_SIZE (LT8712SX_MAIN_FW_SIZE - 1) @@ -44,6 +48,8 @@ #define LT8712SX_STATUS_POLL_DELAY_MS 50 #define LT8712SX_STATUS_POLL_RETRIES 50 +#define LT8712SX_MAX_CEC_PORTS 2 + struct lt8712sx_info { unsigned int connector_type; const char *firmware_name; @@ -85,6 +91,11 @@ struct lt8712sx { struct gpio_desc *enable_gpio; struct mutex lock; bool powered; + + const char *cec_port_names[LT8712SX_MAX_CEC_PORTS]; + struct cec_notifier *cec_notifiers[LT8712SX_MAX_CEC_PORTS]; + unsigned int cec_notifier_count; + bool cec_port_names_initialized; }; static inline struct lt8712sx *bridge_to_lt8712sx(struct drm_bridge *bridge) @@ -98,6 +109,14 @@ static inline struct lt8712sx *connector_to_lt8712sx( return container_of(connector, struct lt8712sx, connector); } +static void lt8712sx_set_phys_addr(struct lt8712sx *lt8712sx, u16 pa) +{ + unsigned int i; + + for (i = 0; i < lt8712sx->cec_notifier_count; i++) + cec_notifier_set_phys_addr(lt8712sx->cec_notifiers[i], pa); +} + static const struct regmap_config lt8712sx_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -846,6 +865,7 @@ static int lt8712sx_connector_get_modes(struct drm_connector *connector) { struct lt8712sx *lt8712sx = connector_to_lt8712sx(connector); const struct drm_edid *drm_edid; + u16 phys_addr; int ret; if (lt8712sx->next_bridge->ops & DRM_BRIDGE_OP_EDID) { @@ -863,12 +883,17 @@ static int lt8712sx_connector_get_modes(struct drm_connector *connector) * If the downstream HDMI connector does not expose DDC to the SoC, * keep the same no-EDID fallback used by simple-bridge. */ + lt8712sx_set_phys_addr(lt8712sx, CEC_PHYS_ADDR_INVALID); ret = drm_add_modes_noedid(connector, 1920, 1200); drm_set_preferred_mode(connector, 1024, 768); return ret; } ret = drm_edid_connector_add_modes(connector); + + phys_addr = connector->display_info.source_physical_address; + lt8712sx_set_phys_addr(lt8712sx, phys_addr); + drm_edid_free(drm_edid); return ret; @@ -882,8 +907,14 @@ static enum drm_connector_status lt8712sx_connector_detect(struct drm_connector *connector, bool force) { struct lt8712sx *lt8712sx = connector_to_lt8712sx(connector); + enum drm_connector_status status; - return drm_bridge_detect(lt8712sx->next_bridge, connector); + status = drm_bridge_detect(lt8712sx->next_bridge, connector); + + if (status != connector_status_connected) + lt8712sx_set_phys_addr(lt8712sx, CEC_PHYS_ADDR_INVALID); + + return status; } static const struct drm_connector_funcs lt8712sx_con_funcs = { @@ -895,6 +926,103 @@ static const struct drm_connector_funcs lt8712sx_con_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; +static void lt8712sx_cec_notifiers_unregister(void *data); + +static int lt8712sx_init_cec_port_names(struct lt8712sx *lt8712sx) +{ + struct device *dev = lt8712sx->dev; + int count; + + if (!device_property_present(dev, "cec-port-names")) { + lt8712sx->cec_notifier_count = 1; + lt8712sx->cec_port_names_initialized = true; + return 0; + } + + if (lt8712sx->cec_port_names_initialized) + return 0; + + count = device_property_string_array_count(dev, "cec-port-names"); + if (count < 0) + return dev_err_probe(dev, count, + "failed to read CEC port names\n"); + + if (count == 0 || count > LT8712SX_MAX_CEC_PORTS) + return dev_err_probe(dev, -EINVAL, + "invalid number of CEC port names\n"); + + count = device_property_read_string_array(dev, "cec-port-names", + lt8712sx->cec_port_names, count); + if (count < 0) + return dev_err_probe(dev, count, + "failed to read CEC port names\n"); + + if (count == 2 && + !strcmp(lt8712sx->cec_port_names[0], lt8712sx->cec_port_names[1])) + return dev_err_probe(dev, -EINVAL, + "duplicate CEC port names\n"); + + lt8712sx->cec_notifier_count = count; + lt8712sx->cec_port_names_initialized = true; + + return 0; +} + +static int lt8712sx_register_cec_notifiers(struct lt8712sx *lt8712sx, + struct drm_connector *connector) +{ + struct cec_connector_info conn_info; + unsigned int i; + int ret; + + ret = lt8712sx_init_cec_port_names(lt8712sx); + if (ret) + return ret; + + cec_fill_conn_info_from_drm(&conn_info, connector); + + for (i = 0; i < lt8712sx->cec_notifier_count; i++) { + if (lt8712sx->cec_notifiers[i]) + continue; + + lt8712sx->cec_notifiers[i] = + cec_notifier_conn_register(lt8712sx->dev, + lt8712sx->cec_port_names[i], + &conn_info); + if (!lt8712sx->cec_notifiers[i]) { + DRM_ERROR("Failed to register CEC notifier\n"); + ret = -ENOMEM; + goto unregister_notifiers; + } + } + + lt8712sx_set_phys_addr(lt8712sx, connector->display_info.source_physical_address); + + return 0; + +unregister_notifiers: + while (i--) { + cec_notifier_conn_unregister(lt8712sx->cec_notifiers[i]); + lt8712sx->cec_notifiers[i] = NULL; + } + lt8712sx->cec_notifier_count = 0; + + return ret; +} + +static int lt8712sx_init_cec_notifiers(struct lt8712sx *lt8712sx) +{ + int ret; + + ret = lt8712sx_init_cec_port_names(lt8712sx); + if (ret) + return ret; + + return devm_add_action_or_reset(lt8712sx->dev, + lt8712sx_cec_notifiers_unregister, + lt8712sx); +} + static int lt8712sx_attach(struct drm_bridge *bridge, struct drm_encoder *encoder, enum drm_bridge_attach_flags flags) @@ -927,6 +1055,34 @@ static int lt8712sx_attach(struct drm_bridge *bridge, return 0; } +static void +lt8712sx_hpd(struct drm_bridge *bridge, struct drm_connector *connector, + enum drm_connector_status status) +{ + struct lt8712sx *lt8712sx = bridge_to_lt8712sx(bridge); + + if (status != connector_status_connected) { + lt8712sx_set_phys_addr(lt8712sx, CEC_PHYS_ADDR_INVALID); + return; + } + + if (lt8712sx_register_cec_notifiers(lt8712sx, connector)) + lt8712sx_set_phys_addr(lt8712sx, CEC_PHYS_ADDR_INVALID); +} + +static void lt8712sx_cec_notifiers_unregister(void *data) +{ + struct lt8712sx *lt8712sx = data; + unsigned int i; + + for (i = 0; i < lt8712sx->cec_notifier_count; i++) { + cec_notifier_conn_unregister(lt8712sx->cec_notifiers[i]); + lt8712sx->cec_notifiers[i] = NULL; + } + + lt8712sx->cec_notifier_count = 0; +} + static void lt8712sx_enable(struct drm_bridge *bridge) { struct lt8712sx *lt8712sx = bridge_to_lt8712sx(bridge); @@ -945,6 +1101,7 @@ static void lt8712sx_disable(struct drm_bridge *bridge) static const struct drm_bridge_funcs lt8712sx_bridge_funcs = { .attach = lt8712sx_attach, + .hpd_notify = lt8712sx_hpd, .enable = lt8712sx_enable, .disable = lt8712sx_disable, }; @@ -1026,8 +1183,14 @@ static int lt8712sx_probe(struct i2c_client *client) lt8712sx_try_optional_firmware(lt8712sx); + ret = lt8712sx_init_cec_notifiers(lt8712sx); + if (ret) + return ret; + lt8712sx->bridge.of_node = dev->of_node; + lt8712sx->bridge.ops = DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER; lt8712sx->bridge.type = lt8712sx->info->connector_type; + lt8712sx->bridge.hdmi_cec_dev = dev; return devm_drm_bridge_add(dev, <8712sx->bridge); } From b85c1fe1c3fbbbe2645a25295e58afd60852bb02 Mon Sep 17 00:00:00 2001 From: Jiali Chen Date: Sun, 28 Jun 2026 05:54:35 +0800 Subject: [PATCH 04/10] dt-bindings: media: cec-gpio: add port-name property allow GPIO CEC adapters to name the connector notifier they bind to through the port-name property. Signed-off-by: Jiali Chen --- .../devicetree/bindings/media/cec/cec-gpio.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/devicetree/bindings/media/cec/cec-gpio.yaml b/Documentation/devicetree/bindings/media/cec/cec-gpio.yaml index 582c6c9cae48d..64d0a948e56b8 100644 --- a/Documentation/devicetree/bindings/media/cec/cec-gpio.yaml +++ b/Documentation/devicetree/bindings/media/cec/cec-gpio.yaml @@ -40,6 +40,13 @@ properties: GPIO that the 5V line is connected to. Used for debugging changes on the 5V line. + port-name: + $ref: /schemas/types.yaml#/definitions/string + description: + HDMI connector port name used with hdmi-phandle to match this GPIO CEC + adapter to a named HDMI controller notifier when that controller exposes + multiple CEC-capable connectors. + required: - compatible - cec-gpios @@ -60,6 +67,13 @@ allOf: properties: hdmi-phandle: false + - if: + required: + - port-name + then: + required: + - hdmi-phandle + unevaluatedProperties: false examples: From 8af306d512f9abaddedbb795ce3489296bfc7e3b Mon Sep 17 00:00:00 2001 From: Jiali Chen Date: Sun, 28 Jun 2026 05:54:35 +0800 Subject: [PATCH 05/10] media: cec: gpio: add named connector support read the optional port-name property and pass it to CEC notifier registration so adapters can bind to a named connector. Signed-off-by: Jiali Chen --- drivers/media/cec/platform/cec-gpio/cec-gpio.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/media/cec/platform/cec-gpio/cec-gpio.c b/drivers/media/cec/platform/cec-gpio/cec-gpio.c index 842555ed42c71..a42a6f346f846 100644 --- a/drivers/media/cec/platform/cec-gpio/cec-gpio.c +++ b/drivers/media/cec/platform/cec-gpio/cec-gpio.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -177,6 +178,7 @@ static int cec_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device *hdmi_dev; + const char *port_name = NULL; struct cec_gpio *cec; u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN; int ret; @@ -184,8 +186,14 @@ static int cec_gpio_probe(struct platform_device *pdev) hdmi_dev = cec_notifier_parse_hdmi_phandle(dev); if (PTR_ERR(hdmi_dev) == -EPROBE_DEFER) return PTR_ERR(hdmi_dev); - if (IS_ERR(hdmi_dev)) + if (IS_ERR(hdmi_dev)) { caps |= CEC_CAP_PHYS_ADDR; + } else if (device_property_present(dev, "port-name")) { + caps |= CEC_CAP_CONNECTOR_INFO; + ret = device_property_read_string(dev, "port-name", &port_name); + if (ret) + return ret; + } cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL); if (!cec) @@ -242,7 +250,7 @@ static int cec_gpio_probe(struct platform_device *pdev) } if (!IS_ERR(hdmi_dev)) { - cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL, + cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, port_name, cec->adap); if (!cec->notifier) { ret = -ENOMEM; From ee3684e414d76090890e0c0fa92407033808a958 Mon Sep 17 00:00:00 2001 From: Jiali Chen Date: Sun, 28 Jun 2026 05:54:35 +0800 Subject: [PATCH 06/10] arm64: dts: qcom: qcs6490-radxa-cm-q64: update v1.1 hardware update the CM-Q64 description to follow the v1.1 hardware revision. the fan driver circuit is on the core module, so move the fan PWM, tachometer and thermal cooling nodes from the Raspberry Pi CM5 IO carrier DTS into the shared CM-Q64 DTSI. add the L5C regulator because GPIO110 belongs to that power domain. Signed-off-by: Jiali Chen --- .../qcom/qcs6490-radxa-cm-q64-rpi-cm5-io.dts | 83 ----------- .../boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi | 135 +++++++++++++++++- 2 files changed, 132 insertions(+), 86 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64-rpi-cm5-io.dts b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64-rpi-cm5-io.dts index ce445191c5322..0238b96c6e6d5 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64-rpi-cm5-io.dts +++ b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64-rpi-cm5-io.dts @@ -10,82 +10,6 @@ / { model = "Radxa CM-Q64 Raspberry Pi Compute Module 5 IO Board"; compatible = "radxa,cm-q64-rpi-cm5-io", "radxa,cm-q64", "qcom,qcm6490"; - - fan_pwm: fan-pwm { - compatible = "pwm-gpio"; - gpios = <&tlmm 106 GPIO_ACTIVE_HIGH>; - pinctrl-0 = <&fan_pwm_cpu>; - pinctrl-names = "default"; - #pwm-cells = <3>; - }; - - pwm_fan: pwm-fan { - pinctrl-0 = <&fan_tacho>; - pinctrl-names = "default"; - compatible = "pwm-fan"; - pwms = <&fan_pwm 0 20000 0>; - fan-supply = <&vcc_3v3>; - interrupt-parent = <&pm8350c_gpios>; - interrupts = <5 IRQ_TYPE_EDGE_FALLING>; - #cooling-cells = <2>; - cooling-levels = <0 120 150 180 210 240 255>; - }; -}; - -&pm8350c_gpios { - gpio-line-names = "", /* 1 */ - "", - "", - "", - "FAN_TACHO_IN", - "", - "LCD_BLEN", - "LCD_BLPWM", - ""; - - fan_tacho: fan-tacho-state { - pins = "gpio5"; - function = PMIC_GPIO_FUNC_NORMAL; - bias-disable; - power-source = <1>; - }; -}; - - -&thermal_zones { - cpuss0-thermal { - trips { - cpuss0_active: trip-point2 { - temperature = <60000>; - hysteresis = <2000>; - type = "active"; - }; - }; - - cooling-maps { - map2 { - trip = <&cpuss0_active>; - cooling-device = <&pwm_fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; - }; - }; - - cpuss1-thermal { - trips { - cpuss1_active: trip-point2 { - temperature = <60000>; - hysteresis = <2000>; - type = "active"; - }; - }; - - cooling-maps { - map2 { - trip = <&cpuss1_active>; - cooling-device = <&pwm_fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; - }; - }; }; &tlmm { @@ -182,11 +106,4 @@ "", "", "", "", /* GPIO_172 ~ GPIO_174 */ "", "", ""; - - fan_pwm_cpu: fan-ctr-pwm-state { - pins = "gpio106"; - function = "gpio"; - bias-disable; - drive-strength = <16>; - }; }; diff --git a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi index 86ef62c5dc9bd..3227a9e952029 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi @@ -45,6 +45,26 @@ stdout-path = "serial0:115200n8"; }; + fan_pwm: fan-pwm { + compatible = "pwm-gpio"; + gpios = <&tlmm 108 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&fan_pwm_cpu>; + pinctrl-names = "default"; + #pwm-cells = <3>; + }; + + pwm_fan: pwm-fan { + pinctrl-0 = <&fan_tacho>; + pinctrl-names = "default"; + compatible = "pwm-fan"; + pwms = <&fan_pwm 0 20000 0>; + fan-supply = <&vcc_3v3>; + interrupt-parent = <&pm8350c_gpios>; + interrupts = <5 IRQ_TYPE_EDGE_FALLING>; + #cooling-cells = <2>; + cooling-levels = <0 120 150 180 210 240 255>; + }; + wcd938x: audio-codec { compatible = "qcom,wcd9380-codec"; @@ -143,6 +163,26 @@ }; }; + /* HDMI0 CEC via GPIO_45 (HDMI0_CEC_CPU), bit-banged through TXS0108 level shifter */ + cec-0 { + compatible = "cec-gpio"; + cec-gpios = <&tlmm 45 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + hdmi-phandle = <<8712sx>; + port-name = "HDMI0"; + pinctrl-0 = <&cec0_gpio_default>; + pinctrl-names = "default"; + }; + + /* HDMI1 CEC via GPIO_46 (HDMI1_CEC_CPU), bit-banged through TXS0108 level shifter */ + cec-1 { + compatible = "cec-gpio"; + cec-gpios = <&tlmm 46 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + hdmi-phandle = <<8712sx>; + port-name = "HDMI1"; + pinctrl-0 = <&cec1_gpio_default>; + pinctrl-names = "default"; + }; + leds { compatible = "gpio-leds"; @@ -266,8 +306,6 @@ regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; vin-supply = <&vreg_l7b_2p96>; - - regulator-always-on; }; vbus: regulator-vbus { @@ -430,6 +468,18 @@ RPMH_REGULATOR_MODE_HPM>; }; + vreg_l5c_1p62: ldo5 { + regulator-name = "vreg_l5c_1p62"; + regulator-min-microvolt = <1620000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + + regulator-always-on; + }; + vreg_l6c_2p96: ldo6 { regulator-name = "vreg_l6c_2p96"; regulator-min-microvolt = <1650000>; @@ -508,6 +558,11 @@ lt8712sx: hdmi-edp-bridge@4f { compatible = "lontium,lt8712sx"; reg = <0x4f>; + cec-port-names = "HDMI0", "HDMI1"; + vdd-supply = <&vcc_3v3s>; + reset-gpios = <&tlmm 110 GPIO_ACTIVE_LOW>; + pinctrl-0 = <<8712sx_reset_default>; + pinctrl-names = "default"; ports { #address-cells = <1>; @@ -541,6 +596,7 @@ rtc: rtc@68 { compatible = "st,m41t11"; reg = <0x68>; + vcc-supply = <&vcc_3v3>; }; }; @@ -690,12 +746,19 @@ "", "", "", - "", + "FAN_TACHO_IN", "", "LCD_BLEN", "LCD_BLPWM", ""; + fan_tacho: fan-tacho-state { + pins = "gpio5"; + function = PMIC_GPIO_FUNC_NORMAL; + bias-disable; + power-source = <1>; + }; + lcd_bl_en: lcd-bl-en-state { pins = "gpio7"; function = PMIC_GPIO_FUNC_NORMAL; @@ -902,7 +965,73 @@ }; }; +&thermal_zones { + cpuss0-thermal { + polling-delay-passive = <250>; + trips { + cpuss0_active: trip-point2 { + temperature = <60000>; + hysteresis = <10000>; + type = "passive"; + }; + }; + + cooling-maps { + map2 { + trip = <&cpuss0_active>; + cooling-device = <&pwm_fan 0 THERMAL_NO_LIMIT>; + }; + }; + }; + + cpuss1-thermal { + polling-delay-passive = <250>; + trips { + cpuss1_active: trip-point2 { + temperature = <60000>; + hysteresis = <10000>; + type = "passive"; + }; + }; + + cooling-maps { + map2 { + trip = <&cpuss1_active>; + cooling-device = <&pwm_fan 0 THERMAL_NO_LIMIT>; + }; + }; + }; +}; + &tlmm { + fan_pwm_cpu: fan-ctr-pwm-state { + pins = "gpio108"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + lt8712sx_reset_default: lt8712sx-reset-default-state { + pins = "gpio110"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + cec0_gpio_default: cec0-gpio-default-state { + pins = "gpio45"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + cec1_gpio_default: cec1-gpio-default-state { + pins = "gpio46"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + pcie0_reset_n: pcie0-reset-n-state { pins = "gpio87"; function = "gpio"; From 4fbfe95314dcb8c4785644f8b28fdf4a49eaf7c8 Mon Sep 17 00:00:00 2001 From: Jiali Chen Date: Mon, 13 Jul 2026 16:12:41 +0800 Subject: [PATCH 07/10] arm64: dts: qcom: radxa-cm-q64: refine fan levels Replace the coarse high-duty-cycle table with evenly spaced steps from 0 to 255. This gives the thermal governor finer control over fan speed across the full PWM range. Signed-off-by: Jiali Chen --- arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi index 3227a9e952029..c3eb12fe20073 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi @@ -62,7 +62,7 @@ interrupt-parent = <&pm8350c_gpios>; interrupts = <5 IRQ_TYPE_EDGE_FALLING>; #cooling-cells = <2>; - cooling-levels = <0 120 150 180 210 240 255>; + cooling-levels = <0 31 63 95 127 159 191 223 255>; }; wcd938x: audio-codec { From 1b38ca939a21602ff809ee28b5ac5018c479c774 Mon Sep 17 00:00:00 2001 From: Jiali Chen Date: Mon, 13 Jul 2026 16:12:41 +0800 Subject: [PATCH 08/10] arm64: dts: qcom: radxa-cm-q64: model power supplies The module is powered from a 5 V DC input, which feeds the 5 V system rail and the 4.3 V PMIC input rail. Model that path and describe the PCIe analog and 3.3 V supplies. On the CM5 IO board, model the GPIO-controlled M.2 3.3 V rail. Signed-off-by: Jiali Chen --- .../qcom/qcs6490-radxa-cm-q64-rpi-cm5-io.dts | 23 ++++++++++++++++ .../boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi | 27 +++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64-rpi-cm5-io.dts b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64-rpi-cm5-io.dts index 0238b96c6e6d5..0fc7c6fa16882 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64-rpi-cm5-io.dts +++ b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64-rpi-cm5-io.dts @@ -10,6 +10,23 @@ / { model = "Radxa CM-Q64 Raspberry Pi Compute Module 5 IO Board"; compatible = "radxa,cm-q64-rpi-cm5-io", "radxa,cm-q64", "qcom,qcm6490"; + + m2_3v3: regulator-m2-3v3 { + compatible = "regulator-fixed"; + regulator-name = "m2_3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + pinctrl-names = "default"; + pinctrl-0 = <&pcieb_pwr_en>; + gpio = <&tlmm 10 GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v_dcin>; + }; +}; + +&pcie1 { + vddpe-3v3-supply = <&m2_3v3>; + vdda-supply = <&vreg_l6b_1p2>; }; &tlmm { @@ -106,4 +123,10 @@ "", "", "", "", /* GPIO_172 ~ GPIO_174 */ "", "", ""; + + pcieb_pwr_en: pinctrl-pcie1-pwr-en { + pins = "gpio10"; + function = "gpio"; + bias-disable; + }; }; diff --git a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi index c3eb12fe20073..544357f67c40c 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi @@ -316,11 +316,31 @@ vin-supply = <&vcc_3v3>; }; + vdd_5v_dcin: regulator-5v-dcin { + compatible = "regulator-fixed"; + regulator-name = "vdd_5v_dcin"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + + regulator-always-on; + }; + + vdd_5v_in: regulator-5v-in { + compatible = "regulator-fixed"; + regulator-name = "vdd_5v_in"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vdd_5v_dcin>; + + regulator-always-on; + }; + vph_pwr: regulator-vph-pwr { compatible = "regulator-fixed"; regulator-name = "vph_pwr"; - regulator-min-microvolt = <3700000>; - regulator-max-microvolt = <3700000>; + regulator-min-microvolt = <4300000>; + regulator-max-microvolt = <4300000>; + vin-supply = <&vdd_5v_in>; regulator-always-on; }; @@ -650,6 +670,9 @@ }; &pcie0 { + vddpe-3v3-supply = <&vcc_3v3>; + vdda-supply = <&vreg_l6b_1p2>; + perst-gpios = <&tlmm 87 GPIO_ACTIVE_LOW>; wake-gpios = <&tlmm 89 GPIO_ACTIVE_HIGH>; From f19a52a527f0e412c42193d545e91efee24dff45 Mon Sep 17 00:00:00 2001 From: Jiali Chen Date: Mon, 13 Jul 2026 16:13:08 +0800 Subject: [PATCH 09/10] arm64: dts: qcom: radxa-cm-q64: increase CEC drive strength The two CEC GPIOs show an elevated communication error rate with 2 mA drive strength. Raise both pins to 16 mA to improve signal integrity. Signed-off-by: Jiali Chen --- arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi index 544357f67c40c..11a206e6bcbb5 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi @@ -1044,14 +1044,14 @@ cec0_gpio_default: cec0-gpio-default-state { pins = "gpio45"; function = "gpio"; - drive-strength = <2>; + drive-strength = <16>; bias-disable; }; cec1_gpio_default: cec1-gpio-default-state { pins = "gpio46"; function = "gpio"; - drive-strength = <2>; + drive-strength = <16>; bias-disable; }; From 4307e8f3b92aaa4c8c2a2f1ac05e72265678e82b Mon Sep 17 00:00:00 2001 From: Jiali Chen Date: Mon, 13 Jul 2026 16:13:08 +0800 Subject: [PATCH 10/10] arm64: dts: qcom: radxa-cm-q64: update thermal policy The smaller CM-Q64 form factor reaches its thermal limits sooner than the reference platform. Lower the CPU and GPU passive trip points and poll the cluster sensors more frequently. Add per-CPU idle injection as a stronger mitigation stage while keeping fan cooling active from the first cluster alert. Signed-off-by: Jiali Chen --- .../boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi | 193 ++++++++++++++++-- 1 file changed, 181 insertions(+), 12 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi index 11a206e6bcbb5..519104267c803 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi @@ -990,40 +990,209 @@ &thermal_zones { cpuss0-thermal { - polling-delay-passive = <250>; + polling-delay-passive = <100>; + trips { - cpuss0_active: trip-point2 { - temperature = <60000>; + cpuss0_alert1: trip-point1 { + temperature = <65000>; hysteresis = <10000>; type = "passive"; }; + + cpuss0_alert2: trip-point2 { + temperature = <80000>; + hysteresis = <5000>; + type = "passive"; + }; }; cooling-maps { - map2 { - trip = <&cpuss0_active>; - cooling-device = <&pwm_fan 0 THERMAL_NO_LIMIT>; + map0 { + trip = <&cpuss0_alert1>; + cooling-device = <&pwm_fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&idle_cooling0 0 0>, + <&idle_cooling1 0 0>, + <&idle_cooling2 0 0>, + <&idle_cooling3 0 0>; + }; + + map1 { + trip = <&cpuss0_alert2>; + cooling-device = <&idle_cooling0 60 90>, + <&idle_cooling1 60 90>, + <&idle_cooling2 60 90>, + <&idle_cooling3 60 90>; }; }; }; cpuss1-thermal { - polling-delay-passive = <250>; + polling-delay-passive = <100>; + trips { - cpuss1_active: trip-point2 { - temperature = <60000>; + cpuss1_alert1: trip-point1 { + temperature = <65000>; hysteresis = <10000>; type = "passive"; }; + + cpuss1_alert2: trip-point2 { + temperature = <80000>; + hysteresis = <5000>; + type = "passive"; + }; }; + cooling-maps { + map0 { + trip = <&cpuss1_alert1>; + cooling-device = <&pwm_fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&idle_cooling4 0 0>, + <&idle_cooling5 0 0>, + <&idle_cooling6 0 0>, + <&idle_cooling7 0 0>; + }; + map1 { + trip = <&cpuss1_alert2>; + cooling-device = <&idle_cooling4 40 90>, + <&idle_cooling5 60 90>, + <&idle_cooling6 60 90>, + <&idle_cooling7 60 90>; + }; + }; + }; + + gpuss0-thermal { cooling-maps { - map2 { - trip = <&cpuss1_active>; - cooling-device = <&pwm_fan 0 THERMAL_NO_LIMIT>; + map0 { + trip = <&gpuss0_alert0>; + cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; }; }; }; + + gpuss1-thermal { + cooling-maps { + map0 { + trip = <&gpuss1_alert0>; + cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; +}; + +&gpuss0_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&gpuss1_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu0_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu1_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu2_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu3_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu4_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu5_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu6_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu7_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu0 { + idle_cooling0: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; +}; + +&cpu1 { + idle_cooling1: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; +}; + +&cpu2 { + idle_cooling2: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; +}; + +&cpu3 { + idle_cooling3: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; +}; + +&cpu4 { + idle_cooling4: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; +}; + +&cpu5 { + idle_cooling5: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; +}; + +&cpu6 { + idle_cooling6: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; +}; + +&cpu7 { + idle_cooling7: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; }; &tlmm {