* [PATCH v5 1/6] arm64: defconfig: Enable Allwinner LRADC input driver
From: Alexander Sverdlin @ 2026-06-23 20:48 UTC (permalink / raw)
To: linux-arm-kernel, linux-sunxi
Cc: Alexander Sverdlin, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Hans de Goede, Dmitry Torokhov, Andre Przywara, Jun Yan,
Lukas Schmid, J. Neuschäfer, Eric Biggers, Michal Simek,
Luca Weiss, Sven Peter, Maxime Ripard, devicetree, linux-kernel,
linux-input
In-Reply-To: <20260623204824.691832-1-alexander.sverdlin@gmail.com>
Enable Allwinner LRADC input driver as module to support buttons on Baijie
HelperBoard A133.
Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Signed-off-by: Alexander Sverdlin <alexander.sverdlin@gmail.com>
---
Changelog:
v4-v5:
- no changes
v3:
- new patch
arch/arm64/configs/defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 654a102cb5bc..c267f0906460 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -527,6 +527,7 @@ CONFIG_KEYBOARD_GPIO=y
CONFIG_KEYBOARD_GPIO_POLLED=m
CONFIG_KEYBOARD_SNVS_PWRKEY=m
CONFIG_KEYBOARD_IMX_SC_KEY=m
+CONFIG_KEYBOARD_SUN4I_LRADC=m
CONFIG_KEYBOARD_CROS_EC=y
CONFIG_KEYBOARD_MTK_PMIC=m
CONFIG_MOUSE_ELAN_I2C=m
--
2.54.0
^ permalink raw reply related
* [PATCH v5 0/6] Add support for Baijie Helper A133 board
From: Alexander Sverdlin @ 2026-06-23 20:48 UTC (permalink / raw)
To: linux-arm-kernel, linux-sunxi
Cc: Alexander Sverdlin, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Hans de Goede, Dmitry Torokhov, Andre Przywara, Jun Yan,
Lukas Schmid, J. Neuschäfer, Eric Biggers, Michal Simek,
Luca Weiss, Sven Peter, Maxime Ripard, devicetree, linux-kernel,
linux-input
Baijie Helper A133 board is a development board around Baijie A133 Core
SBC. Features:
- 1/2/4GiB LPDDR4 DRAM
- 8/16/32GiB eMMC
- AXP707 PMIC
- USB-C OTG port in peripheral mode (via onboard hub)
- 2 USB 2.0 ports
- MicroSD slot and on-board eMMC module
- Gigabit Ethernet
- Bluetooth
- WiFi
Add initial support for both the Helper and Core boards, including UART,
PMU, eMMC, USB, Ethernet, LRADC-connected buttons.
UART1 can only be used for Bluetooth module, but BT-WiFi combo Allwinner
AW869A chip has not mainline driver currently.
Link: https://szbaijie.com/index/product/product_detail.html?product_id=23&language=en
Changelog:
v5:
- sun50i-a100.dtsi: reflowed "compatible" property of lradc node
- dropped "reserve RAM for ATF" patch [v4 6/7]
v4:
- reserve RAM for ATF
- sun4i-lradc-keys: Add A100/A133 compatible
- dt-bindings: renamed "Baijie Helper A133" -> "Baijie A133 HelperBoard"
- dt-bindings: renamed "baijie,helper-a133" -> "baijie,helperboard-a133"
- dt-bindings: introduced allwinner,sun50i-a100-lradc
- reserve RAM for ATF
- renamed "sun50i-a133-baijie-helper.dtb" -> "sun50i-a133-helperboard.dtb"
- added "model" property into root of sun50i-a133-helperboard-core.dtsi
- added "cap-mmc-highspeed" and "max-frequency" into &mmc2
- added "x-powers,drive-vbus-en" and "*-supply" into &axp803
- dropped all "regulator-enable-ramp-delay" properties
- replaced ®_dcdc3 with a "polyphased" comment
- exact DRAM voltage in ®_dcdc5
- disabled ®_dcdc6 to avoid "[ 31.710641] dcdc6: disabling"
- added ®_vdd5v "root" regulator
- added "disable-wp" into &mmc0
- commented &usb_otg
- assigned usb1_vbus-supply in &usbphy
- https://lore.kernel.org/all/20260605070923.3045073-1-alexander.sverdlin@gmail.com/
v3:
- added lradc node to sun50i-a100.dtsi
- enabled LRADC driver in arm64 defconfig
- added my copyrights into the newly introduced DTs
- all DT nodes sorted alphabetically
- all always-on regulators commented/propetly named
- all regulators got proper voltages (not default ranges)
- ADC-sensed buttons K1..K5 added
- re-labelled "eth_phy" -> "rgmii_phy"
- usbphy 0 switched from host into peripheral mode (downstream from an
onboard hub)
- typo sun50i-a133-baije-core.dtsi -> sun50i-a133-baijie-core.dtsi
- https://lore.kernel.org/all/20260517234134.2737320-1-alexander.sverdlin@gmail.com/
v2:
- introduced baijie,helper-a133-core compatible for the Core (SoM) board
- https://lore.kernel.org/all/20260510201644.4143710-1-alexander.sverdlin@gmail.com/
v1:
- https://lore.kernel.org/all/20260503191842.2736130-1-alexander.sverdlin@gmail.com/
Alexander Sverdlin (6):
arm64: defconfig: Enable Allwinner LRADC input driver
dt-bindings: vendor-prefixes: Add Shenzhen Baijie Technology Co., Ltd.
dt-bindings: arm: sunxi: Add Baijie HelperBoard A133 compatible
dt-bindings: input: sun4i-lradc-keys: Add A100/A133 compatible
arm64: dts: allwinner: a100: Add LRADC node
arm64: dts: allwinner: A133: add support for Baijie Helper A133 board
.../devicetree/bindings/arm/sunxi.yaml | 6 +
.../input/allwinner,sun4i-a10-lradc-keys.yaml | 1 +
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
arch/arm64/boot/dts/allwinner/Makefile | 1 +
.../arm64/boot/dts/allwinner/sun50i-a100.dtsi | 10 +
.../sun50i-a133-helperboard-core.dtsi | 197 ++++++++++++++++++
.../dts/allwinner/sun50i-a133-helperboard.dts | 148 +++++++++++++
arch/arm64/configs/defconfig | 1 +
8 files changed, 366 insertions(+)
create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a133-helperboard-core.dtsi
create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a133-helperboard.dts
--
2.54.0
^ permalink raw reply
* [PATCH v2 5/5] riscv: dts: spacemit: improve RTL8211F PHY configuration on K3 Pico-ITX board
From: Aurelien Jarno @ 2026-06-23 20:43 UTC (permalink / raw)
To: linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Yixun Lan
Cc: Aurelien Jarno,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list:RISC-V ARCHITECTURE,
open list:RISC-V SPACEMIT SoC Support
In-Reply-To: <20260623204431.498700-1-aurelien@aurel32.net>
Vendor kernel enabled ALDPS (Advanced Link Down Power Saving) on the
RTL8211F PHY to save power when link down.
Vendor kernel also disabled the 125MHz clkout clock signal, and indeed
the schematics confirms that it only goes to a test point (TP14), so
let's do the same.
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
index bf64b6c6f867f..e12d798b48a49 100644
--- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
+++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
@@ -234,6 +234,8 @@ phy0: phy@1 {
reset-gpios = <&gpio 0 15 GPIO_ACTIVE_LOW>;
reset-assert-us = <10000>;
reset-deassert-us = <10000>;
+ realtek,aldps-enable;
+ realtek,clkout-disable;
};
};
};
--
2.53.0
^ permalink raw reply related
* [PATCH v2 4/5] riscv: dts: spacemit: add i2c aliases on K3 Pico-ITX board
From: Aurelien Jarno @ 2026-06-23 20:43 UTC (permalink / raw)
To: linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Yixun Lan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti
Cc: Aurelien Jarno,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list:RISC-V ARCHITECTURE,
open list:RISC-V SPACEMIT SoC Support
In-Reply-To: <20260623204431.498700-1-aurelien@aurel32.net>
Add i2c aliases for i2c2 and i2c8 on K3 Pico-ITX board. This is useful
to keep a stable number for the /dev entries after loading the i2c-dev
module.
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
index 0a0c221c99e74..bf64b6c6f867f 100644
--- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
+++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
@@ -14,6 +14,8 @@ / {
aliases {
ethernet0 = ð0;
+ i2c2 = &i2c2;
+ i2c8 = &i2c8;
serial0 = &uart0;
};
--
2.53.0
^ permalink raw reply related
* [PATCH v2 3/5] riscv: dts: spacemit: add 24c02 eeprom on K3 Pico-ITX board
From: Aurelien Jarno @ 2026-06-23 20:43 UTC (permalink / raw)
To: linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Yixun Lan
Cc: Aurelien Jarno,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list:RISC-V SPACEMIT SoC Support,
open list:RISC-V SPACEMIT SoC Support
In-Reply-To: <20260623204431.498700-1-aurelien@aurel32.net>
The K3 Pico-ITX board includes a 24c02 eeprom, that stores the product
name, the part name, the MAC address of the network interfaces and the
board's serial number. These values are also exposed via an
onie,tlv-layout nvmem layout.
The eeprom is marked as read-only since its contents are not supposed to
be modified.
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 30 ++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
index 5fe9041ec5702..0a0c221c99e74 100644
--- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
+++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
@@ -47,6 +47,36 @@ &i2c2 {
pinctrl-names = "default";
pinctrl-0 = <&i2c2_1_cfg>;
status = "okay";
+
+ eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+ pagesize = <8>;
+ read-only;
+ size = <256>;
+ status = "okay";
+
+ nvmem-layout {
+ compatible = "onie,tlv-layout";
+
+ mac-address {
+ #nvmem-cell-cells = <1>;
+ };
+
+ num-macs {
+ };
+
+ part-number {
+ };
+
+ product-name {
+ };
+
+ serial-number {
+ };
+
+ };
+ };
};
&i2c8 {
--
2.53.0
^ permalink raw reply related
* [PATCH v2 2/5] riscv: dts: spacemit: enable the i2c2 adapter on K3 Pico-ITX board
From: Aurelien Jarno @ 2026-06-23 20:43 UTC (permalink / raw)
To: linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Yixun Lan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti
Cc: Aurelien Jarno,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list:RISC-V SPACEMIT SoC Support,
open list:RISC-V SPACEMIT SoC Support
In-Reply-To: <20260623204431.498700-1-aurelien@aurel32.net>
Define I2C2 pinctrl properties, and enable it on the K3 Pico-ITX board.
It will be used by the 24c02 eeprom.
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 6 ++++++
arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi | 11 +++++++++++
2 files changed, 17 insertions(+)
v2:
- Rename pinctrl subnode from i2c2-0-pins to i2c2-1-pins
- Add a missing space after bias-pull-up
diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
index 509cebc0c9568..5fe9041ec5702 100644
--- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
+++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
@@ -43,6 +43,12 @@ reg_aux_vcc5v: regulator-aux-vcc5v {
};
};
+&i2c2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_1_cfg>;
+ status = "okay";
+};
+
&i2c8 {
pinctrl-names = "default";
pinctrl-0 = <&i2c8_cfg>;
diff --git a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi
index 3ee1471f37985..50dc124c097f9 100644
--- a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi
+++ b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi
@@ -78,6 +78,17 @@ gmac1-phy-0-pins {
};
};
+ /omit-if-no-ref/
+ i2c2_1_cfg: i2c2-1-cfg {
+ i2c2-1-pins {
+ pinmux = <K3_PADCONF(46, 5)>, /* i2c2 scl */
+ <K3_PADCONF(47, 5)>; /* i2c2 sda */
+
+ bias-pull-up = <0>;
+ drive-strength = <25>;
+ };
+ };
+
/omit-if-no-ref/
i2c8_cfg: i2c8-cfg {
i2c8-pins {
--
2.53.0
^ permalink raw reply related
* [PATCH v2 1/5] riscv: dts: spacemit: set console baud rate on K3 Pico-ITX board
From: Aurelien Jarno @ 2026-06-23 20:43 UTC (permalink / raw)
To: linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Yixun Lan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Guodong Xu
Cc: Aurelien Jarno, Guodong Xu,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list:RISC-V SPACEMIT SoC Support,
open list:RISC-V SPACEMIT SoC Support
In-Reply-To: <20260623204431.498700-1-aurelien@aurel32.net>
Because the default console's baud rate is not set, defconfig kernels do
not have any serial output on this platform. Set the baud rate to
115200, matching what is used by U-Boot etc on this platform.
Fixes: 7a6131804986 ("riscv: dts: spacemit: add K3 Pico-ITX board support")
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
Reviewed-by: Guodong Xu <docular.xu@gmail.com>
---
arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
v2:
- Collect Reviewed-by tag
diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
index b89c1521e6649..509cebc0c9568 100644
--- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
+++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
@@ -18,7 +18,7 @@ aliases {
};
chosen {
- stdout-path = "serial0";
+ stdout-path = "serial0:115200n8";
};
memory@100000000 {
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v3 08/10] drm/rockchip: dw_dp: Add runtime PM support
From: sashiko-bot @ 2026-06-23 20:40 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: dri-devel, conor+dt, robh, devicetree
In-Reply-To: <20260612-synopsys-dw-dp-improvements-v3-8-dc61e6352508@collabora.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Runtime PM initialized without matching active hardware state, causing clocks to remain permanently enabled.
- [High] Using devm_pm_runtime_enable() in a component bind callback ties PM lifecycle incorrectly, causing Use-After-Free and devres leaks.
--
commit 77c03b774daaa80f329daaa791a8229e9e24ad9e
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/rockchip: dw_dp: Add runtime PM support
Add support for runtime PM to the Rockchip RK3576/3588 Synopsys
DesignWare DisplayPort driver.
> diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
> index 9c53f1d2c29ab..1f53228e56d9f 100644
> --- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
[ ... ]
> @@ -213,6 +218,12 @@ static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void *
> if (IS_ERR(dp->base))
> return PTR_ERR(dp->base);
>
> + pm_runtime_use_autosuspend(dev);
> + pm_runtime_set_autosuspend_delay(dev, 500);
[Severity: High]
Since dw_dp_bind() enables the clocks (via devm_clk_get_enabled() when
setting up the base dw_dp struct), shouldn't we call
pm_runtime_set_active() before enabling runtime PM here?
Without it, the PM core assumes the device starts in RPM_SUSPENDED state.
When the device resumes, dw_dp_runtime_resume() will enable the clocks
again (incrementing the enable count to 2). When autosuspend fires,
dw_dp_runtime_suspend() will disable them (decrementing the count back to
1), meaning the hardware clocks are never physically disabled.
> + ret = devm_pm_runtime_enable(dev);
[Severity: High]
Does using devm_pm_runtime_enable() here tie the PM lifecycle to the
component platform device instead of the DRM master?
The rockchip_dw_dp structure is allocated using drmm_kzalloc() earlier in
this function, which binds its lifetime to the DRM master device. However,
devm_pm_runtime_enable() attaches the devres action to the component device,
whose lifetime spans the full probe/remove cycle.
If the DRM master is unbound, dw_dp_rockchip_unbind() will execute but it
does not disable PM runtime or flush the autosuspend timer. The DRM core
will then free the drmm-managed memory.
Could an autosuspend timer firing after unbind cause a use-after-free
regression in dw_dp_rockchip_runtime_suspend() when it tries to access the
freed dp pointer? Repeated bind/unbind cycles might also endlessly leak
devres actions on the platform device.
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to enable runtime PM\n");
> +
> connector = drm_bridge_connector_init(drm_dev, encoder);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612-synopsys-dw-dp-improvements-v3-0-dc61e6352508@collabora.com?part=8
^ permalink raw reply
* Re: [PATCH 4/9] drm/rockchip: vop2: Consolidate HDMI PHY PLL clock parent switch
From: sashiko-bot @ 2026-06-23 20:40 UTC (permalink / raw)
To: Cristian Ciocaltea; +Cc: dri-devel, conor+dt, devicetree, robh
In-Reply-To: <20260617-dw-hdmi-qp-yuv-v1-4-a665cfd06d7d@collabora.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Calling sleepable functions (`mutex_lock` and CCF APIs) inside an atomic context.
--
commit daeb5d187e2f25bcf364f65898a9cf55a75558aa
Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
drm/rockchip: vop2: Consolidate HDMI PHY PLL clock parent switch
This commit refactors the DCLK parent switch logic for HDMI0 and HDMI1
PHY PLLs. It consolidates duplicated clk_get_parent() and clk_set_parent()
calls into a single shared code path based on the active HDMI endpoint.
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
[ ... ]
> @@ -1932,42 +1932,34 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
[ ... ]
> + if (pll_hdmiphy) {
> + if (!vp->dclk_src)
> + vp->dclk_src = clk_get_parent(vp->dclk);
> +
> + ret = clk_set_parent(vp->dclk, pll_hdmiphy);
[Severity: High]
This is a pre-existing issue, but does calling clk_set_parent() here
result in a sleep inside an atomic context?
The vop2_crtc_atomic_enable() function executes as a CRTC atomic enable
callback, which runs in an atomic context where sleeping is forbidden.
Both clk_set_parent() and clk_prepare_enable() (called earlier in this
function) acquire sleepable mutexes, and vop2_lock() internally calls
mutex_lock(). Can this lead to kernel warnings or potential deadlocks
during the atomic commit phase?
> + if (ret < 0)
> + drm_warn(vop2->drm,
> + "Failed to switch DCLK to HDMI PHY PLL: %d\n",
> + ret);
> + }
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260617-dw-hdmi-qp-yuv-v1-0-a665cfd06d7d@collabora.com?part=4
^ permalink raw reply
* Re: [PATCH v5 6/7] arm64: dts: renesas: sparrow-hawk: Enable DisplayPort by adding DSC
From: sashiko-bot @ 2026-06-23 20:37 UTC (permalink / raw)
To: Tomi Valkeinen; +Cc: devicetree, dri-devel, wsa+renesas, robh, conor+dt
In-Reply-To: <20260615-rcar-du-dsc-v5-6-aed1a28610e4@ideasonboard.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The rcar_dsc driver ignores the `resets` property from the DT binding, failing to deassert the DSC hardware module's reset state.
--
commit a8616c71b764dc9fa4ae244e47795371e7fead44
Author: Marek Vasut <marek.vasut+renesas@mailbox.org>
drm/rcar-du: dsc: Add rudimentary Renesas R-Car V4H DSC driver
This commit introduces a rudimentary DSC bridge driver for the Renesas R-Car
V4H SoC. The driver functions as a pass-through to enable the DSI1 pipeline
and performs VESA DSC encoding.
> diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_dsc.c b/drivers/gpu/drm/renesas/rcar-du/rcar_dsc.c
> new file mode 100644
> index 0000000000000..362e683289d69
> --- /dev/null
> +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_dsc.c
[ ... ]
> +static int rcar_dsc_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct rcar_dsc *dsc;
> + int ret;
> +
> + dsc = devm_drm_bridge_alloc(dev, struct rcar_dsc, bridge,
> + &rcar_dsc_bridge_ops);
> + if (IS_ERR(dsc))
> + return PTR_ERR(dsc);
> +
> + platform_set_drvdata(pdev, dsc);
> +
> + dsc->dev = &pdev->dev;
> +
> + dsc->bridge.next_bridge = of_drm_get_bridge_by_endpoint(dev->of_node,
> + 1, 0);
[Severity: High]
Does this driver fail to retrieve and deassert the DSC module's hardware reset
state?
If the reset control isn't retrieved and deasserted, the DSC hardware module
will remain in a reset state, breaking the display pipeline functionality. It
might coincidentally work if the bootloader has already deasserted the reset,
but could fail on suspend/resume or when the bootloader does not initialize
the display.
Should the driver use devm_reset_control_get_exclusive() here and deassert
the reset state during probe?
> + if (IS_ERR(dsc->bridge.next_bridge))
> + return PTR_ERR(dsc->bridge.next_bridge);
> +
> + dsc->bridge.of_node = dev->of_node;
> +
> + pm_runtime_enable(&pdev->dev);
> +
> + ret = devm_drm_bridge_add(dev, &dsc->bridge);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260615-rcar-du-dsc-v5-0-aed1a28610e4@ideasonboard.com?part=6
^ permalink raw reply
* Re: [PATCH 3/9] drm/rockchip: vop2: Avoid DCLK source switch for 10-bit YUV422 output
From: sashiko-bot @ 2026-06-23 20:33 UTC (permalink / raw)
To: Cristian Ciocaltea; +Cc: conor+dt, robh, dri-devel, devicetree
In-Reply-To: <20260617-dw-hdmi-qp-yuv-v1-3-a665cfd06d7d@collabora.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [High] Division by zero in max_dclk calculation due to uninitialized output_bpc.
- [High] Integer overflow in max_dclk calculation on 32-bit architectures.
- [High] Atomic context violation due to calling sleeping functions.
--
commit cdb8cbbc3fd201db3157d6704b3856b8565e10e2
Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
drm/rockchip: vop2: Avoid DCLK source switch for 10-bit YUV422 output
The commit modifies the DCLK source decision logic for HDMI output in VOP2.
It prevents unnecessary switching of the DCLK source to the system CRU for
10-bit YUV422 output by forcing the bandwidth check to 8 bpc.
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> index 2833fb49ad81a..17d21e08ad977 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> @@ -1922,8 +1922,17 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
[Severity: High]
This is a pre-existing issue, but does this function violate atomic context
rules?
According to the DRM subsystem guidelines, CRTC atomic enable callbacks execute
in atomic context where sleeping is forbidden. However, vop2_crtc_atomic_enable()
acquires a mutex via vop2_lock() and later calls clk_prepare_enable(), both of
which can sleep.
Can this cause scheduling while atomic warnings or deadlocks during atomic
commits?
> * to 4K@60Hz, if available, otherwise keep using the system CRU.
> */
> if (vop2->pll_hdmiphy0 || vop2->pll_hdmiphy1) {
> - unsigned long max_dclk = DIV_ROUND_CLOSEST_ULL(VOP2_MAX_DCLK_RATE * 8,
> - vcstate->output_bpc);
> + /*
> + * YUV422 always transmits two 12-bit components per clock
> + * cycle, regardless of the color depth, which from a rate
> + * perspective is equivalent to three 8-bit RGB components.
> + * Force 8 bpc here so the bandwidth check reflects the actual
> + * TMDS rate and avoids an unnecessary DCLK source switch.
> + */
> + unsigned int bpc = vcstate->output_mode == ROCKCHIP_OUT_MODE_YUV422 ?
> + 8 : vcstate->output_bpc;
> + unsigned long max_dclk = DIV_ROUND_CLOSEST_ULL(VOP2_MAX_DCLK_RATE * 8, bpc);
[Severity: High]
This isn't a bug introduced by this patch, but can bpc be 0 here and cause a
division by zero?
If the VOP2 driver is used with a non-HDMI output like DSI or LVDS on a board
that also has an HDMI PHY, vop2->pll_hdmiphy0 or vop2->pll_hdmiphy1 will be
non-NULL. Since DSI/LVDS encoders do not set vcstate->output_bpc, it defaults
to 0.
This would lead to DIV_ROUND_CLOSEST_ULL(..., 0) before the loop checks if
the endpoint is HDMI.
[Severity: High]
This is also a pre-existing issue, but does VOP2_MAX_DCLK_RATE * 8 overflow on
32-bit architectures?
VOP2_MAX_DCLK_RATE is defined as 600000000UL. On architectures where
unsigned long is 32 bits, 600000000UL * 8 equals 4,800,000,000, which exceeds
ULONG_MAX.
This truncates to 505,032,704 before being promoted to unsigned long long inside
DIV_ROUND_CLOSEST_ULL, improperly forcing higher resolutions to fall back to the
system CRU.
Should this be cast to a 64-bit type first, such as VOP2_MAX_DCLK_RATE * 8ULL?
> if (clock <= max_dclk) {
> drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
> struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260617-dw-hdmi-qp-yuv-v1-0-a665cfd06d7d@collabora.com?part=3
^ permalink raw reply
* Re: [PATCH v3 07/10] drm/bridge: synopsys: dw-dp: Add Runtime PM support
From: sashiko-bot @ 2026-06-23 20:31 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: robh, conor+dt, dri-devel, devicetree
In-Reply-To: <20260612-synopsys-dw-dp-improvements-v3-7-dc61e6352508@collabora.com>
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [High] Unchecked return value of clk_prepare_enable() in runtime resume.
- [High] Unchecked return value of pm_runtime_get_sync() during atomic enable.
- [High] Interrupt handler accesses hardware registers while device may be suspended.
Pre-existing issues:
- [High] Memory/refcount leak of the next_bridge acquired via of_drm_get_bridge_by_endpoint().
- [High] PHY power_count underflow causing permanent PHY failure after early return in atomic_enable.
- [High] Uninitialized stack memory copied to buffer in dw_dp_aux_read_data().
--
commit 25b9778935f0029deadc6a4ac4d19728b07017ee
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
Subject: drm/bridge: synopsys: dw-dp: Add Runtime PM support
The patch adds runtime PM stubs to the Synopsys DesignWare DisplayPort bridge
driver and hooks them up to various hardware access paths.
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> index 7fa38145e35ce..7f4f36c614844 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> @@ -1465,6 +1465,8 @@ static ssize_t dw_dp_aux_transfer(struct drm_dp_aux *aux,
> if (WARN_ON(msg->size > 16))
> return -E2BIG;
>
> + ACQUIRE(pm_runtime_active_auto, pm)(dp->dev);
> +
[Severity: High]
This is a pre-existing issue, but I noticed a potential information leak in
dw_dp_aux_read_data() which is called during these transfers:
drivers/gpu/drm/bridge/synopsys/dw-dp.c:dw_dp_aux_read_data() {
...
u32 value;
regmap_read(dp->regmap, DW_DP_AUX_DATA0 + i * 4, &value);
for (j = 0; j < num; j++)
buffer[i * 4 + j] = value >> (j * 8);
...
}
If regmap_read() fails, the local variable "value" remains uninitialized.
Could this copy uninitialized kernel stack memory into the buffer, which is
then returned to userspace via the DRM DP AUX chardev?
> switch (msg->request & ~DP_AUX_I2C_MOT) {
> case DP_AUX_NATIVE_WRITE:
> case DP_AUX_I2C_WRITE:
[ ... ]
> @@ -1655,6 +1657,8 @@ static void dw_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> struct drm_connector_state *conn_state;
> int ret;
>
> + pm_runtime_get_sync(dp->dev);
> +
[Severity: High]
Does this code need to check the return value of pm_runtime_get_sync()?
If the device fails to resume (for example, if the clocks fail to enable),
the driver will continue with hardware initialization routines like
dw_dp_link_enable(). Can this lead to a crash by accessing a suspended
device?
> connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
> if (!connector) {
> dev_err(dp->dev, "failed to get connector\n");
> return;
> }
[Severity: High]
This is a pre-existing issue, but does returning early here cause a
phy->power_count underflow later?
If we return early because connector is NULL, or if dw_dp_link_enable() fails,
we skip powering on the PHY. However, because atomic_enable returns void,
the DRM framework will still consider the bridge enabled and later call
dw_dp_bridge_atomic_disable(), which unconditionally executes:
phy_power_off(dp->phy);
Could this drop the PHY's power_count below zero and permanently prevent it
from powering on during subsequent modesets?
[ ... ]
> @@ -2153,6 +2160,26 @@ void dw_dp_unbind(struct dw_dp *dp)
> }
> EXPORT_SYMBOL_GPL(dw_dp_unbind);
>
> +int dw_dp_runtime_suspend(struct dw_dp *dp)
> +{
> + clk_disable_unprepare(dp->aux_clk);
> + clk_disable_unprepare(dp->apb_clk);
> +
> + return 0;
> +}
[Severity: High]
Is it safe to leave the IRQ enabled while the device is suspended?
If a spurious interrupt or a hot-plug event fires while the device is in
the RPM_SUSPENDED state, dw_dp_irq() will execute and unconditionally call:
regmap_read(dp->regmap, DW_DP_GENERAL_INTERRUPT, &value);
Since the APB clock is disabled here, might this MMIO read hang the bus
and cause a kernel panic?
> +EXPORT_SYMBOL_GPL(dw_dp_runtime_suspend);
> +
> +int dw_dp_runtime_resume(struct dw_dp *dp)
> +{
> + clk_prepare_enable(dp->apb_clk);
> + clk_prepare_enable(dp->aux_clk);
> +
[Severity: High]
Should the return values of clk_prepare_enable() be checked?
If the clocks fail to enable, the function proceeds to call dw_dp_init_hw()
which accesses hardware registers via regmap. Could this lead to a bus fault?
> + dw_dp_init_hw(dp);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(dw_dp_runtime_resume);
There is also one additional pre-existing issue in a part of the file not
modified by this patch:
[Severity: High]
This is a pre-existing issue, but I noticed a potential leak in dw_dp_bind():
drivers/gpu/drm/bridge/synopsys/dw-dp.c:dw_dp_bind() {
...
next_bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 1, 0);
if (IS_ERR(next_bridge)) {
...
ret = drm_bridge_attach(encoder, next_bridge, bridge,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
...
}
Does this code leak the reference count for next_bridge?
of_drm_get_bridge_by_endpoint() returns a bridge with an incremented refcount,
and drm_bridge_attach() takes its own reference. Should there be a call to
drm_bridge_put() to drop the initial reference so the next bridge module can
be unloaded?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612-synopsys-dw-dp-improvements-v3-0-dc61e6352508@collabora.com?part=7
^ permalink raw reply
* Re: [PATCH v2 4/4] staging: fbtft: remove fb_ssd1351 driver
From: Amit Barzilai @ 2026-06-23 20:28 UTC (permalink / raw)
To: javierm, andriy.shevchenko, mripard
Cc: airlied, andy, azuddinadam, chintanlike, conor+dt, deller,
devicetree, dri-devel, gregkh, krzk+dt, linux-fbdev, linux-kernel,
linux-staging, maarten.lankhorst, robh, simona, tzimmermann
In-Reply-To: <87ik79iqsz.fsf@ocarina.mail-host-address-is-not-set>
Javier Martinez Canillas <javierm@redhat.com> writes:
> Andy Shevchenko <andriy.shevchenko@intel.com> writes:
>
>> On Mon, Jun 22, 2026 at 06:25:06PM +0300, Amit Barzilai wrote:
>>> The SSD1351 support was added to the ssd130x DRM driver. To avoid
>>> confusion and irrelevant updates, the staging fb_ssd1351 driver is
>>> removed.
>>
>> NAK, the fbtft has two drivers in one (SPI + parallel), plus as Maxime said,
>> it has its own binding.
>>
>
> Yes, I agree that we don't want to delete the fbtft driver.
I will remove this patch from the next version.
Thank you Javier, Andy and Maxime for the feedback and explanations.
--
Thanks,
Amit
^ permalink raw reply
* Re: [PATCH v5 5/7] arm64: dts: renesas: r8a779g0: Add DSC
From: sashiko-bot @ 2026-06-23 20:27 UTC (permalink / raw)
To: Tomi Valkeinen; +Cc: robh, wsa+renesas, devicetree, conor+dt, dri-devel
In-Reply-To: <20260615-rcar-du-dsc-v5-5-aed1a28610e4@ideasonboard.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The rcar_dsc driver does not deassert its hardware reset, leaving the DSC block permanently in reset.
- [Medium] Mixing devm_drm_bridge_add with manual pm_runtime_disable creates a devres cleanup ordering bug and a use-after-disable window.
--
commit ffeafc5aca0f12828e91ec48615ef59b3d5989f2
Author: Marek Vasut <marek.vasut+renesas@mailbox.org>
arm64: dts: renesas: r8a779g0: Add DSC
This commit adds the Renesas DSC Display Stream Compression bridge to the
r8a779g0 device tree. The node is placed between the DU and DSI1 units to
make the DSI1 pipeline functional.
> --- a/arch/arm64/boot/dts/renesas/r8a779g0.dtsi
> +++ b/arch/arm64/boot/dts/renesas/r8a779g0.dtsi
> @@ -2298,6 +2298,35 @@ du_out_dsi0: endpoint {
[ ... ]
> + dsc: bridge@feb8d000 {
> + compatible = "renesas,r8a779g0-dsc";
[Severity: Medium]
Does the driver backing this device introduce a devres cleanup regression?
Looking at rcar_dsc.c, rcar_dsc_probe() uses a managed function for the
bridge but manually enables PM runtime:
drivers/gpu/drm/renesas/rcar-du/rcar_dsc.c:rcar_dsc_probe() {
...
pm_runtime_enable(&pdev->dev);
ret = devm_drm_bridge_add(dev, &dsc->bridge);
...
}
Then rcar_dsc_remove() manually disables PM runtime:
drivers/gpu/drm/renesas/rcar-du/rcar_dsc.c:rcar_dsc_remove() {
pm_runtime_disable(&pdev->dev);
}
If the driver is unbound, rcar_dsc_remove() manually calls
pm_runtime_disable(). Only after this returns do the devres handlers
run to call drm_bridge_remove().
If a DRM atomic commit executes during this window, rcar_dsc_atomic_enable()
will call pm_runtime_get_sync(). Because PM runtime is disabled, won't this
fail, trigger a warning, and leave the hardware unpowered?
Using devm_pm_runtime_enable() instead would guarantee the correct reverse
cleanup order.
> + reg = <0 0xfeb8d000 0 0x400>;
> + interrupts = <GIC_SPI 559 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&cpg CPG_MOD 2819>;
> + power-domains = <&sysc R8A779G0_PD_ALWAYS_ON>;
> + resets = <&cpg 2819>;
[Severity: High]
Is there a regression here that leaves the DSC block permanently in reset?
Looking at rcar_dsc_probe() in drivers/gpu/drm/renesas/rcar-du/rcar_dsc.c,
it doesn't appear to request or explicitly deassert the hardware reset line
defined here.
If the DSC block is left in its initial reset state, won't it fail to pass
pixel data and break the display pipeline?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260615-rcar-du-dsc-v5-0-aed1a28610e4@ideasonboard.com?part=5
^ permalink raw reply
* Re: [PATCH v2 2/2] dt-bindings: Drop incorrect usage of double '::'
From: Andi Shyti @ 2026-06-23 20:26 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: linux-arm-msm, devicetree, linux-kernel, linux-arm-kernel,
linux-samsung-soc, linux-clk, dri-devel, freedreno, linux-i2c,
linux-pm, linux-leds, linux-media, linux-mmc, linux-phy,
linux-gpio, linux-renesas-soc, linux-serial, linux-sound,
linux-usb
In-Reply-To: <20260623054842.21831-4-krzysztof.kozlowski@oss.qualcomm.com>
Hi Krzysztof,
On Tue, Jun 23, 2026 at 07:48:44AM +0200, Krzysztof Kozlowski wrote:
> There is no use of double colon '::' in YAML. OTOH, the literal style
> block, e.g. using '|' treats all characters as content [1] therefore
> single use of ':' in descriptions is perfectly fine, whenever '|' is
> used.
>
> Cleanup existing code, so the confusing style won't be re-used in new
> contributions.
>
> Link: https://yaml.org/spec/1.2.2/#literal-style [1]
> Acked-by: Conor Dooley <conor.dooley@microchip.com>
> Acked-by: Alim Akhtar <alim.akhtar@samsung.com>
> Acked-by: Sebastian Reichel <sebastian.reichel@collabora.com>
> Acked-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> Acked-by: Mark Brown <broonie@kernel.org>
> Acked-by: Geert Uytterhoeven <geert+renesas@glider.be> # renesas
> Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Acked-by: Andi Shyti <andi.shyti@kernel.org>
Thanks,
Andi
^ permalink raw reply
* Re: [PATCH v2 1/2] dt-bindings: clock: Drop incorrect usage of double '::'
From: Andi Shyti @ 2026-06-23 20:26 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: linux-arm-msm, devicetree, linux-kernel, linux-arm-kernel,
linux-samsung-soc, linux-clk, dri-devel, freedreno, linux-i2c,
linux-pm, linux-leds, linux-media, linux-mmc, linux-phy,
linux-gpio, linux-renesas-soc, linux-serial, linux-sound,
linux-usb
In-Reply-To: <20260623054842.21831-3-krzysztof.kozlowski@oss.qualcomm.com>
Hi Krzysztof,
On Tue, Jun 23, 2026 at 07:48:43AM +0200, Krzysztof Kozlowski wrote:
> There is no use of double colon '::' in YAML. OTOH, the literal style
> block, e.g. using '|' treats all characters as content [1] therefore
> single use of ':' in descriptions is perfectly fine, whenever '|' is
> used.
>
> Cleanup existing code, so the confusing style won't be re-used in new
> contributions.
>
> Link: https://yaml.org/spec/1.2.2/#literal-style [1]
> Acked-by: Alim Akhtar <alim.akhtar@samsung.com>
> Acked-by: Conor Dooley <conor.dooley@microchip.com>
> Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Acked-by: Andi Shyti <andi.shyti@kernel.org>
Thanks,
Andi
^ permalink raw reply
* Re: [PATCH 2/9] drm/rockchip: vop2: Reset AXI and DCLK to improve robustness
From: sashiko-bot @ 2026-06-23 20:20 UTC (permalink / raw)
To: Cristian Ciocaltea; +Cc: devicetree, robh, dri-devel, conor+dt
In-Reply-To: <20260617-dw-hdmi-qp-yuv-v1-2-a665cfd06d7d@collabora.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] AXI reset is performed after detaching the IOMMU domain, risking memory corruption.
Pre-existing issues:
- [High] Unpowered hardware access and unbalanced PM runtime usage due to ignored error in vop2_enable().
- [High] Clock enable count underflow and resource tracking mismatch on early return in vop2_crtc_atomic_enable().
--
commit 13864800def8231541cc3847f090a3ab6ab365c2
Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Subject: drm/rockchip: vop2: Reset AXI and DCLK to improve robustness
Assert the AXI reset in the CRTC disable path, and the VP DCLK reset in
the enable path. These resets are intended to leave the hardware in a clean
state for the next use, helping recover from exceptions such as IOMMU page
faults.
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> index 4cce3e336f5b3..2833fb49ad81a 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
[ ... ]
> @@ -938,6 +959,8 @@ static void vop2_disable(struct vop2 *vop2)
> {
> rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
>
> + vop2_clk_reset(vop2, vop2->axi_rst);
> +
[Severity: High]
Does asserting the AXI reset here risk memory corruption?
If the AXI master is recovering from an exception and still issuing DMA
requests, detaching the IOMMU domain first leaves a window where the master
could access physical memory without translation.
Could this lead to memory corruption or spurious page faults? Should the AXI
master be explicitly reset before the IOMMU is detached?
> pm_runtime_put_sync(vop2->dev);
>
> regcache_drop_region(vop2->map, 0, vop2_regmap_config.max_register);
[ ... ]
> @@ -1948,6 +1971,8 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
>
> vop2_crtc_atomic_try_set_gamma(vop2, vp, crtc, crtc_state);
>
> + vop2_clk_reset(vop2, vp->dclk_rst);
> +
[Severity: High]
This isn't a bug introduced by this patch, but is it possible for unpowered
hardware access to cause a crash if vop2_enable() fails?
In vop2_enable(), if pm_runtime_resume_and_get() or clock preparation fails,
it logs an error and returns void. However, vop2_crtc_atomic_enable()
unconditionally increments vop2->enable_count and continues configuring
hardware:
if (!vop2->enable_count)
vop2_enable(vop2);
vop2->enable_count++;
Since the power domain and core clocks would remain off on failure, could the
subsequent writes to VOP2 hardware registers trigger a synchronous external
abort and crash the system?
[Severity: High]
This is a pre-existing issue, but can an early return in
vop2_crtc_atomic_enable() cause severe state mismatches?
If clk_prepare_enable(vp->dclk) fails earlier in this function:
ret = clk_prepare_enable(vp->dclk);
if (ret < 0) {
drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n",
vp->id, ret);
vop2_unlock(vop2);
return;
}
The function returns early without updating state properly. During disable,
vop2_crtc_atomic_disable() blindly decrements the tracking state:
clk_disable_unprepare(vp->dclk);
vop2->enable_count--;
if (!vop2->enable_count)
vop2_disable(vop2);
If another CRTC is active, couldn't this underflow or prematurely drop the
count to 0, unintentionally shutting off core clocks and PM runtime for the
actively running CRTC?
> drm_crtc_vblank_on(crtc);
>
> vop2_unlock(vop2);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260617-dw-hdmi-qp-yuv-v1-0-a665cfd06d7d@collabora.com?part=2
^ permalink raw reply
* Re: [PATCH v3 06/10] drm/rockchip: dw_dp: Implement out-of-band HPD handling
From: sashiko-bot @ 2026-06-23 20:20 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: robh, conor+dt, dri-devel, devicetree
In-Reply-To: <20260612-synopsys-dw-dp-improvements-v3-6-dc61e6352508@collabora.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Using IS_ERR() to validate the return value of platform_get_mem_or_io() fails to catch NULL, leading to a kernel crash when the resource is missing.
- [Medium] The `dp_dp_rockchip_get_vo_grf` function fails to implement the documented fallback to read the `vo-grf` phandle from the DisplayPort device node.
--
commit 2739e6354d59ce0c07c5ba0c2a12b2779e399b4a
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/rockchip: dw_dp: Implement out-of-band HPD handling
Implement out-of-band hotplug handling to receive external hotplug
information from the USB-C state machine. This properly detects if a cable
is plugged in for adapters.
> diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
> index 35598ab9fe84a..9c53f1d2c29ab 100644
> --- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
[ ... ]
> @@ -72,14 +117,49 @@ static const struct drm_encoder_helper_funcs dw_dp_encoder_helper_funcs = {
> .atomic_check = dw_dp_encoder_atomic_check,
> };
>
> +static struct regmap *dp_dp_rockchip_get_vo_grf(struct rockchip_dw_dp *dp)
> +{
> + struct device_node *np = dev_of_node(dp->dev);
> + struct of_phandle_args args;
> + struct regmap *regmap;
> + int ret;
> +
> + ret = of_parse_phandle_with_args(np, "phys", "#phy-cells", 0, &args);
> + if (ret)
> + return ERR_PTR(-ENODEV);
> +
> + /*
> + * Limit this workaround to RK3576 and RK3588, new platforms should
> + * add a VO GRF phandle in the DisplayPort DT node.
> + */
> + if (!of_device_is_compatible(args.np, "rockchip,rk3576-usbdp-phy") &&
> + !of_device_is_compatible(args.np, "rockchip,rk3588-usbdp-phy")) {
> + regmap = ERR_PTR(-ENODEV);
> + goto out_put_node;
> + }
> +
> + regmap = syscon_regmap_lookup_by_phandle(args.np, "rockchip,vo-grf");
[Severity: Medium]
Does this function actually implement the forward-compatibility behavior
described in the comment?
If a new platform follows the advice and adds a VO GRF phandle directly to
the DisplayPort DT node (dropping the "phys" property), it looks like the
unconditional of_parse_phandle_with_args() check for "phys" will fail and
return ERR_PTR(-ENODEV) early.
Should there be a primary lookup path for "rockchip,vo-grf" on the DP node
itself before falling back to this PHY workaround?
> +
> +out_put_node:
> + of_node_put(args.np);
> + return regmap;
> +}
> +
> static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void *data)
> {
> - const struct dw_dp_plat_data *plat_data;
> + const struct rockchip_dw_dp_plat_data *plat_data_const;
> + struct platform_device *pdev = to_platform_device(dev);
> + struct dw_dp_plat_data *plat_data;
> struct drm_device *drm_dev = data;
> struct rockchip_dw_dp *dp;
> struct drm_encoder *encoder;
> struct drm_connector *connector;
> - int ret;
> + struct resource *res;
> + int ret, id;
> +
> + plat_data = drmm_kzalloc(drm_dev, sizeof(*plat_data), GFP_KERNEL);
> + if (!plat_data)
> + return -ENOMEM;
[ ... ]
> + dp->pdata = plat_data_const;
> +
> + res = platform_get_mem_or_io(pdev, 0);
> + if (IS_ERR(res))
> + return PTR_ERR(res);
> +
> + /* find the DisplayPort ID from the io address */
> + dp->id = -ENODEV;
> + for (id = 0; id < plat_data_const->num_ctrls; id++) {
> + if (res->start == plat_data_const->ctrl_ids[id]) {
> + dp->id = id;
> + break;
> + }
> + }
[Severity: High]
Can this code cause a NULL pointer dereference if the resource is missing
from the device tree?
Looking at platform_get_mem_or_io(), it returns NULL when the resource is
not found, rather than an ERR_PTR. Because IS_ERR(NULL) evaluates to false,
the check "if (IS_ERR(res))" would let a NULL pointer slip through. This
NULL pointer would then be dereferenced when accessing res->start in the
loop.
Should this be updated to check "if (!res)" instead?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612-synopsys-dw-dp-improvements-v3-0-dc61e6352508@collabora.com?part=6
^ permalink raw reply
* Re: [PATCH v3 05/10] drm/bridge: synopsys: dw-dp: Support software triggered OOB HPD
From: sashiko-bot @ 2026-06-23 20:11 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: dri-devel, robh, conor+dt, devicetree
In-Reply-To: <20260612-synopsys-dw-dp-improvements-v3-5-dc61e6352508@collabora.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Reference leak of the downstream bridge in `dw_dp_bind()` on both success and error paths.
- [High] The driver incorrectly treats the absence of a downstream bridge as a fatal error, breaking backward compatibility.
Pre-existing issues:
- [High] Missing `cancel_work_sync()` for `dp->hpd_work` during unbind leads to a Use-After-Free.
--
commit 121b9fc711d52bcdbd8b50a6dc50a5308864d167
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/bridge: synopsys: dw-dp: Support software triggered OOB HPD
Add support for USB-C DP AltMode out-of-band hotplug handling. The
handling itself is implemented in the platform specific driver as the
registers to force HPD state are not part of the Designware DisplayPort
IP itself. Instead the platform integration might provide the necessary
functionality to mux the HPD signal.
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> index 98cff435dfb87..7fa38145e35ce 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
[ ... ]
> @@ -2078,6 +2107,13 @@ struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder,
> goto unregister_aux;
> }
[Severity: High]
Does dw_dp_bind() leak the reference to the downstream bridge?
of_drm_get_bridge_by_endpoint() returns a bridge pointer with an incremented
reference count, which is stored in next_bridge:
next_bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 1, 0);
if (IS_ERR(next_bridge)) {
ret = PTR_ERR(next_bridge);
...
The reference is not dropped after drm_bridge_attach() succeeds, nor is it
dropped on the error path before jumping to unregister_aux. Could this leak
the reference on both success and error paths, preventing the downstream
bridge from cleanly unloading?
[Severity: High]
Should the absence of a downstream bridge be treated as a fatal error?
If the device tree defines the DP controller as the end of the display chain
(i.e., no endpoint 1 is defined), of_drm_get_bridge_by_endpoint() returns
-ENODEV. The code unconditionally aborts initialization here instead of
gracefully ignoring it:
next_bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 1, 0);
if (IS_ERR(next_bridge)) {
ret = PTR_ERR(next_bridge);
dev_err_probe(dev, ret, "failed to get follow-up bridge.\n");
goto unregister_aux;
}
Could this break backward compatibility and cause the driver to fail to probe
on boards that do not define a downstream bridge?
>
> + if (dw_dp_is_routed_to_usb_c(encoder)) {
> + dev_dbg(dev, "USB-C mode\n");
> +
> + if (dp->plat_data.hpd_sw_sel)
> + dp->plat_data.hpd_sw_sel(dp->plat_data.data, 1);
> + }
> +
> dw_dp_init_hw(dp);
[ ... ]
[Severity: High]
This isn't a bug introduced by this patch, but does dw_dp_unbind() leave
pending work scheduled?
During bind, the driver initializes dp->hpd_work and schedules it in the
interrupt handler dw_dp_handle_hpd_event(). However, dw_dp_unbind() does not
cancel it:
void dw_dp_unbind(struct dw_dp *dp)
{
drm_dp_aux_unregister(&dp->aux);
}
Because the dp context is allocated with devm_drm_bridge_alloc(), it is
automatically freed shortly after unbind returns. If the scheduled work
executes after dp is freed, could it cause a use-after-free in
dw_dp_hpd_work()?
Should cancel_work_sync(&dp->hpd_work) be added to dw_dp_unbind()?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612-synopsys-dw-dp-improvements-v3-0-dc61e6352508@collabora.com?part=5
^ permalink raw reply
* [PATCH v5 2/3] iio: health: add MAX86150 ECG and PPG biosensor driver
From: Md Shofiqul Islam @ 2026-06-23 20:11 UTC (permalink / raw)
To: linux-iio
Cc: jic23, lars, conor, conor+dt, robh, krzk+dt, devicetree,
linux-kernel, Md Shofiqul Islam
In-Reply-To: <20260623201124.18271-1-shofiqtest@gmail.com>
Add a new IIO driver for the Analog Devices MAX86150 integrated
biosensor, which combines two PPG optical channels (Red/IR LED) and
one ECG biopotential channel in a single I2C device.
Key features:
- 32-entry hardware FIFO with configurable almost-full threshold
- Interrupt-driven triggered buffer using IIO trigger framework
- Per-sample timestamp reconstruction anchored to A_FULL IRQ time
- DMA-safe FIFO read buffer padded to full cacheline to satisfy
CONFIG_DMA_API_DEBUG constraints (fifo_raw and buf in separate
cachelines)
- Level-triggered interrupt safety via IRQF_ONESHOT (threaded IRQ
keeps line masked until INT_STATUS1 is cleared)
- PPG_RDY polling in read_raw() to handle slow oscillator start-up
- SYS_SHDN power management: device stays off until buffer enabled
or sysfs read
- Optional vdd, avdd, vref, leds regulator support
Signed-off-by: Md Shofiqul Islam <shofiqtest@gmail.com>
---
drivers/iio/health/Kconfig | 23 ++
drivers/iio/health/Makefile | 1 +
drivers/iio/health/max86150.c | 730 ++++++++++++++++++++++++++++++++++
3 files changed, 754 insertions(+)
create mode 100644 drivers/iio/health/max86150.c
diff --git a/drivers/iio/health/Kconfig b/drivers/iio/health/Kconfig
index a89f3abf11f4..6496cf55290c 100644
--- a/drivers/iio/health/Kconfig
+++ b/drivers/iio/health/Kconfig
@@ -13,6 +13,7 @@ config AFE4403
depends on SPI_MASTER
select REGMAP_SPI
select IIO_BUFFER
+ select IIO_TRIGGER
select IIO_TRIGGERED_BUFFER
help
Say yes to choose the Texas Instruments AFE4403
@@ -26,6 +27,7 @@ config AFE4404
depends on I2C
select REGMAP_I2C
select IIO_BUFFER
+ select IIO_TRIGGER
select IIO_TRIGGERED_BUFFER
help
Say yes to choose the Texas Instruments AFE4404
@@ -39,6 +41,7 @@ config MAX30100
depends on I2C
select REGMAP_I2C
select IIO_BUFFER
+ select IIO_TRIGGER
select IIO_KFIFO_BUF
help
Say Y here to build I2C interface support for the Maxim
@@ -52,6 +55,7 @@ config MAX30102
depends on I2C
select REGMAP_I2C
select IIO_BUFFER
+ select IIO_TRIGGER
select IIO_KFIFO_BUF
help
Say Y here to build I2C interface support for the Maxim
@@ -62,4 +66,23 @@ config MAX30102
endmenu
+
+config MAX86150
+ tristate "MAX86150 ECG and PPG biosensor"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGER
+ select IIO_TRIGGERED_BUFFER
+ select REGMAP_I2C
+ help
+ Say Y here to enable support for the Maxim MAX86150 combined
+ ECG and photoplethysmography (PPG) biosensor.
+
+ The driver exposes three IIO channels: two PPG optical channels
+ (Red and IR LED) for heart rate and SpO2 monitoring, and one
+ ECG channel for biopotential recording.
+
+ This driver can also be built as a module. If so, the module
+ will be called max86150.
+
endmenu
diff --git a/drivers/iio/health/Makefile b/drivers/iio/health/Makefile
index 910817112258..04fc73c58444 100644
--- a/drivers/iio/health/Makefile
+++ b/drivers/iio/health/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_AFE4403) += afe4403.o
obj-$(CONFIG_AFE4404) += afe4404.o
obj-$(CONFIG_MAX30100) += max30100.o
obj-$(CONFIG_MAX30102) += max30102.o
+obj-$(CONFIG_MAX86150) += max86150.o
diff --git a/drivers/iio/health/max86150.c b/drivers/iio/health/max86150.c
new file mode 100644
index 000000000000..92bf31f0a761
--- /dev/null
+++ b/drivers/iio/health/max86150.c
@@ -0,0 +1,730 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MAX86150 combined ECG and PPG biosensor driver
+ *
+ * Copyright (C) 2026 Md Shofiqul Islam <shofiqtest@gmail.com>
+ *
+ * The MAX86150 integrates two PPG optical channels (Red/IR LED) and one
+ * ECG biopotential channel in a single I2C device. Data is captured
+ * through a 32-entry hardware FIFO with a configurable almost-full
+ * interrupt, making it well-suited for continuous monitoring with a
+ * low-power host.
+ *
+ * Datasheet:
+ * https://www.analog.com/media/en/technical-documentation/data-sheets/MAX86150.pdf
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+/* Register addresses */
+#define MAX86150_REG_INT_STATUS1 0x00
+#define MAX86150_REG_INT_STATUS2 0x01
+#define MAX86150_REG_INT_ENABLE1 0x02
+#define MAX86150_REG_INT_ENABLE2 0x03
+#define MAX86150_REG_FIFO_WR_PTR 0x04
+#define MAX86150_REG_OVF_COUNTER 0x05
+#define MAX86150_REG_FIFO_RD_PTR 0x06
+#define MAX86150_REG_FIFO_DATA 0x07
+#define MAX86150_REG_FIFO_CONFIG 0x08
+#define MAX86150_REG_FIFO_DCTRL1 0x09 /* FD1[3:0] FD2[7:4] */
+#define MAX86150_REG_FIFO_DCTRL2 0x0A /* FD3[3:0] FD4[7:4] */
+#define MAX86150_REG_SYS_CTRL 0x0D
+#define MAX86150_REG_PPG_CONFIG1 0x10
+#define MAX86150_REG_PPG_CONFIG2 0x11
+#define MAX86150_REG_LED1_PA 0x14 /* Red LED pulse amplitude */
+#define MAX86150_REG_LED2_PA 0x15 /* IR LED pulse amplitude */
+#define MAX86150_REG_ECG_CONFIG1 0x3C
+#define MAX86150_REG_ECG_CONFIG3 0x3E
+#define MAX86150_REG_PART_ID 0xFF
+
+/* Field masks */
+#define MAX86150_PART_ID_VAL 0x1E
+
+/* INT_STATUS1 / INT_ENABLE1 */
+#define MAX86150_INT_A_FULL BIT(7) /* FIFO almost full */
+#define MAX86150_INT_PPG_RDY BIT(6) /* new PPG sample ready */
+
+/* SYS_CTRL */
+#define MAX86150_SYS_SHDN BIT(1)
+#define MAX86150_SYS_RESET BIT(0)
+
+/* FIFO_CONFIG */
+#define MAX86150_FIFO_SMP_AVE GENMASK(7, 5)
+#define MAX86150_FIFO_ROLLOVER_EN BIT(4)
+#define MAX86150_FIFO_A_FULL GENMASK(3, 0)
+
+/* FIFO slot data-type codes */
+#define MAX86150_FD_NONE 0x0
+#define MAX86150_FD_LED1 0x1 /* Red PPG */
+#define MAX86150_FD_LED2 0x2 /* IR PPG */
+#define MAX86150_FD_ECG 0x9
+#define MAX86150_FIFO_FD1 GENMASK(3, 0)
+#define MAX86150_FIFO_FD2 GENMASK(7, 4)
+#define MAX86150_FIFO_FD3 GENMASK(3, 0)
+#define MAX86150_FIFO_FD4 GENMASK(7, 4)
+
+/* PPG_CONFIG1 */
+#define MAX86150_PPG_ADC_RGE GENMASK(7, 6)
+#define MAX86150_PPG_SR GENMASK(5, 1)
+
+/* Geometry */
+#define MAX86150_FIFO_DEPTH 32
+#define MAX86150_BYTES_PER_SLOT 3 /* 24-bit word per slot */
+#define MAX86150_NUM_SLOTS 3 /* Red, IR, ECG */
+#define MAX86150_SAMPLE_BYTES (MAX86150_NUM_SLOTS * MAX86150_BYTES_PER_SLOT)
+/* Samples present in the FIFO when the A_FULL interrupt fires */
+#define MAX86150_A_FULL_SAMPLES (MAX86150_FIFO_DEPTH - MAX86150_FIFO_A_FULL_VAL)
+
+/* Default hardware configuration */
+#define MAX86150_LED_PA_DEFAULT 0x3F /* ~50 mA */
+#define MAX86150_PPG_SR_100HZ 4 /* PPG_SR field value for 100 Hz */
+#define MAX86150_PPG_ADC_RGE_16384 2 /* 16384 nA full scale */
+/* Fire A_FULL when 17 slots remain (32 - 15 = 17 samples in FIFO) */
+#define MAX86150_FIFO_A_FULL_VAL 15
+
+/* Scan element indices */
+enum max86150_scan_idx {
+ MAX86150_IDX_PPG_RED = 0,
+ MAX86150_IDX_PPG_IR = 1,
+ MAX86150_IDX_ECG = 2,
+ MAX86150_IDX_TS,
+};
+
+/**
+ * struct max86150_data - driver private state
+ * @regmap: register map for this device
+ * @dev: parent device (for dev_err logging)
+ * @trig: IIO hardware trigger backed by the device interrupt line
+ * @sample_period_ns: sample period in nanoseconds (set from configured rate)
+ * @fifo_raw: DMA-safe buffer for regmap_noinc_read() FIFO bursts.
+ * Padded to ARCH_DMA_MINALIGN bytes so that @buf starts
+ * in the next cacheline and the two fields never share a
+ * cacheline -- required by CONFIG_DMA_API_DEBUG.
+ * @buf: IIO push buffer sized for worst-case (all 3 channels
+ * active): 3 x s32 (12 bytes) + 4-byte pad + s64
+ * timestamp = 24 bytes. __aligned(8) satisfies
+ * iio_push_to_buffers_with_timestamp().
+ */
+struct max86150_data {
+ struct regmap *regmap;
+ struct device *dev;
+ struct iio_trigger *trig;
+ u32 sample_period_ns;
+ u8 fifo_raw[ALIGN(MAX86150_SAMPLE_BYTES, ARCH_DMA_MINALIGN)]
+ __aligned(ARCH_DMA_MINALIGN);
+ s32 buf[6] __aligned(8);
+};
+
+/* IIO channel specification */
+
+static const struct iio_chan_spec max86150_channels[] = {
+ {
+ /* PPG Red LED - optical intensity, 19-bit unsigned */
+ .type = IIO_INTENSITY,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_RED,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .scan_index = MAX86150_IDX_PPG_RED,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 19,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ /* PPG IR LED - optical intensity, 19-bit unsigned */
+ .type = IIO_INTENSITY,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_IR,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .scan_index = MAX86150_IDX_PPG_IR,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 19,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ /* ECG biopotential - voltage, 18-bit signed two's complement */
+ .type = IIO_VOLTAGE,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .scan_index = MAX86150_IDX_ECG,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 18,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(MAX86150_IDX_TS),
+};
+
+/* Regmap configuration */
+
+static const struct regmap_config max86150_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX86150_REG_PART_ID,
+};
+
+/* FIFO helper */
+
+/**
+ * max86150_read_one_sample - burst-read one complete 3-slot FIFO entry
+ * @data: driver state
+ * @ppg_red: out - 19-bit PPG Red ADC value (unsigned)
+ * @ppg_ir: out - 19-bit PPG IR ADC value (unsigned)
+ * @ecg: out - 18-bit ECG ADC value (sign-extended to s32)
+ *
+ * Each FIFO entry is 9 bytes (3 slots x 3 bytes). FIFO_DATA is a
+ * streaming register - the address does not auto-increment on each
+ * byte, so regmap_noinc_read() is used instead of regmap_bulk_read().
+ *
+ * Byte layout in the 24-bit FIFO word (MSB first):
+ * PPG 19-bit unsigned: bits [18:0], top 5 bits are always zero
+ * ECG 18-bit signed: bits [17:0], top 6 bits are sign extension
+ */
+static int max86150_read_one_sample(struct max86150_data *data,
+ u32 *ppg_red, u32 *ppg_ir, s32 *ecg)
+{
+ int ret;
+
+ /*
+ * Use data->fifo_raw (heap memory) not a local array so the buffer is
+ * DMA-mappable for I2C host controllers that use DMA for burst reads.
+ */
+ ret = regmap_noinc_read(data->regmap, MAX86150_REG_FIFO_DATA,
+ data->fifo_raw, sizeof(data->fifo_raw));
+ if (ret)
+ return ret;
+
+ /* Bytes [0..2]: PPG Red - 19-bit value in bits [18:0] */
+ *ppg_red = (u32)(data->fifo_raw[0] & 0x07) << 16 |
+ (u32)data->fifo_raw[1] << 8 | data->fifo_raw[2];
+
+ /* Bytes [3..5]: PPG IR - same format */
+ *ppg_ir = (u32)(data->fifo_raw[3] & 0x07) << 16 |
+ (u32)data->fifo_raw[4] << 8 | data->fifo_raw[5];
+
+ /* Bytes [6..8]: ECG - 18-bit signed, sign-extend to s32 */
+ *ecg = sign_extend32((u32)(data->fifo_raw[6] & 0x03) << 16 |
+ (u32)data->fifo_raw[7] << 8 | data->fifo_raw[8], 17);
+
+ return 0;
+}
+
+/* IIO read_raw */
+
+static int max86150_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max86150_data *data = iio_priv(indio_dev);
+ unsigned int ppg_rdy_status;
+ u32 ppg_red, ppg_ir;
+ s32 ecg;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /*
+ * Claim direct mode to prevent concurrent sysfs reads from
+ * corrupting the FIFO pointers while a triggered buffer
+ * capture is active.
+ */
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ /*
+ * Single-shot path: wake the device, flush stale FIFO data,
+ * wait one sample period, read, then return to shutdown so
+ * the LEDs are not drawing current when idle.
+ */
+ ret = regmap_update_bits(data->regmap, MAX86150_REG_SYS_CTRL,
+ MAX86150_SYS_SHDN, 0);
+ if (!ret)
+ ret = regmap_write(data->regmap,
+ MAX86150_REG_FIFO_WR_PTR, 0);
+ if (!ret)
+ ret = regmap_write(data->regmap,
+ MAX86150_REG_OVF_COUNTER, 0);
+ if (!ret)
+ ret = regmap_write(data->regmap,
+ MAX86150_REG_FIFO_RD_PTR, 0);
+ if (ret) {
+ regmap_update_bits(data->regmap, MAX86150_REG_SYS_CTRL,
+ MAX86150_SYS_SHDN, MAX86150_SYS_SHDN);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ }
+
+ /*
+ * Poll PPG_RDY rather than sleeping a fixed interval -- the
+ * internal oscillator may start slower than nominal, leaving
+ * the FIFO empty if we read too early. 25 ms timeout covers
+ * more than two 100 Hz sample periods.
+ */
+ ret = regmap_read_poll_timeout(data->regmap,
+ MAX86150_REG_INT_STATUS1,
+ ppg_rdy_status,
+ ppg_rdy_status & MAX86150_INT_PPG_RDY,
+ 1000, 25000);
+ if (ret) {
+ regmap_update_bits(data->regmap, MAX86150_REG_SYS_CTRL,
+ MAX86150_SYS_SHDN, MAX86150_SYS_SHDN);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ }
+
+ ret = max86150_read_one_sample(data, &ppg_red, &ppg_ir, &ecg);
+ regmap_update_bits(data->regmap, MAX86150_REG_SYS_CTRL,
+ MAX86150_SYS_SHDN, MAX86150_SYS_SHDN);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+
+ switch (chan->scan_index) {
+ case MAX86150_IDX_PPG_RED:
+ *val = ppg_red;
+ break;
+ case MAX86150_IDX_PPG_IR:
+ *val = ppg_ir;
+ break;
+ case MAX86150_IDX_ECG:
+ *val = ecg;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info max86150_iio_info = {
+ .read_raw = max86150_read_raw,
+ .validate_trigger = iio_trigger_validate_own_device,
+};
+
+/* Trigger */
+
+/*
+ * Control device power and the FIFO almost-full interrupt when the IIO
+ * triggered buffer is started (state=true) or stopped (state=false).
+ *
+ * On start: wake from shutdown, flush stale FIFO data so the first
+ * samples pushed to userspace are from after buffer enable, then
+ * unmask the A_FULL interrupt.
+ *
+ * On stop: mask the interrupt, then return to shutdown so the LED
+ * drivers do not draw current while capture is inactive.
+ */
+static int max86150_set_trigger_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct max86150_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (!state) {
+ ret = regmap_write(data->regmap, MAX86150_REG_INT_ENABLE1, 0);
+ regmap_update_bits(data->regmap, MAX86150_REG_SYS_CTRL,
+ MAX86150_SYS_SHDN, MAX86150_SYS_SHDN);
+ return ret;
+ }
+
+ ret = regmap_update_bits(data->regmap, MAX86150_REG_SYS_CTRL,
+ MAX86150_SYS_SHDN, 0);
+ if (!ret)
+ ret = regmap_write(data->regmap, MAX86150_REG_FIFO_WR_PTR, 0);
+ if (!ret)
+ ret = regmap_write(data->regmap, MAX86150_REG_OVF_COUNTER, 0);
+ if (!ret)
+ ret = regmap_write(data->regmap, MAX86150_REG_FIFO_RD_PTR, 0);
+ if (!ret)
+ ret = regmap_write(data->regmap, MAX86150_REG_INT_ENABLE1,
+ MAX86150_INT_A_FULL);
+ if (ret)
+ regmap_update_bits(data->regmap, MAX86150_REG_SYS_CTRL,
+ MAX86150_SYS_SHDN, MAX86150_SYS_SHDN);
+ return ret;
+}
+
+static const struct iio_trigger_ops max86150_trigger_ops = {
+ .set_trigger_state = max86150_set_trigger_state,
+};
+
+/* Triggered buffer */
+
+/**
+ * max86150_trigger_handler - threaded IRQ handler for FIFO almost-full
+ *
+ * Called by the IIO buffer infrastructure when the hardware trigger fires.
+ * Reads INT_STATUS1 to de-assert the interrupt, then drains all available
+ * FIFO samples into the IIO push buffer, packing only the channels that
+ * are currently enabled in active_scan_mask.
+ */
+static irqreturn_t max86150_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *idev = pf->indio_dev;
+ struct max86150_data *data = iio_priv(idev);
+ unsigned int status, wr_ptr, rd_ptr, ovf;
+ u32 ppg_red, ppg_ir;
+ s32 ecg;
+ s64 t_drain = 0;
+ int ret, n_avail, i, j;
+
+ /*
+ * Reading INT_STATUS1 clears the interrupt. Do this before touching
+ * the FIFO so the pin is de-asserted while we drain samples.
+ */
+ ret = regmap_read(data->regmap, MAX86150_REG_INT_STATUS1, &status);
+ if (ret)
+ goto done;
+
+ ret = regmap_read(data->regmap, MAX86150_REG_FIFO_WR_PTR, &wr_ptr);
+ if (ret)
+ goto done;
+ ret = regmap_read(data->regmap, MAX86150_REG_FIFO_RD_PTR, &rd_ptr);
+ if (ret)
+ goto done;
+
+ /*
+ * OVF_COUNTER: if non-zero the FIFO overflowed; drain all 32 slots.
+ * When wr_ptr == rd_ptr with no overflow the FIFO could be empty OR
+ * hold exactly MAX86150_FIFO_DEPTH entries (pointer wrap-around).
+ * Use the A_FULL status bit to disambiguate: if the IRQ fired for
+ * A_FULL but the computed count is zero, the FIFO wrapped to full.
+ */
+ ret = regmap_read(data->regmap, MAX86150_REG_OVF_COUNTER, &ovf);
+ if (ret)
+ goto done;
+
+ if (ovf > 0) {
+ n_avail = MAX86150_FIFO_DEPTH;
+ t_drain = ktime_get_ns();
+ } else {
+ n_avail = (wr_ptr - rd_ptr) & (MAX86150_FIFO_DEPTH - 1);
+ if (n_avail == 0 && (status & MAX86150_INT_A_FULL))
+ n_avail = MAX86150_FIFO_DEPTH;
+ }
+
+ /*
+ * In the normal (no-overflow) case, anchor timestamps to the A_FULL
+ * IRQ capture time: sample index (A_FULL_SAMPLES - 1) corresponds to
+ * pf->timestamp, eliminating scheduling-latency jitter.
+ *
+ * In the overflow case the hardware has overwritten the oldest samples;
+ * pf->timestamp reflects an earlier A_FULL event and is no longer a
+ * valid anchor. t_drain (set above) is used as a best-effort reference
+ * for the newest surviving sample instead.
+ */
+ for (i = 0; i < n_avail; i++) {
+ s64 ts;
+
+ if (ovf > 0)
+ ts = t_drain -
+ (s64)(n_avail - 1 - i) * data->sample_period_ns;
+ else
+ ts = pf->timestamp +
+ (s64)(i - (MAX86150_A_FULL_SAMPLES - 1)) *
+ data->sample_period_ns;
+
+ ret = max86150_read_one_sample(data, &ppg_red, &ppg_ir, &ecg);
+ if (ret)
+ break;
+
+ /*
+ * Zero the entire buffer before packing so padding bytes
+ * between enabled channels do not leak previous sample data
+ * to userspace when fewer than 3 channels are active.
+ */
+ memset(data->buf, 0, sizeof(data->buf));
+
+ /*
+ * Pack only active channels at consecutive positions [0..j-1].
+ * iio_push_to_buffers_with_timestamp() uses scan_bytes (which
+ * accounts for the active channel count) to place the timestamp,
+ * so static indexing would misplace it when fewer than 3
+ * channels are enabled.
+ */
+ j = 0;
+ if (test_bit(MAX86150_IDX_PPG_RED, idev->active_scan_mask))
+ data->buf[j++] = ppg_red;
+ if (test_bit(MAX86150_IDX_PPG_IR, idev->active_scan_mask))
+ data->buf[j++] = ppg_ir;
+ if (test_bit(MAX86150_IDX_ECG, idev->active_scan_mask))
+ data->buf[j++] = ecg;
+
+ iio_push_to_buffers_with_timestamp(idev, data->buf, ts);
+ }
+
+done:
+ iio_trigger_notify_done(idev->trig);
+ return IRQ_HANDLED;
+}
+
+/* Chip initialisation / teardown */
+
+static void max86150_powerdown(void *arg)
+{
+ struct max86150_data *data = arg;
+
+ regmap_write(data->regmap, MAX86150_REG_INT_ENABLE1, 0);
+ regmap_write(data->regmap, MAX86150_REG_SYS_CTRL, MAX86150_SYS_SHDN);
+}
+
+static int max86150_chip_init(struct max86150_data *data)
+{
+ int ret;
+
+ /* Software reset; the bit self-clears within 1 ms */
+ ret = regmap_write(data->regmap, MAX86150_REG_SYS_CTRL,
+ MAX86150_SYS_RESET);
+ if (ret)
+ return ret;
+ usleep_range(1000, 2000);
+
+ /*
+ * FIFO: no sample averaging, rollover enabled, assert A_FULL when
+ * 17 samples are in the FIFO (32 - 15 = 17 available to read).
+ */
+ ret = regmap_write(data->regmap, MAX86150_REG_FIFO_CONFIG,
+ MAX86150_FIFO_ROLLOVER_EN |
+ FIELD_PREP(MAX86150_FIFO_A_FULL,
+ MAX86150_FIFO_A_FULL_VAL));
+ if (ret)
+ return ret;
+
+ /* Slot 1 = PPG Red (LED1), Slot 2 = PPG IR (LED2) */
+ ret = regmap_write(data->regmap, MAX86150_REG_FIFO_DCTRL1,
+ FIELD_PREP(MAX86150_FIFO_FD1, MAX86150_FD_LED1) |
+ FIELD_PREP(MAX86150_FIFO_FD2, MAX86150_FD_LED2));
+ if (ret)
+ return ret;
+
+ /* Slot 3 = ECG, Slot 4 = disabled */
+ ret = regmap_write(data->regmap, MAX86150_REG_FIFO_DCTRL2,
+ FIELD_PREP(MAX86150_FIFO_FD3, MAX86150_FD_ECG) |
+ FIELD_PREP(MAX86150_FIFO_FD4, MAX86150_FD_NONE));
+ if (ret)
+ return ret;
+
+ /* PPG: 100 Hz sample rate, 16384 nA ADC full-scale range */
+ ret = regmap_write(data->regmap, MAX86150_REG_PPG_CONFIG1,
+ FIELD_PREP(MAX86150_PPG_ADC_RGE,
+ MAX86150_PPG_ADC_RGE_16384) |
+ FIELD_PREP(MAX86150_PPG_SR,
+ MAX86150_PPG_SR_100HZ));
+ if (ret)
+ return ret;
+
+ /* LED pulse amplitudes (~50 mA) */
+ ret = regmap_write(data->regmap, MAX86150_REG_LED1_PA,
+ MAX86150_LED_PA_DEFAULT);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, MAX86150_REG_LED2_PA,
+ MAX86150_LED_PA_DEFAULT);
+ if (ret)
+ return ret;
+
+ /*
+ * Record sample period for timestamp reconstruction in the trigger
+ * handler. The PPG_SR field is fixed to 100 Hz in this driver.
+ */
+ data->sample_period_ns = 10000000; /* 100 Hz = 10 ms */
+
+ /*
+ * Assert SYS_SHDN so the LED drivers do not draw current while
+ * the driver is bound but no capture is active.
+ * set_trigger_state() clears SHDN when the IIO buffer is enabled
+ * and re-asserts it when disabled. read_raw() wakes and sleeps
+ * the device around each single-shot read.
+ */
+ return regmap_write(data->regmap, MAX86150_REG_SYS_CTRL,
+ MAX86150_SYS_SHDN);
+}
+
+/* Probe */
+
+static int max86150_probe(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev;
+ struct max86150_data *data;
+ unsigned int part_id;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->dev = &client->dev;
+
+ /*
+ * Enable power supplies before any I2C access. Both supplies are
+ * optional in the device tree; use _optional variant so probing
+ * succeeds on boards that power the device from fixed rails with no
+ * DT regulator node.
+ */
+ ret = devm_regulator_get_enable_optional(&client->dev, "vdd");
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to get/enable vdd supply\n");
+
+ ret = devm_regulator_get_enable_optional(&client->dev, "avdd");
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to get/enable avdd supply\n");
+
+ ret = devm_regulator_get_enable_optional(&client->dev, "vref");
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to get/enable vref supply\n");
+
+ ret = devm_regulator_get_enable_optional(&client->dev, "leds");
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to get/enable leds supply\n");
+
+ data->regmap = devm_regmap_init_i2c(client, &max86150_regmap_config);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(data->regmap),
+ "Failed to initialise regmap\n");
+
+ ret = regmap_read(data->regmap, MAX86150_REG_PART_ID, &part_id);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "Cannot read part ID\n");
+
+ if (part_id != MAX86150_PART_ID_VAL)
+ return dev_err_probe(&client->dev, -ENODEV,
+ "Unexpected part ID 0x%02x (expected 0x%02x)\n",
+ part_id, MAX86150_PART_ID_VAL);
+
+ ret = max86150_chip_init(data);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "Chip initialisation failed\n");
+
+ ret = devm_add_action_or_reset(&client->dev, max86150_powerdown, data);
+ if (ret)
+ return ret;
+
+ indio_dev->name = "max86150";
+ indio_dev->channels = max86150_channels;
+ indio_dev->num_channels = ARRAY_SIZE(max86150_channels);
+ indio_dev->info = &max86150_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ /*
+ * If the device tree provides an interrupt, set up a hardware
+ * trigger so userspace can use the FIFO almost-full signal to
+ * drive capture without polling.
+ */
+ if (client->irq > 0) {
+ unsigned long irq_trig;
+
+ data->trig = devm_iio_trigger_alloc(&client->dev,
+ "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!data->trig)
+ return -ENOMEM;
+
+ data->trig->ops = &max86150_trigger_ops;
+ iio_trigger_set_drvdata(data->trig, indio_dev);
+
+ /*
+ * Honour the interrupt trigger type from the device tree.
+ * Fall back to falling-edge if the DT does not specify one.
+ *
+ * Use a threaded IRQ with IRQF_ONESHOT so that level-triggered
+ * lines are kept masked until max86150_trigger_handler reads
+ * INT_STATUS1 and de-asserts the interrupt. A hard handler
+ * returning IRQ_HANDLED without clearing the source would cause
+ * an immediate re-fire loop on active-low level interrupts.
+ */
+ irq_trig = irq_get_trigger_type(client->irq);
+ if (!irq_trig)
+ irq_trig = IRQF_TRIGGER_FALLING;
+
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL,
+ iio_trigger_generic_data_rdy_poll,
+ irq_trig | IRQF_ONESHOT,
+ "max86150", data->trig);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "Cannot request IRQ %d\n",
+ client->irq);
+
+ ret = devm_iio_trigger_register(&client->dev, data->trig);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "Cannot register trigger\n");
+
+ /*
+ * Set the default trigger so userspace can enable the buffer
+ * without a manual current_trigger write. iio_device_unregister()
+ * calls iio_trigger_put() to release this reference.
+ */
+ indio_dev->trig = iio_trigger_get(data->trig);
+ }
+
+ ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
+ iio_pollfunc_store_time,
+ max86150_trigger_handler,
+ NULL);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "Cannot setup triggered buffer\n");
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+/* I2C driver table */
+
+static const struct i2c_device_id max86150_id[] = {
+ { "max86150" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max86150_id);
+
+static const struct of_device_id max86150_of_match[] = {
+ { .compatible = "adi,max86150" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max86150_of_match);
+
+static struct i2c_driver max86150_driver = {
+ .driver = {
+ .name = "max86150",
+ .of_match_table = max86150_of_match,
+ },
+ .probe = max86150_probe,
+ .id_table = max86150_id,
+};
+module_i2c_driver(max86150_driver);
+
+MODULE_AUTHOR("Md Shofiqul Islam <shofiqtest@gmail.com>");
+MODULE_DESCRIPTION("MAX86150 ECG and PPG biosensor driver");
+MODULE_LICENSE("GPL");
--
2.51.1
^ permalink raw reply related
* [PATCH v5 3/3] MAINTAINERS: add entry for MAX86150 IIO health driver
From: Md Shofiqul Islam @ 2026-06-23 20:11 UTC (permalink / raw)
To: linux-iio
Cc: jic23, lars, conor, conor+dt, robh, krzk+dt, devicetree,
linux-kernel, Md Shofiqul Islam
In-Reply-To: <20260623201124.18271-1-shofiqtest@gmail.com>
Signed-off-by: Md Shofiqul Islam <shofiqtest@gmail.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 3115538ce829..361a7c8b99ea 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15863,6 +15863,13 @@ S: Supported
F: Documentation/devicetree/bindings/power/supply/maxim,max77976.yaml
F: drivers/power/supply/max77976_charger.c
+MAX86150 ECG AND PPG BIOSENSOR DRIVER
+M: Md Shofiqul Islam <shofiqtest@gmail.com>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/health/adi,max86150.yaml
+F: drivers/iio/health/max86150.c
+
MAXIM MUIC CHARGER DRIVERS FOR EXYNOS BASED BOARDS
M: Krzysztof Kozlowski <krzk@kernel.org>
L: linux-pm@vger.kernel.org
--
2.51.1
^ permalink raw reply related
* [PATCH v5 1/3] dt-bindings: iio: health: add adi,max86150
From: Md Shofiqul Islam @ 2026-06-23 20:11 UTC (permalink / raw)
To: linux-iio
Cc: jic23, lars, conor, conor+dt, robh, krzk+dt, devicetree,
linux-kernel, Md Shofiqul Islam
In-Reply-To: <20260623201124.18271-1-shofiqtest@gmail.com>
Add Device Tree binding schema for the Analog Devices MAX86150
integrated ECG and PPG biosensor.
The device exposes two PPG channels (Red LED and IR LED) and one ECG
channel over I2C, with a 32-entry hardware FIFO and an active-low
interrupt.
Signed-off-by: Md Shofiqul Islam <shofiqtest@gmail.com>
---
.../bindings/iio/health/adi,max86150.yaml | 78 +++++++++++++++++++
1 file changed, 78 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/health/adi,max86150.yaml
diff --git a/Documentation/devicetree/bindings/iio/health/adi,max86150.yaml b/Documentation/devicetree/bindings/iio/health/adi,max86150.yaml
new file mode 100644
index 000000000000..c191f4f1525b
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/health/adi,max86150.yaml
@@ -0,0 +1,78 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/health/adi,max86150.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices MAX86150 ECG and PPG Biosensor
+
+maintainers:
+ - Md Shofiqul Islam <shofiqtest@gmail.com>
+
+description: |
+ The MAX86150 is an integrated biosensor SoC that combines:
+ - Two PPG (photoplethysmography) channels: Red LED and IR LED,
+ for heart rate and blood-oxygen saturation (SpO2) measurement.
+ - One ECG (electrocardiogram) channel for biopotential recording.
+
+ The device communicates over I2C at up to 400 kHz and raises an
+ active-low interrupt when the 32-entry hardware FIFO reaches its
+ configurable almost-full threshold.
+
+ Datasheet:
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX86150.pdf
+
+properties:
+ compatible:
+ const: adi,max86150
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+ description: |
+ Active-low interrupt line. Asserted when the FIFO almost-full
+ threshold is reached or when a new PPG sample is ready.
+
+ vdd-supply:
+ description: Digital core power supply (1.8 V).
+
+ avdd-supply:
+ description: Analog core power supply (1.8 V).
+
+ vref-supply:
+ description: ECG reference voltage supply.
+
+ leds-supply:
+ description: LED anode supply, typically 3.3 V.
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+ - avdd-supply
+ - vref-supply
+ - leds-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ heart-rate@5e {
+ compatible = "adi,max86150";
+ reg = <0x5e>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
+ vdd-supply = <&vdd_1v8>;
+ avdd-supply = <&vdd_1v8>;
+ vref-supply = <&vdd_1v8>;
+ leds-supply = <&vdd_3v3>;
+ };
+ };
--
2.51.1
^ permalink raw reply related
* [PATCH v5 0/3] iio: health: add MAX86150 ECG and PPG biosensor driver
From: Md Shofiqul Islam @ 2026-06-23 20:11 UTC (permalink / raw)
To: linux-iio
Cc: jic23, lars, conor, conor+dt, robh, krzk+dt, devicetree,
linux-kernel, Md Shofiqul Islam
In-Reply-To: <20260623174600.17100-1-shofiqtest@gmail.com>
Changes in v5 (addressing Conor Dooley and Sashiko review of v4):
DT binding (Conor Dooley):
- Rename binding file and compatible to adi,max86150 per ADI policy for
former Maxim products.
- Add missing power supply properties: avdd-supply (analog core),
vref-supply (ECG reference); make all four supplies required.
- Fix vdd-supply description: remove voltage tolerances; say "1.8 V".
- Fix leds-supply description: say "typically 3.3 V" (datasheet max
is 5.0 V, not 5.5 V as previously stated).
- Rename example node to heart-rate@5e per prior DT art.
Driver (Sashiko):
- [High] Re-assign indio_dev->trig after trigger registration so the
hardware trigger is the default and iio_trigger_validate_own_device
accepts writes to current_trigger without a manual sysfs step.
iio_device_unregister() releases the reference via iio_trigger_put().
- [High] Switch from devm_request_irq() to devm_request_threaded_irq()
with IRQF_ONESHOT. A hard handler returning IRQ_HANDLED on a
level-triggered line unmasks the still-asserted INT pin and causes an
immediate re-fire loop. IRQF_ONESHOT keeps the line masked until
max86150_trigger_handler reads INT_STATUS1 and de-asserts the source.
- [Medium] Fix overflow timestamps: when OVF_COUNTER > 0, pf->timestamp
reflects an earlier A_FULL event and is no longer a valid anchor.
Capture ktime_get_ns() at drain time and reconstruct timestamps
relative to the newest surviving sample instead.
- [Medium] Pad fifo_raw to ARCH_DMA_MINALIGN bytes so that buf starts
in the next cacheline. Previously the 9-byte fifo_raw (DMA target)
and buf (CPU push buffer) shared a cacheline, violating DMA API
constraints flagged by CONFIG_DMA_API_DEBUG.
- [Medium] Replace blind usleep_range() in read_raw() with
regmap_read_poll_timeout() on the PPG_RDY bit. A fixed 11 ms sleep
risks reading an empty FIFO if the internal oscillator starts slower
than nominal; polling with a 25 ms timeout is robust.
Link: https://lore.kernel.org/linux-iio/20260623174600.17100-1-shofiqtest@gmail.com/
v4 cover letter
Md Shofiqul Islam (3):
dt-bindings: iio: health: add adi,max86150
iio: health: add MAX86150 ECG and PPG biosensor driver
MAINTAINERS: add entry for MAX86150 IIO health driver
.../bindings/iio/health/adi,max86150.yaml | 78 ++
MAINTAINERS | 7 +
drivers/iio/health/Kconfig | 23 +
drivers/iio/health/Makefile | 1 +
drivers/iio/health/max86150.c | 730 ++++++++++++++++++
5 files changed, 839 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/health/adi,max86150.yaml
create mode 100644 drivers/iio/health/max86150.c
--
2.51.1
^ permalink raw reply
* Re: [PATCH v5 3/7] drm/rcar-du: dsc: Add rudimentary Renesas R-Car V4H DSC driver
From: sashiko-bot @ 2026-06-23 20:01 UTC (permalink / raw)
To: Tomi Valkeinen; +Cc: robh, conor+dt, wsa+renesas, devicetree, dri-devel
In-Reply-To: <20260615-rcar-du-dsc-v5-3-aed1a28610e4@ideasonboard.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The DRM bridge remains registered and globally accessible after `pm_runtime_disable()` is called, creating a teardown order race during driver removal.
- [Medium] The sleeping function `pm_runtime_get_sync()` is called from an atomic context path (`rcar_dsc_atomic_enable`).
--
commit a8616c71b764dc9fa4ae244e47795371e7fead44
Author: Marek Vasut <marek.vasut+renesas@mailbox.org>
drm/rcar-du: dsc: Add rudimentary Renesas R-Car V4H DSC driver
The Renesas DSC Display Stream Compression is a bridge embedded in the
Renesas R-Car V4H SoC. The bridge performs VESA DSC encoding of up to
8k or 400 Mpixel/s. Add rudimentary driver, which currently acts as a
pass-through bridge and allows DSI1 to be operational on R-Car V4H.
> diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_dsc.c b/drivers/gpu/drm/renesas/rcar-du/rcar_dsc.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_dsc.c
[ ... ]
> +static void rcar_dsc_atomic_enable(struct drm_bridge *bridge,
> + struct drm_atomic_commit *commit)
> +{
> + struct rcar_dsc *dsc = bridge_to_rcar_dsc(bridge);
> +
> + WARN_ON(pm_runtime_get_sync(dsc->dev) < 0);
[Severity: Medium]
Is it safe to call the sleeping function pm_runtime_get_sync() here?
Since rcar_dsc_atomic_enable() is called during non-blocking atomic commits,
doesn't this path execute in atomic context where sleeping is forbidden and
can lead to kernel warnings or deadlocks?
> +}
[ ... ]
> +static int rcar_dsc_probe(struct platform_device *pdev)
> +{
[ ... ]
> + pm_runtime_enable(&pdev->dev);
> +
> + ret = devm_drm_bridge_add(dev, &dsc->bridge);
> + if (ret)
> + goto err_runtime_disable;
> +
> + return 0;
> +
> +err_runtime_disable:
> + pm_runtime_disable(&pdev->dev);
> +
> + return ret;
> +}
> +
> +static void rcar_dsc_remove(struct platform_device *pdev)
> +{
> + pm_runtime_disable(&pdev->dev);
> +}
[Severity: High]
Does this create a teardown order race during driver removal?
Because devm_drm_bridge_add() defers unregistration until after
rcar_dsc_remove() returns, the bridge remains globally accessible to the DRM
subsystem after pm_runtime_disable() has already shut down power management.
If a concurrent modeset calls rcar_dsc_atomic_enable() during this window,
won't pm_runtime_get_sync() return -EACCES and trigger the WARN_ON since
power management is disabled?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260615-rcar-du-dsc-v5-0-aed1a28610e4@ideasonboard.com?part=3
^ permalink raw reply
* [PATCH 5/5] arm64: dts: ti: k3-am62-verdin: Add Mezzanine with Toradex Display 10.1" LVDS
From: Leonardo Costa @ 2026-06-23 19:57 UTC (permalink / raw)
To: laurent.pinchart, neil.armstrong, jesszhan0024, maarten.lankhorst,
mripard, tzimmermann, airlied, simona, robh, krzk+dt, conor+dt,
nm, vigneshr, kristo, prabhakar.mahadev-lad.rj, thierry.reding,
sam
Cc: leonardo.costa, dri-devel, devicetree, linux-kernel,
linux-arm-kernel
In-Reply-To: <20260623195741.495734-1-leoreis.costa@gmail.com>
From: Leonardo Costa <leonardo.costa@toradex.com>
Add a device tree overlay enabling the Toradex Capacitive Touch Display
10.1" LVDS V2 on the Verdin Development Board with Verdin AM62 Mezzanine
expansion board. The panel connects via the AM62 OLDI0 on the Mezzanine
LVDS interface (J10). The panel is an Opto Logic SCX1001511GGC49 10.1" WXGA
TFT LCD LVDS and the touch input is provided by an ILITEK ILI251x
capacitive touch controller.
Link: https://developer.toradex.com/hardware/accessories/displays/capacitive-touch-display-101inch-lvds
Signed-off-by: Leonardo Costa <leonardo.costa@toradex.com>
---
arch/arm64/boot/dts/ti/Makefile | 5 +
...zanine-panel-cap-touch-10inch-lvds-v2.dtso | 109 ++++++++++++++++++
2 files changed, 114 insertions(+)
create mode 100644 arch/arm64/boot/dts/ti/k3-am625-verdin-dev-mezzanine-panel-cap-touch-10inch-lvds-v2.dtso
diff --git a/arch/arm64/boot/dts/ti/Makefile b/arch/arm64/boot/dts/ti/Makefile
index 8b90fef11cb32..d9e0b1b80b28d 100644
--- a/arch/arm64/boot/dts/ti/Makefile
+++ b/arch/arm64/boot/dts/ti/Makefile
@@ -31,6 +31,7 @@ dtb-$(CONFIG_ARCH_K3) += k3-am625-sk.dtb
dtb-$(CONFIG_ARCH_K3) += k3-am625-tqma62xx-mba62xx.dtb
dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-dev-mezzanine-can.dtbo
dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-dev-mezzanine-panel-cap-touch-10inch-lvds.dtbo
+dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-dev-mezzanine-panel-cap-touch-10inch-lvds-v2.dtbo
dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-dev-nau8822-btl.dtbo
dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-dsi-to-hdmi.dtbo
dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-dsi-to-lvds-panel-cap-touch-10inch.dtbo
@@ -240,6 +241,9 @@ k3-am625-verdin-wifi-dev-mezzanine-can-dtbs := k3-am625-verdin-wifi-dev.dtb \
k3-am625-verdin-wifi-dev-mezzanine-panel-cap-touch-10inch-lvds-dtbs := \
k3-am625-verdin-wifi-dev.dtb \
k3-am625-verdin-dev-mezzanine-panel-cap-touch-10inch-lvds.dtbo
+k3-am625-verdin-wifi-dev-mezzanine-panel-cap-touch-10inch-lvds-v2-dtbs := \
+ k3-am625-verdin-wifi-dev.dtb \
+ k3-am625-verdin-dev-mezzanine-panel-cap-touch-10inch-lvds-v2.dtbo
k3-am625-verdin-wifi-dev-nau8822-btl-dtbs := k3-am625-verdin-wifi-dev.dtb \
k3-am625-verdin-dev-nau8822-btl.dtbo
k3-am625-verdin-wifi-dev-ov5640-24mhz-dtbs := k3-am625-verdin-wifi-dev.dtb \
@@ -362,6 +366,7 @@ dtb- += k3-am625-beagleplay-csi2-ov5640.dtb \
k3-am625-verdin-wifi-dev-dsi-to-lvds-v2-panel-cap-touch-10inch.dtb \
k3-am625-verdin-wifi-dev-mezzanine-can.dtb \
k3-am625-verdin-wifi-dev-mezzanine-panel-cap-touch-10inch-lvds.dtb \
+ k3-am625-verdin-wifi-dev-mezzanine-panel-cap-touch-10inch-lvds-v2.dtb \
k3-am625-verdin-wifi-dev-nau8822-btl.dtb \
k3-am625-verdin-wifi-dev-ov5640-24mhz.dtb \
k3-am625-verdin-wifi-dev-ov5640.dtb \
diff --git a/arch/arm64/boot/dts/ti/k3-am625-verdin-dev-mezzanine-panel-cap-touch-10inch-lvds-v2.dtso b/arch/arm64/boot/dts/ti/k3-am625-verdin-dev-mezzanine-panel-cap-touch-10inch-lvds-v2.dtso
new file mode 100644
index 0000000000000..26f6715463986
--- /dev/null
+++ b/arch/arm64/boot/dts/ti/k3-am625-verdin-dev-mezzanine-panel-cap-touch-10inch-lvds-v2.dtso
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+/*
+ * Copyright (c) Toradex
+ *
+ * Toradex Capacitive Touch Display 10.1" LVDS V2 on the Verdin AM62 Mezzanine
+ * LVDS interface (J10), used with the Verdin Development Board.
+ *
+ * https://developer.toradex.com/hardware/accessories/displays/capacitive-touch-display-101inch-lvds
+ * https://www.toradex.com/accessories/capacitive-touch-display-10.1-inch-lvds
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/pwm/pwm.h>
+
+&{/} {
+ backlight_pwm2: backlight-pwm2 {
+ compatible = "pwm-backlight";
+ brightness-levels = <0 45 63 88 119 158 203 255>;
+ default-brightness-level = <4>;
+ /* Verdin GPIO_4 (SODIMM 212) - LVDS_BKL_EN */
+ enable-gpios = <&mcu_gpio0 4 GPIO_ACTIVE_HIGH>;
+ /* Verdin PWM_2 (SODIMM 16) - LVDS_PWM */
+ pwms = <&epwm0 1 6666667 0>;
+ };
+
+ panel-lvds-native {
+ compatible = "optologic,scx1001511ggc49", "panel-lvds";
+ backlight = <&backlight_pwm2>;
+ data-mapping = "vesa-24";
+ height-mm = <136>;
+ width-mm = <217>;
+
+ panel-timing {
+ clock-frequency = <64500000>;
+ hactive = <1280>;
+ hback-porch = <20>;
+ hfront-porch = <20>;
+ hsync-len = <2>;
+ vactive = <800>;
+ vback-porch = <5>;
+ vfront-porch = <5>;
+ vsync-len = <3>;
+ de-active = <1>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ pixelclk-active = <0>;
+ };
+
+ port {
+ panel_lvds_native_in: endpoint {
+ remote-endpoint = <&oldi0_out>;
+ };
+ };
+ };
+};
+
+&dss {
+ status = "okay";
+};
+
+&dss_ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* DSS VP1: internal DPI output to OLDIx */
+ port@0 {
+ reg = <0>;
+
+ dss0_out: endpoint {
+ remote-endpoint = <&oldi0_in>;
+ };
+ };
+};
+
+/* Verdin I2C_2_DSI */
+&main_i2c2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ touchscreen@41 {
+ compatible = "ilitek,ili251x";
+ reg = <0x41>;
+ /* Verdin GPIO_3 (SODIMM 210) - LVDS_TOUCH_INT# */
+ interrupt-parent = <&mcu_gpio0>;
+ interrupts = <3 IRQ_TYPE_EDGE_RISING>;
+ /* Verdin GPIO_2 (SODIMM 208) - LVDS_TOUCH_RST# */
+ reset-gpios = <&mcu_gpio0 2 GPIO_ACTIVE_LOW>;
+ };
+};
+
+&oldi0 {
+ status = "okay";
+};
+
+&oldi0_port0 {
+ oldi0_in: endpoint {
+ remote-endpoint = <&dss0_out>;
+ };
+};
+
+&oldi0_port1 {
+ oldi0_out: endpoint {
+ remote-endpoint = <&panel_lvds_native_in>;
+ };
+};
^ 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