* [PATCH] drm/meson: constrain the DW-HDMI regmap range
From: Zinan Zhou @ 2026-07-18 9:07 UTC (permalink / raw)
To: Neil Armstrong
Cc: Zinan Zhou, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Kevin Hilman, Jerome Brunet,
Martin Blumenstingl, dri-devel, linux-amlogic, linux-arm-kernel,
linux-kernel
The DesignWare HDMI regmap declares 0x10000 as its inclusive maximum
register. A regmap traversal can consequently read one byte past the
directly mapped G12A 64 KiB resource. It also treats the Meson TOP window
from offset 0x8000 as DWC registers. On the older indirect interface, the
address is truncated to 16 bits and aliases register zero.
Limit the regmap to 0x7e12, the last register defined by the supported
Synopsys HDMI controller. This also keeps register iteration below the
Meson TOP register window at offset 0x8000.
Fixes: 3f68be7d8e96 ("drm/meson: Add support for HDMI encoder and DW-HDMI bridge + PHY")
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Zinan Zhou <zinan@mieulab.com>
---
drivers/gpu/drm/meson/meson_dw_hdmi.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
index fef1702acb14..1004108fb7ca 100644
--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
+++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c
@@ -100,6 +100,8 @@
#define HDMITX_DWC_ADDR_REG 0x10
#define HDMITX_DWC_DATA_REG 0x14
#define HDMITX_DWC_CTRL_REG 0x18
+/* Last DWC register; direct layouts place the TOP block at offset 0x8000. */
+#define HDMITX_DWC_MAX_REGISTER 0x7e12
/* HHI Registers */
#define HHI_MEM_PD_REG0 0x100 /* 0x40 */
@@ -564,7 +566,7 @@ static const struct regmap_config meson_dw_hdmi_regmap_config = {
.val_bits = 8,
.reg_read = meson_dw_hdmi_reg_read,
.reg_write = meson_dw_hdmi_reg_write,
- .max_register = 0x10000,
+ .max_register = HDMITX_DWC_MAX_REGISTER,
.fast_io = true,
};
base-commit: b0a652436b892eb9a036a031b33099dca036faaa
--
2.43.0
^ permalink raw reply related
* [PATCH v3 2/4] media: synopsys: hdmirx: add HDMI audio capture support
From: Igor Paunovic @ 2026-07-18 8:57 UTC (permalink / raw)
To: Dmitry Osipenko, Mauro Carvalho Chehab
Cc: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Heiko Stuebner, linux-media, linux-rockchip, linux-arm-kernel,
kernel, devicetree, linux-kernel, Igor Paunovic
In-Reply-To: <20260718085728.6797-1-royalnet026@gmail.com>
The Synopsys DesignWare HDMI RX controller extracts the audio stream
embedded in the incoming HDMI signal and feeds it to an on-SoC I2S
controller. Expose it as an ALSA capture device by registering the
generic hdmi-codec as a child of the controller, so that a
simple-audio-card in the device tree can bind the HDMI RX audio DAI.
The sample rate is recovered from the ACR N/CTS values together with the
measured TMDS character rate. A periodic worker keeps the local audio
reference clock locked to the source by nudging it in small ppm steps to
hold the audio FIFO fill level near its target, which avoids FIFO
under/overflow and the resulting dropped samples.
Signed-off-by: Igor Paunovic <royalnet026@gmail.com>
---
Changes in v3:
- restore the v1 audio teardown in remove(): audio_shutdown() already
stops the worker when the stream closes, so the extra flag clear
and trailing cancel added in v2 were redundant (Dmitry Osipenko)
- rename the ACR read locals and add a comment documenting the
register byte packing
- drop the get_dai_id stub so OF-graph cards resolve the DAI index
from the reg property
Changes in v2:
- register the S/PDIF DAI so the indexes match the binding and reject
it with -EOPNOTSUPP until wired up (Sebastian Reichel)
- use platform_device_register_data() and drop the fixed 32-bit DMA
mask (Dmitry Osipenko)
- don't leave an ERR_PTR in audio_pdev on registration failure
- fix teardown ordering in remove()
- stop the worker before reprogramming shared state in hw_params()
- look up the "audio" clock by name instead of indexing clks[1]
- keep the worker on system_unbound_wq when re-arming
.../platform/synopsys/hdmirx/snps_hdmirx.c | 271 ++++++++++++++++++
.../platform/synopsys/hdmirx/snps_hdmirx.h | 8 +
2 files changed, 279 insertions(+)
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
index 9cceffa..8636944 100644
--- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
@@ -41,6 +41,8 @@
#include <media/videobuf2-dma-contig.h>
#include <media/videobuf2-v4l2.h>
+#include <sound/hdmi-codec.h>
+
#include "snps_hdmirx.h"
#include "snps_hdmirx_cec.h"
@@ -132,6 +134,13 @@ struct snps_hdmirx_dev {
struct delayed_work delayed_work_hotplug;
struct delayed_work delayed_work_res_change;
struct hdmirx_cec *cec;
+ struct platform_device *audio_pdev;
+ struct clk *audio_clk;
+ struct delayed_work audio_work;
+ u32 audio_clkrate;
+ u32 audio_fs;
+ int audio_pre_state;
+ bool audio_streaming;
struct mutex phy_rw_lock; /* to protect phy r/w configuration */
struct mutex stream_lock; /* to lock video stream capture */
struct mutex work_lock; /* to lock the critical section of hotplug event */
@@ -2283,6 +2292,13 @@ static int hdmirx_parse_dt(struct snps_hdmirx_dev *hdmirx_dev)
if (hdmirx_dev->num_clks < 1)
return -ENODEV;
+ for (int i = 0; i < hdmirx_dev->num_clks; i++) {
+ if (!strcmp(hdmirx_dev->clks[i].id, "audio")) {
+ hdmirx_dev->audio_clk = hdmirx_dev->clks[i].clk;
+ break;
+ }
+ }
+
hdmirx_dev->resets[HDMIRX_RST_A].id = "axi";
hdmirx_dev->resets[HDMIRX_RST_P].id = "apb";
hdmirx_dev->resets[HDMIRX_RST_REF].id = "ref";
@@ -2650,6 +2666,253 @@ static int hdmirx_register_cec(struct snps_hdmirx_dev *hdmirx_dev,
return 0;
}
+#define HDMIRX_AUDIO_INIT_FIFO_STATE 128
+#define HDMIRX_AUDIO_INIT_STATE (HDMIRX_AUDIO_INIT_FIFO_STATE * 4)
+
+static const int hdmirx_supported_fs[] = {
+ 32000, 44100, 48000, 88200, 96000, 176400, 192000, 768000, -1
+};
+
+static int hdmirx_audio_closest_fs(int fs)
+{
+ int i = 0, fs_t = hdmirx_supported_fs[0];
+
+ while (fs_t > 0) {
+ if (abs(fs - fs_t) <= 2000)
+ return fs_t;
+ fs_t = hdmirx_supported_fs[++i];
+ }
+ return 0;
+}
+
+/* Recover the incoming audio sample rate from the ACR N/CTS + TMDS clock. */
+static u32 hdmirx_audio_fs(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ u64 tmds_clk, fs_audio = 0;
+ u32 acr_cts, acr_n, tmdsqpclk_freq;
+ u32 acr_pb3_0, acr_pb7_4;
+
+ tmdsqpclk_freq = hdmirx_readl(hdmirx_dev, CMU_TMDSQPCLK_FREQ);
+ hdmirx_readl(hdmirx_dev, PKTDEC_ACR_PH2_1);
+ acr_pb3_0 = hdmirx_readl(hdmirx_dev, PKTDEC_ACR_PB3_0);
+ acr_pb7_4 = hdmirx_readl(hdmirx_dev, PKTDEC_ACR_PB7_4);
+ /*
+ * The packet decoder stores the ACR subpacket bytes with packet byte
+ * 0 in register bits [7:0], so byte-swap each word to line the bytes
+ * up: CTS is packet bytes 1-3 (PKTDEC_ACR_PB3_0) and N is packet
+ * bytes 4-6 (PKTDEC_ACR_PB7_4), 20 bits each.
+ */
+ acr_cts = be32_to_cpu((__force __be32)acr_pb3_0) & 0xfffff;
+ acr_n = (be32_to_cpu((__force __be32)acr_pb7_4) & 0x0fffff00) >> 8;
+ tmds_clk = tmdsqpclk_freq * 4 * 1000U;
+ if (acr_cts != 0) {
+ fs_audio = div_u64((tmds_clk * acr_n), acr_cts);
+ fs_audio /= 128;
+ fs_audio = hdmirx_audio_closest_fs(fs_audio);
+ }
+ return (u32)fs_audio;
+}
+
+/* Nudge the audio reference clock by +/- ppm to keep the FIFO balanced. */
+static void hdmirx_audio_clk_ppm_inc(struct snps_hdmirx_dev *hdmirx_dev, int ppm)
+{
+ int delta, inc;
+ long rate = hdmirx_dev->audio_clkrate;
+
+ if (ppm < 0) {
+ ppm = -ppm;
+ inc = -1;
+ } else {
+ inc = 1;
+ }
+ delta = (int)div64_u64((u64)rate * ppm + 500000, 1000000);
+ delta *= inc;
+ rate = hdmirx_dev->audio_clkrate + delta;
+ clk_set_rate(hdmirx_dev->audio_clk, rate);
+ hdmirx_dev->audio_clkrate = rate;
+}
+
+static int hdmirx_audio_clk_adjust(struct snps_hdmirx_dev *hdmirx_dev,
+ int total_offset, int single_offset)
+{
+ int schedule_time = 500;
+ int ppm = 10;
+ u32 offset_abs = abs(total_offset);
+
+ if (offset_abs > 200) {
+ ppm += 200;
+ schedule_time -= 100;
+ }
+ if (offset_abs > 100) {
+ ppm += 200;
+ schedule_time -= 100;
+ }
+ if (offset_abs > 32) {
+ ppm += 20;
+ schedule_time -= 100;
+ }
+ if (offset_abs > 16)
+ ppm += 20;
+ if (total_offset > 16 && single_offset > 0)
+ hdmirx_audio_clk_ppm_inc(hdmirx_dev, ppm);
+ else if (total_offset < -16 && single_offset < 0)
+ hdmirx_audio_clk_ppm_inc(hdmirx_dev, -ppm);
+ return schedule_time;
+}
+
+static void hdmirx_audio_fifo_reinit(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_writel(hdmirx_dev, AUDIO_FIFO_CONTROL, 1);
+ usleep_range(200, 210);
+ hdmirx_writel(hdmirx_dev, AUDIO_FIFO_CONTROL, 0);
+}
+
+/*
+ * Periodic worker that locks the local audio clock to the source by keeping
+ * the audio FIFO fill level close to its target, avoiding under/overflow.
+ */
+static void hdmirx_audio_work(struct work_struct *work)
+{
+ struct snps_hdmirx_dev *hdmirx_dev =
+ container_of(to_delayed_work(work), struct snps_hdmirx_dev, audio_work);
+ unsigned long delay = 200;
+ int cur, total, single;
+ u32 fifo, fs;
+
+ fs = hdmirx_audio_fs(hdmirx_dev);
+ fifo = hdmirx_readl(hdmirx_dev, AUDIO_FIFO_STATUS2);
+
+ if (fifo & (AFIFO_UNDERFLOW_ST | AFIFO_OVERFLOW_ST)) {
+ if (fs) {
+ clk_set_rate(hdmirx_dev->audio_clk, fs * 128);
+ hdmirx_dev->audio_clkrate = fs * 128;
+ hdmirx_dev->audio_fs = fs;
+ }
+ hdmirx_audio_fifo_reinit(hdmirx_dev);
+ hdmirx_dev->audio_pre_state = 0;
+ goto out;
+ }
+
+ cur = fifo & 0xffff;
+ total = cur - HDMIRX_AUDIO_INIT_STATE;
+ single = cur - hdmirx_dev->audio_pre_state;
+
+ if (fs && abs((int)fs - (int)hdmirx_dev->audio_fs) > 1000) {
+ clk_set_rate(hdmirx_dev->audio_clk, fs * 128);
+ hdmirx_dev->audio_clkrate = fs * 128;
+ hdmirx_dev->audio_fs = fs;
+ hdmirx_audio_fifo_reinit(hdmirx_dev);
+ hdmirx_dev->audio_pre_state = 0;
+ goto out;
+ }
+
+ if (cur != 0)
+ delay = hdmirx_audio_clk_adjust(hdmirx_dev, total, single);
+ hdmirx_dev->audio_pre_state = cur;
+out:
+ /* Only re-arm while streaming; avoids a self-reschedule race with
+ * the cancel_delayed_work_sync() callers (hw_params and
+ * audio_shutdown).
+ */
+ if (READ_ONCE(hdmirx_dev->audio_streaming))
+ queue_delayed_work(system_unbound_wq, &hdmirx_dev->audio_work,
+ msecs_to_jiffies(delay));
+}
+
+static int hdmirx_audio_hw_params(struct device *dev, void *data,
+ struct hdmi_codec_daifmt *fmt,
+ struct hdmi_codec_params *hparms)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+ u32 fs;
+
+ /* Only the I2S interface (DAI 0) is wired up so far. */
+ if (fmt->fmt == HDMI_SPDIF)
+ return -EOPNOTSUPP;
+
+ /*
+ * Stop the worker before touching the shared audio state; it is
+ * re-armed below once the new parameters are in place.
+ */
+ WRITE_ONCE(hdmirx_dev->audio_streaming, false);
+ cancel_delayed_work_sync(&hdmirx_dev->audio_work);
+
+ fs = hdmirx_audio_fs(hdmirx_dev);
+ if (!fs)
+ fs = hparms ? hparms->sample_rate : 48000;
+ if (!fs)
+ fs = 48000;
+
+ hdmirx_dev->audio_fs = fs;
+ hdmirx_dev->audio_clkrate = fs * 128;
+ clk_set_rate(hdmirx_dev->audio_clk, fs * 128);
+
+ hdmirx_audio_fifo_reinit(hdmirx_dev);
+ hdmirx_writel(hdmirx_dev, AUDIO_FIFO_THR_PASS, HDMIRX_AUDIO_INIT_FIFO_STATE);
+ hdmirx_writel(hdmirx_dev, AUDIO_FIFO_THR,
+ AFIFO_THR_LOW_QST(0x20) | AFIFO_THR_HIGH_QST(0x160));
+ hdmirx_writel(hdmirx_dev, AUDIO_FIFO_MUTE_THR,
+ AFIFO_THR_MUTE_LOW_QST(0x8) | AFIFO_THR_MUTE_HIGH_QST(0x178));
+
+ hdmirx_update_bits(hdmirx_dev, AUDIO_PROC_CONFIG0, I2S_EN, I2S_EN);
+ hdmirx_update_bits(hdmirx_dev, GLOBAL_SWENABLE, AUDIO_ENABLE, AUDIO_ENABLE);
+
+ hdmirx_dev->audio_pre_state = 0;
+ WRITE_ONCE(hdmirx_dev->audio_streaming, true);
+ mod_delayed_work(system_unbound_wq, &hdmirx_dev->audio_work,
+ msecs_to_jiffies(200));
+
+ dev_dbg(dev, "audio hw_params: fs=%u\n", fs);
+ return 0;
+}
+
+static void hdmirx_audio_shutdown(struct device *dev, void *data)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+
+ WRITE_ONCE(hdmirx_dev->audio_streaming, false);
+ cancel_delayed_work_sync(&hdmirx_dev->audio_work);
+ hdmirx_update_bits(hdmirx_dev, GLOBAL_SWENABLE, AUDIO_ENABLE, 0);
+}
+
+static const struct hdmi_codec_ops hdmirx_audio_codec_ops = {
+ .hw_params = hdmirx_audio_hw_params,
+ .audio_shutdown = hdmirx_audio_shutdown,
+};
+
+static int hdmirx_register_audio_device(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct hdmi_codec_pdata codec_data = {
+ .ops = &hdmirx_audio_codec_ops,
+ .i2s = 1,
+ .no_i2s_playback = 1,
+ .max_i2s_channels = 8,
+ /*
+ * The controller also has an S/PDIF audio interface (DAI 1 in
+ * the binding). Register it so DAI indexes match the binding,
+ * but reject its use in hw_params() until it is wired up.
+ */
+ .spdif = 1,
+ .no_spdif_playback = 1,
+ .data = hdmirx_dev,
+ };
+ struct platform_device *audio_pdev;
+
+ if (!hdmirx_dev->audio_clk)
+ return -ENODEV;
+
+ audio_pdev = platform_device_register_data(hdmirx_dev->dev,
+ HDMI_CODEC_DRV_NAME,
+ PLATFORM_DEVID_AUTO,
+ &codec_data, sizeof(codec_data));
+ if (IS_ERR(audio_pdev))
+ return PTR_ERR(audio_pdev);
+
+ hdmirx_dev->audio_pdev = audio_pdev;
+
+ return 0;
+}
+
static int hdmirx_probe(struct platform_device *pdev)
{
struct snps_hdmirx_dev *hdmirx_dev;
@@ -2701,6 +2964,7 @@ static int hdmirx_probe(struct platform_device *pdev)
hdmirx_delayed_work_hotplug);
INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_res_change,
hdmirx_delayed_work_res_change);
+ INIT_DELAYED_WORK(&hdmirx_dev->audio_work, hdmirx_audio_work);
hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
hdmirx_dev->timings = cea640x480;
@@ -2769,6 +3033,10 @@ static int hdmirx_probe(struct platform_device *pdev)
V4L2_DEBUGFS_IF_AVI, hdmirx_dev,
hdmirx_debugfs_if_read);
+ ret = hdmirx_register_audio_device(hdmirx_dev);
+ if (ret)
+ dev_warn(dev, "failed to register HDMI audio codec: %d\n", ret);
+
return 0;
err_unreg_video_dev:
@@ -2788,6 +3056,9 @@ static void hdmirx_remove(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+ if (hdmirx_dev->audio_pdev)
+ platform_device_unregister(hdmirx_dev->audio_pdev);
+
v4l2_debugfs_if_free(hdmirx_dev->infoframes);
debugfs_remove_recursive(hdmirx_dev->debugfs_dir);
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
index 31b887e..a99f54f 100644
--- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
@@ -81,6 +81,7 @@
#define DATAPATH_ENABLE BIT(12)
#define PKTFIFO_ENABLE BIT(11)
#define AVPUNIT_ENABLE BIT(8)
+#define AUDIO_ENABLE BIT(9)
#define MAIN_ENABLE BIT(0)
#define GLOBAL_TIMER_REF_BASE 0x0028
#define CORE_CONFIG 0x0050
@@ -177,20 +178,27 @@
#define VPROC_FMT_OVR_VALUE(x) UPDATE(x, 6, 4)
#define VPROC_FMT_OVR_EN BIT(0)
+#define AUDIO_FIFO_CONFIG 0x0460
#define AFIFO_FILL_RESTART BIT(0)
+#define AUDIO_FIFO_CONTROL 0x0464
#define AFIFO_INIT_P BIT(0)
+#define AUDIO_FIFO_THR_PASS 0x0468
+#define AUDIO_FIFO_THR 0x046c
#define AFIFO_THR_LOW_QST_MASK GENMASK(25, 16)
#define AFIFO_THR_LOW_QST(x) UPDATE(x, 25, 16)
#define AFIFO_THR_HIGH_QST_MASK GENMASK(9, 0)
#define AFIFO_THR_HIGH_QST(x) UPDATE(x, 9, 0)
+#define AUDIO_FIFO_MUTE_THR 0x0470
#define AFIFO_THR_MUTE_LOW_QST_MASK GENMASK(25, 16)
#define AFIFO_THR_MUTE_LOW_QST(x) UPDATE(x, 25, 16)
#define AFIFO_THR_MUTE_HIGH_QST_MASK GENMASK(9, 0)
#define AFIFO_THR_MUTE_HIGH_QST(x) UPDATE(x, 9, 0)
+#define AUDIO_FIFO_STATUS2 0x0478
#define AFIFO_UNDERFLOW_ST BIT(25)
#define AFIFO_OVERFLOW_ST BIT(24)
+#define AUDIO_PROC_CONFIG0 0x0480
#define SPEAKER_ALLOC_OVR_EN BIT(16)
#define I2S_BPCUV_EN BIT(4)
#define SPDIF_EN BIT(2)
--
2.53.0
^ permalink raw reply related
* [PATCH v3 3/4] arm64: dts: rockchip: add #sound-dai-cells to the RK3588 HDMI receiver
From: Igor Paunovic @ 2026-07-18 8:57 UTC (permalink / raw)
To: Dmitry Osipenko, Mauro Carvalho Chehab
Cc: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Heiko Stuebner, linux-media, linux-rockchip, linux-arm-kernel,
kernel, devicetree, linux-kernel, Igor Paunovic
In-Reply-To: <20260718085728.6797-1-royalnet026@gmail.com>
The Synopsys HDMI RX controller exposes two digital audio interfaces,
one for I2S (0) and one for S/PDIF (1). Add the #sound-dai-cells
property so audio cards can reference them as a codec, as documented
by the snps,dw-hdmi-rx binding.
Signed-off-by: Igor Paunovic <royalnet026@gmail.com>
---
New in v3.
| 1 +
1 file changed, 1 insertion(+)
--git a/arch/arm64/boot/dts/rockchip/rk3588-extra.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-extra.dtsi
index a264001..fa100e3 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-extra.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-extra.dtsi
@@ -338,6 +338,7 @@
reset-names = "axi", "apb", "ref", "biu";
rockchip,grf = <&sys_grf>;
rockchip,vo1-grf = <&vo1_grf>;
+ #sound-dai-cells = <1>;
status = "disabled";
};
--
2.53.0
^ permalink raw reply related
* [PATCH v3 4/4] arm64: dts: rockchip: enable HDMI RX audio capture on Orange Pi 5 Plus
From: Igor Paunovic @ 2026-07-18 8:57 UTC (permalink / raw)
To: Dmitry Osipenko, Mauro Carvalho Chehab
Cc: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Heiko Stuebner, linux-media, linux-rockchip, linux-arm-kernel,
kernel, devicetree, linux-kernel, Igor Paunovic
In-Reply-To: <20260718085728.6797-1-royalnet026@gmail.com>
Route the HDMI receiver audio to i2s7_8ch, the receive-only I2S
interface dedicated to HDMI RX, through a simple-audio-card with the
receiver as bitclock and frame master. Together with the audio
capture support in the snps_hdmirx driver this exposes a capture-only
ALSA card fed by the HDMI input.
Tested on the Orange Pi 5 Plus with multiple HDMI sources: capture
follows the source sample rate and stays in sync via the FIFO-level
clock tracking in the driver.
Signed-off-by: Igor Paunovic <royalnet026@gmail.com>
---
New in v3.
.../dts/rockchip/rk3588-orangepi-5-plus.dts | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts b/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts
index 9950d11..d5840e1 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts
@@ -37,6 +37,22 @@
};
};
+ hdmiin-sound {
+ compatible = "simple-audio-card";
+ simple-audio-card,name = "rockchip,hdmiin";
+ simple-audio-card,format = "i2s";
+ simple-audio-card,bitclock-master = <&hdmiin_codec>;
+ simple-audio-card,frame-master = <&hdmiin_codec>;
+
+ simple-audio-card,cpu {
+ sound-dai = <&i2s7_8ch>;
+ };
+
+ hdmiin_codec: simple-audio-card,codec {
+ sound-dai = <&hdmi_receiver 0>;
+ };
+ };
+
ir-receiver {
compatible = "gpio-ir-receiver";
gpios = <&gpio4 RK_PB3 GPIO_ACTIVE_LOW>;
@@ -247,6 +263,10 @@
status = "okay";
};
+&i2s7_8ch {
+ status = "okay";
+};
+
&led_blue_gpio {
gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>;
status = "okay";
--
2.53.0
^ permalink raw reply related
* [PATCH v3 0/4] media: synopsys: hdmirx: add HDMI audio capture support
From: Igor Paunovic @ 2026-07-18 8:57 UTC (permalink / raw)
To: Dmitry Osipenko, Mauro Carvalho Chehab
Cc: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Heiko Stuebner, linux-media, linux-rockchip, linux-arm-kernel,
kernel, devicetree, linux-kernel, Igor Paunovic
This series adds audio capture support to the Synopsys DesignWare HDMI
RX controller used on the Rockchip RK3588: the controller's audio FIFO
is exposed through an ASoC hdmi-codec device, with a periodic worker
that keeps the local audio clock locked to the incoming stream by
tracking the FIFO fill level. Together with the two new dts patches
this yields a capture-only ALSA card fed by the HDMI input.
Validated on the Orange Pi 5 Plus against multiple HDMI sources:
capture follows the source sample rate (44.1/48 kHz switches
included), survives repeated stream start/stop cycles and SIGKILL of
the capturing process, and stays free of FIFO under/overruns via the
clock tracking.
Changes in v3:
- Restored the v1 audio teardown in remove(): the worker is only
armed while a capture stream is active and audio_shutdown() already
stops it, so the extra flag clear and trailing cancel added in v2
were redundant (Dmitry Osipenko's review; the v2 change came from
an incorrect automated-review finding)
- Renamed the ACR read locals and added a comment documenting the
register byte packing that makes the swap correct
- Dropped the get_dai_id stub so OF-graph cards resolve the DAI index
from the reg property naturally
- New patches 3 and 4: dts enablement, so the series is testable end
to end (requested by Dmitry). Patch 3 adds #sound-dai-cells to the
RK3588 hdmi_receiver node, patch 4 wires up the Orange Pi 5 Plus
hdmiin sound card. Both can go through the rockchip tree instead if
preferred.
Link to v2: https://lore.kernel.org/linux-media/20260715200834.8486-1-royalnet026@gmail.com/
Link to v1 (RFC): https://lore.kernel.org/linux-media/20260715051939.64652-1-royalnet026@gmail.com/
Igor Paunovic (4):
dt-bindings: media: snps,dw-hdmi-rx: add #sound-dai-cells
media: synopsys: hdmirx: add HDMI audio capture support
arm64: dts: rockchip: add #sound-dai-cells to the RK3588 HDMI receiver
arm64: dts: rockchip: enable HDMI RX audio capture on Orange Pi 5 Plus
.../bindings/media/snps,dw-hdmi-rx.yaml | 9 +
.../arm64/boot/dts/rockchip/rk3588-extra.dtsi | 1 +
.../dts/rockchip/rk3588-orangepi-5-plus.dts | 20 ++
.../platform/synopsys/hdmirx/snps_hdmirx.c | 271 ++++++++++++++++++
.../platform/synopsys/hdmirx/snps_hdmirx.h | 8 +
5 files changed, 309 insertions(+)
--
2.53.0
^ permalink raw reply
* [PATCH v3 1/4] dt-bindings: media: snps,dw-hdmi-rx: add #sound-dai-cells
From: Igor Paunovic @ 2026-07-18 8:57 UTC (permalink / raw)
To: Dmitry Osipenko, Mauro Carvalho Chehab
Cc: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Heiko Stuebner, linux-media, linux-rockchip, linux-arm-kernel,
kernel, devicetree, linux-kernel, Igor Paunovic
In-Reply-To: <20260718085728.6797-1-royalnet026@gmail.com>
The HDMI RX controller can expose the audio embedded in the incoming
HDMI stream as an ALSA capture device. Document the #sound-dai-cells
property so that a sound card can reference the HDMI RX audio DAI.
Signed-off-by: Igor Paunovic <royalnet026@gmail.com>
---
Changes in v3:
- no changes
Changes in v2 (all suggested by Sebastian Reichel):
- reworded the commit message to describe the hardware
- property description now documents the DAI indexes (0 = I2S,
1 = S/PDIF)
- moved the consumer reference into the binding example
.../devicetree/bindings/media/snps,dw-hdmi-rx.yaml | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
index b7f6c87..fa6cd0d 100644
--- a/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
+++ b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
@@ -78,6 +78,13 @@ properties:
The phandle of the syscon node for the Video Output GRF register
to enable EDID transfer through SDAIN and SCLIN.
+ "#sound-dai-cells":
+ const: 1
+ description:
+ The HDMI RX controller has two digital audio interfaces, one for
+ I2S and one for S/PDIF. The DAI cell selects the interface, 0 for
+ I2S and 1 for S/PDIF.
+
required:
- compatible
- reg
@@ -129,4 +136,6 @@ examples:
pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_5v_detection>;
pinctrl-names = "default";
hpd-gpios = <&gpio1 22 GPIO_ACTIVE_LOW>;
+ /* referenced by a sound card as <&hdmi_receiver 0> (0: I2S, 1: S/PDIF) */
+ #sound-dai-cells = <1>;
};
--
2.53.0
^ permalink raw reply related
* Re: [PATCH 5/6] iommu: Add Broadcom BCM2712 IOMMU driver
From: Daniel Drake @ 2026-07-18 8:39 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Joerg Roedel (AMD), Will Deacon, Robin Murphy, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
Broadcom internal kernel review list, iommu, linux-kernel,
devicetree, linux-rpi-kernel, linux-arm-kernel, nick.hollinghurst
In-Reply-To: <20260712221125.GD1835788@ziepe.ca>
Hi,
On 12/07/2026 23:11, Jason Gunthorpe wrote:
>> +static int bcm2712_iommu_map_pages(struct iommu_domain *domain,
>> + unsigned long iova, phys_addr_t paddr,
>> + size_t pgsize, size_t pgcount, int prot,
>> + gfp_t gfp, size_t *mapped)
>> +{
>> + struct bcm2712_iommu_domain *mydomain = to_bcm2712_domain(domain);
>> + struct pt_iommu *pt = &mydomain->pt.iommu;
>> +
>> + return pt->ops->map_range(pt, bcm2712_iova_to_offset(mydomain, iova),
>> + paddr, pgsize * pgcount, prot, gfp, mapped);
>> +}
>
> These ops should not be present, the iommpt provides the ops directly
> through a macro;
>
>> +static const struct iommu_ops bcm2712_iommu_ops = {
>> + .identity_domain = &bcm2712_identity_domain,
>> + .domain_alloc_paging = bcm2712_iommu_domain_alloc,
>> + .probe_device = bcm2712_iommu_probe_device,
>> + .device_group = generic_single_device_group,
>> + .of_xlate = bcm2712_iommu_of_xlate,
>> + .default_domain_ops = &(const struct iommu_domain_ops) {
>> + .attach_dev = bcm2712_iommu_attach_dev,
>> + .iotlb_sync = bcm2712_iommu_sync,
>> + .iotlb_sync_map = bcm2712_iommu_sync_map,
>> + .flush_iotlb_all = bcm2712_iommu_sync_all,
>> + .free = bcm2712_iommu_domain_free,
>> + .map_pages = bcm2712_iommu_map_pages,
>> + .unmap_pages = bcm2712_iommu_unmap_pages,
>> + .iova_to_phys = bcm2712_iova_to_phys,
>> + },
>
> Then use something like:
>
> IOMMU_PT_DOMAIN_OPS(bcm2712),
>
> To define all the page table related ops automatically.
I don't think I can use that because this setup uses an IOVA aperture at
base 0xA00000000, whereas generic_pt assumes it is managing a 0-indexed
virtual address space. So the driver has to intercept every incoming
IOVA and translate for the aperture, see how map_pages calls:
static inline unsigned long
bcm2712_iova_to_offset(struct bcm2712_iommu_domain *domain, unsigned
long iova)
{
return iova - domain->mmu->aperture_start;
}
That's why I also set is_iommupt=false. I will add a comment to make
this clear. Let me know if you see a better approach. I was wondering
about making iommupt understand apertures and translate accordingly, but
I imagine you would want to keep that kind of thing out of the generic
fast path?
Thanks for all the other feedback too - working on it!
Daniel
^ permalink raw reply
* Re: [PATCH v7 01/13] dt-bindings: phy: Add zx297520v3 USB phy documentation
From: Stefan Dösinger @ 2026-07-18 7:50 UTC (permalink / raw)
To: Vinod Koul
Cc: Krzysztof Kozlowski, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Brian Masney,
Neil Armstrong, Russell King, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, linux-phy
In-Reply-To: <20260717-uncovered-feathered-groundhog-eab16e@quoll>
[-- Attachment #1: Type: text/plain, Size: 1097 bytes --]
Am Freitag, 17. Juli 2026, 09:32:18 Ostafrikanische Zeit schrieb Krzysztof
Kozlowski:
> On Fri, Jul 17, 2026 at 12:35:37AM +0300, Stefan Dösinger wrote:
> > + resets:
> > + items:
> > + - description: USB phy reset
> > + - description: HSIC phy reset
>
> Why do you reference here resets from the parent? That's a clear signal
> you have one device, so one device node regardless of Linux phy core
> behavior.
This is a follow-up question for the PHY maintainers, if they notice the E-
Mail thread: I'm trying to resolve an apparent conflict between DT
expectations and a check in the PHY core:
device_set_of_node_from_dev(dev, dev->parent);
...
devm_of_phy_provider_register(dev, xlate);
Passes the letter of the check in __of_phy_provider_register(), but I am not
sure about the spirit.
"dev" here is a MFD child node without a DT node in accordance to DT's
writing-bindings instructions ("DON’T create nodes just for the sake of
instantiating drivers"). The PHY core prohibits a child to register a phy
provider to its parent node. Why?
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 870 bytes --]
^ permalink raw reply
* Re: [RFC PATCH v2 1/8] dt-bindings: npu: rockchip: add rockchip,rk3576-rknn-core
From: Krzysztof Kozlowski @ 2026-07-18 7:49 UTC (permalink / raw)
To: Jiaxing Hu, tomeu, heiko, robh, krzk+dt, conor+dt, joro, will,
robin.murphy, ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, dri-devel, linux-rockchip, iommu, linux-pm,
devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <20260718031146.3368811-2-gahing@gahingwoo.com>
On 18/07/2026 05:11, Jiaxing Hu wrote:
> Document the RK3576 NPU core: add rockchip,rk3576-rknn-core to the
> compatible enum. The RK3576 has no NPU SRAM supply, so make sram-supply
> required only for rockchip,rk3588-rknn-core
Full stop here.
via an allOf/if-then instead
> of dropping it from the shared required list (which would have weakened
> validation for RK3588).
This is redundant.
...
> - npu-supply
> - - sram-supply
> +
> +allOf:
> + - if:
> + properties:
> + compatible:
> + contains:
> + const: rockchip,rk3588-rknn-core
> + then:
> + required:
> + - sram-supply
>
else:
properties:
sram-supply: false
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v2 1/2] dt-bindings: arm: rockchip: Add Vicharak Vaaman board
From: Krzysztof Kozlowski @ 2026-07-18 7:47 UTC (permalink / raw)
To: Hrushiraj Gandhi, heiko
Cc: devicetree, linux-arm-kernel, linux-rockchip, linux-kernel, robh,
krzk+dt, conor+dt
In-Reply-To: <20260718065900.755631-2-hrushirajg23@gmail.com>
On 18/07/2026 08:58, Hrushiraj Gandhi wrote:
> Document the compatible string for the Vicharak Vaaman, an RK3399-based
> single-board computer.
>
> Signed-off-by: Hrushiraj Gandhi <hrushirajg23@gmail.com>
> ---
> Documentation/devicetree/bindings/arm/rockchip.yaml | 5 +++++
> 1 file changed, 5 insertions(+)
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH v2 2/2] arm64: dts: rockchip: Add Vicharak Vaaman board
From: Hrushiraj Gandhi @ 2026-07-18 6:59 UTC (permalink / raw)
To: heiko
Cc: devicetree, linux-arm-kernel, linux-rockchip, linux-kernel, robh,
krzk+dt, conor+dt, Hrushiraj Gandhi
In-Reply-To: <20260718065900.755631-1-hrushirajg23@gmail.com>
Add initial devicetree support for the Vicharak Vaaman, an RK3399-based
single-board computer.
Supported peripherals:
- RK808 PMIC with core/logic/IO regulators
- SYR827/SYR828 (vdd_cpu_b/vdd_gpu) CPU-big and GPU regulators
- Mali GPU
- Gigabit Ethernet (RGMII, via &gmac)
- eMMC (HS400, enhanced strobe)
- microSD card slot
- SARADC and TSADC
- PWM-based vdd_log regulator
- UART2 serial console
Signed-off-by: Hrushiraj Gandhi <hrushirajg23@gmail.com>
---
Changes in v2:
- Fix fixed-regulator node naming to use 'regulator-[voltage]' prefix
consistently throughout
---
arch/arm64/boot/dts/rockchip/Makefile | 1 +
.../dts/rockchip/rk3399-vicharak-vaaman.dts | 438 ++++++++++++++++++
2 files changed, 439 insertions(+)
create mode 100644 arch/arm64/boot/dts/rockchip/rk3399-vicharak-vaaman.dts
diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
index 761d82b4f4f2..e1b974502915 100644
--- a/arch/arm64/boot/dts/rockchip/Makefile
+++ b/arch/arm64/boot/dts/rockchip/Makefile
@@ -90,6 +90,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rockpro64.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rockpro64-screen.dtbo
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-sapphire.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-sapphire-excavator.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-vicharak-vaaman.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399pro-rock-pi-n10.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3528-armsom-sige1.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3528-nanopi-zero2.dtb
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-vicharak-vaaman.dts b/arch/arm64/boot/dts/rockchip/rk3399-vicharak-vaaman.dts
new file mode 100644
index 000000000000..dbf79b996500
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3399-vicharak-vaaman.dts
@@ -0,0 +1,438 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2026 Vicharak Computers Pvt Ltd
+ */
+
+/dts-v1/;
+#include "rk3399.dtsi"
+
+/ {
+ model = "Vicharak Vaaman";
+ compatible = "vicharak,vaaman", "rockchip,rk3399";
+
+ aliases {
+ ethernet0 = &gmac;
+ mmc0 = &sdhci;
+ mmc1 = &sdmmc;
+ };
+
+ chosen {
+ stdout-path = "serial2:1500000n8";
+ };
+
+ clkin_gmac: external-gmac-clock {
+ compatible = "fixed-clock";
+ clock-frequency = <125000000>;
+ clock-output-names = "clkin_gmac";
+ #clock-cells = <0>;
+ };
+
+ vcc12v_dcin: regulator-dc-12v {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc12v_dcin";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <12000000>;
+ regulator-max-microvolt = <12000000>;
+ };
+
+ vcc3v3_lan: regulator-vcc3v3-lan {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc3v3_lan";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ vin-supply = <&vcc3v3_sys>;
+ };
+
+ vcc3v3_sys: regulator-vcc3v3-sys {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc3v3_sys";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ vin-supply = <&vcc5v0_sys>;
+ };
+
+ vcc5v0_sys: regulator-vcc-sys {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc5v0_sys";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ vin-supply = <&vcc12v_dcin>;
+ };
+
+ vdd_log: regulator-vdd-log {
+ compatible = "pwm-regulator";
+ pwms = <&pwm2 0 25000 1>;
+ pwm-supply = <&vcc5v0_sys>;
+ regulator-name = "vdd_log";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1400000>;
+ };
+};
+
+&cpu_l0 {
+ cpu-supply = <&vdd_cpu_l>;
+};
+
+&cpu_l1 {
+ cpu-supply = <&vdd_cpu_l>;
+};
+
+&cpu_l2 {
+ cpu-supply = <&vdd_cpu_l>;
+};
+
+&cpu_l3 {
+ cpu-supply = <&vdd_cpu_l>;
+};
+
+&cpu_b0 {
+ cpu-supply = <&vdd_cpu_b>;
+};
+
+&cpu_b1 {
+ cpu-supply = <&vdd_cpu_b>;
+};
+
+&emmc_phy {
+ rockchip,enable-strobe-pulldown;
+ status = "okay";
+};
+
+&gmac {
+ assigned-clocks = <&cru SCLK_RMII_SRC>;
+ assigned-clock-parents = <&clkin_gmac>;
+ clock_in_out = "input";
+ phy-supply = <&vcc3v3_lan>;
+ /* TX/RX delays tuned for the Vaaman board PCB trace lengths */
+ phy-mode = "rgmii";
+ pinctrl-names = "default";
+ pinctrl-0 = <&rgmii_pins>;
+ snps,reset-gpio = <&gpio3 RK_PB7 GPIO_ACTIVE_LOW>;
+ snps,reset-active-low;
+ snps,reset-delays-us = <0 10000 50000>;
+ tx_delay = <0x28>;
+ rx_delay = <0x11>;
+ status = "okay";
+};
+
+&gpu {
+ mali-supply = <&vdd_gpu>;
+ status = "okay";
+};
+
+&i2c0 {
+ clock-frequency = <400000>;
+ i2c-scl-rising-time-ns = <168>;
+ i2c-scl-falling-time-ns = <4>;
+ status = "okay";
+
+ rk808: pmic@1b {
+ compatible = "rockchip,rk808";
+ reg = <0x1b>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <RK_PC5 IRQ_TYPE_LEVEL_LOW>;
+ #clock-cells = <1>;
+ clock-output-names = "xin32k", "rk808-clkout2";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pmic_int_l>;
+ system-power-controller;
+ wakeup-source;
+
+ vcc1-supply = <&vcc5v0_sys>;
+ vcc2-supply = <&vcc5v0_sys>;
+ vcc3-supply = <&vcc5v0_sys>;
+ vcc4-supply = <&vcc5v0_sys>;
+ vcc6-supply = <&vcc5v0_sys>;
+ vcc7-supply = <&vcc5v0_sys>;
+ vcc8-supply = <&vcc3v3_sys>;
+ vcc9-supply = <&vcc5v0_sys>;
+ vcc10-supply = <&vcc5v0_sys>;
+ vcc11-supply = <&vcc5v0_sys>;
+ vcc12-supply = <&vcc3v3_sys>;
+ vddio-supply = <&vcc_3v0>;
+
+ regulators {
+ vdd_center: DCDC_REG1 {
+ regulator-name = "vdd_center";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-ramp-delay = <6001>;
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ regulator-suspend-microvolt = <900000>;
+ };
+ };
+
+ vdd_cpu_l: DCDC_REG2 {
+ regulator-name = "vdd_cpu_l";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-initial-mode = <0x2>;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-ramp-delay = <6001>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcc_ddr: DCDC_REG3 {
+ regulator-name = "vcc_ddr";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ };
+ };
+
+ vcc_1v8: DCDC_REG4 {
+ regulator-name = "vcc_1v8";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <1800000>;
+ };
+ };
+
+ vcca1v8_codec: LDO_REG1 {
+ regulator-name = "vcca1v8_codec";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <1800000>;
+ };
+ };
+
+ vcca1v8_hdmi: LDO_REG2 {
+ regulator-name = "vcca1v8_hdmi";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcca_1v8: LDO_REG3 {
+ regulator-name = "vcca_1v8";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <1800000>;
+ };
+ };
+
+ vcc_sdio: LDO_REG4 {
+ regulator-name = "vcc_sdio";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <3000000>;
+ };
+ };
+
+ vcca3v0_codec: LDO_REG5 {
+ regulator-name = "vcca3v0_codec";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcc_1v5: LDO_REG6 {
+ regulator-name = "vcc_1v5";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <1500000>;
+ };
+ };
+
+ vcca0v9_hdmi: LDO_REG7 {
+ regulator-name = "vcca0v9_hdmi";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ regulator-suspend-microvolt = <900000>;
+ };
+ };
+
+ vcc_3v0: LDO_REG8 {
+ regulator-name = "vcc_3v0";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <3000000>;
+ };
+ };
+
+ vcc_cam: SWITCH_REG1 {
+ regulator-name = "vcc_cam";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcc_mipi: SWITCH_REG2 {
+ regulator-name = "vcc_mipi";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+ };
+ };
+
+ vdd_cpu_b: regulator@40 {
+ compatible = "silergy,syr827";
+ reg = <0x40>;
+ fcs,suspend-voltage-selector = <1>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&vsel1_pin>;
+ regulator-name = "vdd_cpu_b";
+ regulator-min-microvolt = <712500>;
+ regulator-max-microvolt = <1500000>;
+ regulator-ramp-delay = <1000>;
+ regulator-always-on;
+ regulator-boot-on;
+ vin-supply = <&vcc5v0_sys>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdd_gpu: regulator@41 {
+ compatible = "silergy,syr828";
+ reg = <0x41>;
+ fcs,suspend-voltage-selector = <1>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&vsel2_pin>;
+ regulator-name = "vdd_gpu";
+ regulator-min-microvolt = <712500>;
+ regulator-max-microvolt = <1500000>;
+ regulator-ramp-delay = <1000>;
+ regulator-always-on;
+ regulator-boot-on;
+ vin-supply = <&vcc5v0_sys>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+};
+
+&io_domains {
+ audio-supply = <&vcca1v8_codec>;
+ bt656-supply = <&vcc_3v0>;
+ gpio1830-supply = <&vcc_3v0>;
+ sdmmc-supply = <&vcc_sdio>;
+ status = "okay";
+};
+
+&pinctrl {
+ pmic {
+ pmic_int_l: pmic-int-l {
+ rockchip,pins =
+ <1 RK_PC5 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+
+ vsel1_pin: vsel1-pin {
+ rockchip,pins =
+ <1 RK_PC1 RK_FUNC_GPIO &pcfg_pull_down>;
+ };
+
+ vsel2_pin: vsel2-pin {
+ rockchip,pins =
+ <1 RK_PB6 RK_FUNC_GPIO &pcfg_pull_down>;
+ };
+ };
+};
+
+&pmu_io_domains {
+ pmu1830-supply = <&vcc_3v0>;
+ status = "okay";
+};
+
+&pwm2 {
+ status = "okay";
+};
+
+&saradc {
+ vref-supply = <&vcc_1v8>;
+ status = "okay";
+};
+
+&sdhci {
+ max-frequency = <150000000>;
+ bus-width = <8>;
+ mmc-hs400-1_8v;
+ mmc-hs400-enhanced-strobe;
+ non-removable;
+ status = "okay";
+};
+
+&sdmmc {
+ bus-width = <4>;
+ cap-mmc-highspeed;
+ cap-sd-highspeed;
+ cd-gpios = <&gpio0 RK_PA7 GPIO_ACTIVE_LOW>;
+ disable-wp;
+ max-frequency = <150000000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&sdmmc_clk &sdmmc_cd &sdmmc_cmd &sdmmc_bus4>;
+ status = "okay";
+};
+
+&tsadc {
+ /* tshut mode 0:CRU 1:GPIO */
+ rockchip,hw-tshut-mode = <1>;
+ /* tshut polarity 0:LOW 1:HIGH */
+ rockchip,hw-tshut-polarity = <1>;
+ status = "okay";
+};
+
+&uart2 {
+ status = "okay";
+};
--
2.47.3
^ permalink raw reply related
* [PATCH v2 1/2] dt-bindings: arm: rockchip: Add Vicharak Vaaman board
From: Hrushiraj Gandhi @ 2026-07-18 6:58 UTC (permalink / raw)
To: heiko
Cc: devicetree, linux-arm-kernel, linux-rockchip, linux-kernel, robh,
krzk+dt, conor+dt, Hrushiraj Gandhi
In-Reply-To: <20260718065900.755631-1-hrushirajg23@gmail.com>
Document the compatible string for the Vicharak Vaaman, an RK3399-based
single-board computer.
Signed-off-by: Hrushiraj Gandhi <hrushirajg23@gmail.com>
---
Documentation/devicetree/bindings/arm/rockchip.yaml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/rockchip.yaml b/Documentation/devicetree/bindings/arm/rockchip.yaml
index 1a9dde18626d..7cfd3687768c 100644
--- a/Documentation/devicetree/bindings/arm/rockchip.yaml
+++ b/Documentation/devicetree/bindings/arm/rockchip.yaml
@@ -1306,6 +1306,11 @@ properties:
- const: turing,rk1
- const: rockchip,rk3588
+ - description: Vicharak Vaaman
+ items:
+ - const: vicharak,vaaman
+ - const: rockchip,rk3399
+
- description: WolfVision PF5 mainboard
items:
- const: wolfvision,rk3568-pf5
--
2.47.3
^ permalink raw reply related
* [PATCH v2 0/2] Add support for Vicharak Vaaman board
From: Hrushiraj Gandhi @ 2026-07-18 6:58 UTC (permalink / raw)
To: heiko
Cc: devicetree, linux-arm-kernel, linux-rockchip, linux-kernel, robh,
krzk+dt, conor+dt, Hrushiraj Gandhi
This series adds initial support for the Vicharak Vaaman, an RK3399-based
single-board computer.
Patch 1 documents the "vicharak,vaaman" compatible string in the Rockchip
arm bindings.
Patch 2 adds the board devicetree, enabling the RK808 PMIC with
SYR827/SYR828 CPU-big/GPU regulators, Mali GPU, Gigabit Ethernet,
eMMC, microSD, SARADC/TSADC, and UART2 console.
The vendor prefix 'vicharak' was previously accepted via:
https://lkml.org/lkml/2026/6/24/929
Changes in v2:
- Fix fixed-regulator node naming to use 'regulator-[voltage]' prefix
consistently throughout
Hrushiraj Gandhi (2):
dt-bindings: arm: rockchip: Add Vicharak Vaaman board
arm64: dts: rockchip: Add Vicharak Vaaman board
.../devicetree/bindings/arm/rockchip.yaml | 5 +
arch/arm64/boot/dts/rockchip/Makefile | 1 +
.../dts/rockchip/rk3399-vicharak-vaaman.dts | 438 ++++++++++++++++++
3 files changed, 444 insertions(+)
create mode 100644 arch/arm64/boot/dts/rockchip/rk3399-vicharak-vaaman.dts
--
2.47.3
^ permalink raw reply
* [PATCH] hwrng: stm32 - Fix runtime PM cleanup on registration failure
From: Can Peng @ 2026-07-18 3:29 UTC (permalink / raw)
To: olivia, herbert, mcoquelin.stm32, alexandre.torgue, danielt,
linusw
Cc: linux-crypto, linux-stm32, linux-arm-kernel, linux-kernel,
Can Peng, stable
stm32_rng_probe() enables autosuspend and runtime PM before registering the
hwrng. If devm_hwrng_register() fails, probe returns with runtime PM left
enabled and autosuspend still selected.
The remove callback also only disables runtime PM and does not undo
pm_runtime_use_autosuspend().
Use devm_pm_runtime_enable() so runtime PM is unwound automatically on
probe failure and driver detach. Since the managed cleanup also disables
runtime PM,drop the remove callback.
Fixes: c6a97c42e399 ("hwrng: stm32 - add support for STM32 HW RNG")
Cc: stable@vger.kernel.org
Signed-off-by: Can Peng <pengcan@kylinos.cn>
---
drivers/char/hw_random/stm32-rng.c | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/drivers/char/hw_random/stm32-rng.c b/drivers/char/hw_random/stm32-rng.c
index 9a8c00586ab0..f5bfe54c01dc 100644
--- a/drivers/char/hw_random/stm32-rng.c
+++ b/drivers/char/hw_random/stm32-rng.c
@@ -368,11 +368,6 @@ static int stm32_rng_init(struct hwrng *rng)
return 0;
}
-static void stm32_rng_remove(struct platform_device *ofdev)
-{
- pm_runtime_disable(&ofdev->dev);
-}
-
static int __maybe_unused stm32_rng_runtime_suspend(struct device *dev)
{
struct stm32_rng_private *priv = dev_get_drvdata(dev);
@@ -590,7 +585,9 @@ static int stm32_rng_probe(struct platform_device *ofdev)
pm_runtime_set_autosuspend_delay(dev, 100);
pm_runtime_use_autosuspend(dev);
- pm_runtime_enable(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
return devm_hwrng_register(dev, &priv->rng);
}
@@ -602,7 +599,6 @@ static struct platform_driver stm32_rng_driver = {
.of_match_table = stm32_rng_match,
},
.probe = stm32_rng_probe,
- .remove = stm32_rng_remove,
};
module_platform_driver(stm32_rng_driver);
--
2.53.0
^ permalink raw reply related
* Re: [RFC v3 2/2] arm64: kprobes: Allow reentering kprobes while single-stepping
From: Hongyan Xia @ 2026-07-18 3:17 UTC (permalink / raw)
To: Will Deacon
Cc: Pu Hu, Jiazi Li, catalin.marinas@arm.com,
linux-kernel@vger.kernel.org, naveen@kernel.org,
mhiramat@kernel.org, yang@os.amperecomputing.com,
davem@davemloft.net, linux-arm-kernel@lists.infradead.org,
linux-trace-kernel@vger.kernel.org
In-Reply-To: <alpuL10h7-OK2hFb@willie-the-truck>
On 7/18/2026 2:02 AM, Will Deacon wrote:
> On Fri, Jul 17, 2026 at 11:31:31AM +0000, Hongyan Xia wrote:
>> On 7/17/2026 7:01 PM, Will Deacon wrote:
>>> Thanks. So perf is run synchronously from the debug exception entry path,
>>
>> Yes, exactly.
>>
>>> rather than because of a second exception taking place. Got it. But then
>>> it sounds like we should really make the debug exception handling path (at
>>> least, the part that runs for handling the kprobe step) noinstr to avoid
>>> getting into this state to begin with. Is that practical?
>>
>> Not sure about making the whole path noinstr (@Masami might have a
>> better opinion on this than me). Personally I don't mind either
>> disallowing it or making it correct.
>>
>> But it might be a good idea not to diverge too much between ISAs. This
>> patch is pretty much mirroring what the x86 side handles this situation.
>
> Ok, so how about this. I'll take these fixes for now, but let's try to
> make these paths noinstr in the future? That's a much bigger job, but I
> do worry that we're going to otherwise end up adding special logic every
> time we run into an unexpected re-entrant case.
Sure, thanks. In our case, surely if someone wants to trace certain
events like preempt_on/off (which is what we are doing with perf),
events outside debug exceptions should be enough. He or she might not be
so keen on events inside debug exceptions, and noinstr shouldn't hurt.
We will give it a thought and see if we can come up with some RFCs later.
>
> Will
^ permalink raw reply
* [RFC PATCH v2 8/8] arm64: dts: rockchip: rk3576-rock-4d: enable NPU
From: Jiaxing Hu @ 2026-07-18 3:11 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, joro, will, robin.murphy,
ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, dri-devel, linux-rockchip, iommu, linux-pm,
devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
In-Reply-To: <20260718031146.3368811-1-gahing@gahingwoo.com>
Enable rknn_core_0 and rknn_mmu_0 on the Radxa ROCK 4D and supply the
NPU rail (vdd_npu_s0) via npu-supply.
Signed-off-by: Jiaxing Hu <gahing@gahingwoo.com>
---
arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts b/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts
index 272af1012..09d2fc98b 100644
--- a/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts
@@ -442,6 +442,7 @@ regulator-state-mem {
};
vdd_npu_s0: dcdc-reg2 {
+ regulator-always-on;
regulator-boot-on;
regulator-enable-ramp-delay = <400>;
regulator-min-microvolt = <550000>;
@@ -869,3 +870,18 @@ vp0_out_hdmi: endpoint@ROCKCHIP_VOP2_EP_HDMI0 {
remote-endpoint = <&hdmi_in_vp0>;
};
};
+
+&rknn_core_0 {
+ npu-supply = <&vdd_npu_s0>;
+ /*
+ * Power BOTH NPU domains (NPU0 + NPU1) like the vendor's single NPU node,
+ * even though rocket computes only on core 0: the CBUF->CMAC read path is
+ * only fully powered with NPU1 up. rocket attaches the multi-PD list.
+ */
+ power-domains = <&power RK3576_PD_NPU0>, <&power RK3576_PD_NPU1>;
+ status = "okay";
+};
+
+&rknn_mmu_0 {
+ status = "okay";
+};
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v2 7/8] arm64: dts: rockchip: rk3576: add NPU (RKNN) nodes
From: Jiaxing Hu @ 2026-07-18 3:11 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, joro, will, robin.murphy,
ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, dri-devel, linux-rockchip, iommu, linux-pm,
devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
In-Reply-To: <20260718031146.3368811-1-gahing@gahingwoo.com>
Add the RK3576 NPU: rknn_core_0/1 and rknn_mmu_0/1, with register
addresses, GIC_SPI 247/248 interrupts, clocks, resets and power-domains.
Run the full RKNN clock set (CLK_RKNN_DSU0, ACLK_RKNN0/1, HCLK_RKNN_ROOT,
ACLK/HCLK_RKNN_CBUF) during the NPU0/NPU1 power transitions and on the
MMU nodes; without the functional and CBUF clocks the block powers on but
its registers (including the IOMMU banks) read back dead. Drive the RKNN
BIU reset (SRST_A_RKNN0/1_BIU) from the power domain so it fires before
the IOMMU resumes.
Signed-off-by: Jiaxing Hu <gahing@gahingwoo.com>
---
arch/arm64/boot/dts/rockchip/rk3576.dtsi | 78 +++++++++++++++++++++++-
1 file changed, 76 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi
index e12a2a0cf..5033f7628 100644
--- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi
@@ -1070,14 +1070,22 @@ power-domain@RK3576_PD_NPUTOP {
power-domain@RK3576_PD_NPU0 {
reg = <RK3576_PD_NPU0>;
clocks = <&cru HCLK_RKNN_ROOT>,
- <&cru ACLK_RKNN0>;
+ <&cru ACLK_RKNN0>,
+ <&cru CLK_RKNN_DSU0>,
+ <&cru ACLK_RKNN_CBUF>,
+ <&cru HCLK_RKNN_CBUF>;
+ resets = <&cru SRST_A_RKNN0_BIU>;
pm_qos = <&qos_npu_m0>;
#power-domain-cells = <0>;
};
power-domain@RK3576_PD_NPU1 {
reg = <RK3576_PD_NPU1>;
clocks = <&cru HCLK_RKNN_ROOT>,
- <&cru ACLK_RKNN1>;
+ <&cru ACLK_RKNN1>,
+ <&cru CLK_RKNN_DSU0>,
+ <&cru ACLK_RKNN_CBUF>,
+ <&cru HCLK_RKNN_CBUF>;
+ resets = <&cru SRST_A_RKNN1_BIU>;
pm_qos = <&qos_npu_m1>;
#power-domain-cells = <0>;
};
@@ -1804,6 +1812,72 @@ qos_npu_m1ro: qos@27f22100 {
reg = <0x0 0x27f22100 0x0 0x20>;
};
+ rknn_core_0: npu@27700000 {
+ compatible = "rockchip,rk3576-rknn-core";
+ reg = <0x0 0x27700000 0x0 0x1000>,
+ <0x0 0x27701000 0x0 0x1000>,
+ <0x0 0x27703000 0x0 0x1000>,
+ <0x0 0x27704000 0x0 0x1000>,
+ <0x0 0x27705000 0x0 0x1000>;
+ reg-names = "pc", "cna", "core", "dpu", "dpu_rdma";
+ interrupts = <GIC_SPI 247 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru ACLK_RKNN0>, <&cru HCLK_RKNN_ROOT>,
+ <&cru CLK_RKNN_DSU0>, <&cru PCLK_NPUTOP_ROOT>,
+ <&cru ACLK_RKNN_CBUF>, <&cru HCLK_RKNN_CBUF>;
+ clock-names = "aclk", "hclk", "npu", "pclk",
+ "aclk_cbuf", "hclk_cbuf";
+ resets = <&cru SRST_A_RKNN0>;
+ reset-names = "srst_a";
+ power-domains = <&power RK3576_PD_NPU0>;
+ iommus = <&rknn_mmu_0>;
+ status = "disabled";
+ };
+
+ rknn_mmu_0: iommu@27702000 {
+ compatible = "rockchip,rk3576-iommu", "rockchip,rk3568-iommu";
+ reg = <0x0 0x27702000 0x0 0x100>,
+ <0x0 0x27702100 0x0 0x100>;
+ interrupts = <GIC_SPI 247 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru ACLK_RKNN0>, <&cru HCLK_RKNN_ROOT>,
+ <&cru CLK_RKNN_DSU0>, <&cru ACLK_RKNN_CBUF>,
+ <&cru HCLK_RKNN_CBUF>;
+ #iommu-cells = <0>;
+ power-domains = <&power RK3576_PD_NPU0>;
+ status = "disabled";
+ };
+
+ rknn_core_1: npu@27710000 {
+ compatible = "rockchip,rk3576-rknn-core";
+ reg = <0x0 0x27710000 0x0 0x1000>,
+ <0x0 0x27711000 0x0 0x1000>,
+ <0x0 0x27713000 0x0 0x1000>,
+ <0x0 0x27714000 0x0 0x1000>,
+ <0x0 0x27715000 0x0 0x1000>;
+ reg-names = "pc", "cna", "core", "dpu", "dpu_rdma";
+ interrupts = <GIC_SPI 248 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru ACLK_RKNN1>, <&cru HCLK_RKNN_ROOT>,
+ <&cru CLK_RKNN_DSU0>, <&cru PCLK_NPUTOP_ROOT>;
+ clock-names = "aclk", "hclk", "npu", "pclk";
+ resets = <&cru SRST_A_RKNN1>;
+ reset-names = "srst_a";
+ power-domains = <&power RK3576_PD_NPU1>;
+ iommus = <&rknn_mmu_1>;
+ status = "disabled";
+ };
+
+ rknn_mmu_1: iommu@2770a000 {
+ compatible = "rockchip,rk3576-iommu", "rockchip,rk3568-iommu";
+ reg = <0x0 0x2770a000 0x0 0x100>,
+ <0x0 0x2770a100 0x0 0x100>;
+ interrupts = <GIC_SPI 248 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru ACLK_RKNN1>, <&cru HCLK_RKNN_ROOT>,
+ <&cru CLK_RKNN_DSU0>, <&cru ACLK_RKNN_CBUF>,
+ <&cru HCLK_RKNN_CBUF>;
+ #iommu-cells = <0>;
+ power-domains = <&power RK3576_PD_NPU1>;
+ status = "disabled";
+ };
+
gmac0: ethernet@2a220000 {
compatible = "rockchip,rk3576-gmac", "snps,dwmac-4.20a";
reg = <0x0 0x2a220000 0x0 0x10000>;
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v2 6/8] accel/rocket: add RK3576 NPU (RKNN) support
From: Jiaxing Hu @ 2026-07-18 3:11 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, joro, will, robin.murphy,
ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, dri-devel, linux-rockchip, iommu, linux-pm,
devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
In-Reply-To: <20260718031146.3368811-1-gahing@gahingwoo.com>
Add RK3576 support to the rocket DRM accelerator driver (used with the
Mesa Teflon TFLite delegate). Per-SoC differences are selected by new
of_device_id match data (struct rocket_soc_data) so the RK3588 path stays
unchanged:
- match rockchip,rk3576-rknn-core; iterate its nodes at probe
- RK3576 takes six clocks (adds the CBUF domain ACLK/HCLK_RKNN_CBUF):
the CNA fills the CBUF and CORE reads from it, so the compute path
stalls without them. RK3588 keeps its four clocks.
- RK3576 requests one reset (srst_a); its BIU reset (srst_h) is driven
from the power domain. RK3588 keeps both.
- RK3576 spans two power domains (PD_NPU0 + PD_NPU1) and attaches the
list explicitly; RK3588 is single-domain and keeps the driver-core
auto-attach.
- RK3576 has no maskable completion interrupt (PC_DONE is read-only in
INTERRUPT_MASK), so it polls PC_DONE via an hrtimer; RK3588 keeps the
DPU completion IRQ.
- guard rocket_job_timedout() MMIO behind pm_runtime_active()
Tested on a Radxa ROCK 4D: the NPU probes, powers on, brings up its
IOMMUs and runs submitted jobs to completion. Full multi-layer inference
is not yet correct on this SoC (only the first operation per power session
produces valid output); see the cover letter.
Signed-off-by: Jiaxing Hu <gahing@gahingwoo.com>
---
drivers/accel/rocket/rocket_core.c | 39 +++++++-
drivers/accel/rocket/rocket_core.h | 22 ++++-
drivers/accel/rocket/rocket_device.c | 4 +
drivers/accel/rocket/rocket_drv.c | 22 ++++-
drivers/accel/rocket/rocket_job.c | 127 +++++++++++++++++++++++++--
5 files changed, 200 insertions(+), 14 deletions(-)
diff --git a/drivers/accel/rocket/rocket_core.c b/drivers/accel/rocket/rocket_core.c
index b3b2fa9ba..140e37969 100644
--- a/drivers/accel/rocket/rocket_core.c
+++ b/drivers/accel/rocket/rocket_core.c
@@ -8,6 +8,7 @@
#include <linux/err.h>
#include <linux/iommu.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
@@ -21,14 +22,31 @@ int rocket_core_init(struct rocket_core *core)
u32 version;
int err = 0;
+ /*
+ * RK3576 moves the BIU (srst_h) reset to its power domain, so it only
+ * requests srst_a here (soc->num_resets == 1); RK3588 keeps both.
+ */
core->resets[0].id = "srst_a";
core->resets[1].id = "srst_h";
- err = devm_reset_control_bulk_get_exclusive(&pdev->dev, ARRAY_SIZE(core->resets),
+ err = devm_reset_control_bulk_get_exclusive(&pdev->dev, core->soc->num_resets,
core->resets);
if (err)
return dev_err_probe(dev, err, "failed to get resets for core %d\n", core->index);
- err = devm_clk_bulk_get(dev, ARRAY_SIZE(core->clks), core->clks);
+ core->clks[0].id = "aclk";
+ core->clks[1].id = "hclk";
+ core->clks[2].id = "npu";
+ core->clks[3].id = "pclk";
+ /*
+ * RK3576 (soc->num_clks == 6): the CBUF (convolution buffer) has its own
+ * clock domain. The CNA fills the CBUF and CORE reads from it; without
+ * these the compute path stalls after loading one slice (RDMA, which
+ * bypasses the CBUF, still runs). The vendor keeps all NPU clocks on
+ * whenever powered.
+ */
+ core->clks[4].id = "aclk_cbuf";
+ core->clks[5].id = "hclk_cbuf";
+ err = devm_clk_bulk_get(dev, core->soc->num_clks, core->clks);
if (err)
return dev_err_probe(dev, err, "failed to get clocks for core %d\n", core->index);
@@ -65,6 +83,23 @@ int rocket_core_init(struct rocket_core *core)
return err;
}
+ /*
+ * RK3576: the NPU spans TWO power domains (PD_NPU0 + PD_NPU1). The vendor
+ * powers BOTH from its single NPU node even when computing on one core --
+ * the CBUF->CMAC read path only works fully with NPU1 powered. A device
+ * with more than one power-domain is skipped by the driver-core single-PD
+ * auto-attach, so attach the list explicitly. RK3588 has a single domain
+ * and keeps the driver-core auto-attach (soc->multi_power_domain == false).
+ */
+ if (core->soc->multi_power_domain) {
+ struct dev_pm_domain_list *pd_list;
+
+ err = devm_pm_domain_attach_list(dev, NULL, &pd_list);
+ if (err < 0)
+ return dev_err_probe(dev, err,
+ "failed to attach NPU power domains\n");
+ }
+
pm_runtime_use_autosuspend(dev);
/*
diff --git a/drivers/accel/rocket/rocket_core.h b/drivers/accel/rocket/rocket_core.h
index f6d738285..2ab389c4b 100644
--- a/drivers/accel/rocket/rocket_core.h
+++ b/drivers/accel/rocket/rocket_core.h
@@ -6,6 +6,7 @@
#include <drm/gpu_scheduler.h>
#include <linux/clk.h>
+#include <linux/hrtimer.h>
#include <linux/io.h>
#include <linux/mutex_types.h>
#include <linux/reset.h>
@@ -27,16 +28,30 @@
#define rocket_core_writel(core, reg, value) \
writel(value, (core)->core_iomem + (REG_CORE_##reg) - REG_CORE_S_STATUS)
+/*
+ * Per-SoC differences, selected by the of_device_id match data. The RK3588
+ * path (all flags/counts at their base values) must stay byte-for-byte the
+ * original behaviour; RK3576 opts in to the extra clocks, the multi-domain
+ * attach and the polled completion.
+ */
+struct rocket_soc_data {
+ unsigned int num_clks; /* clk_bulk count: 4 base, 6 with CBUF */
+ unsigned int num_resets; /* reset_bulk count: 2 base, 1 on RK3576 */
+ bool multi_power_domain; /* device spans more than one PM domain */
+ bool poll_completion; /* PC_DONE not routable to the GIC; poll it */
+};
+
struct rocket_core {
struct device *dev;
struct rocket_device *rdev;
+ const struct rocket_soc_data *soc;
unsigned int index;
int irq;
void __iomem *pc_iomem;
void __iomem *cna_iomem;
void __iomem *core_iomem;
- struct clk_bulk_data clks[4];
+ struct clk_bulk_data clks[6];
struct reset_control_bulk_data resets[2];
struct iommu_group *iommu_group;
@@ -52,6 +67,11 @@ struct rocket_core {
atomic_t pending;
} reset;
+ /* RK3576 has no completion IRQ; poll for PC_DONE via hrtimer. */
+ struct hrtimer poll_timer;
+ struct work_struct poll_work;
+ atomic_t poll_active;
+
struct drm_gpu_scheduler sched;
u64 fence_context;
u64 emit_seqno;
diff --git a/drivers/accel/rocket/rocket_device.c b/drivers/accel/rocket/rocket_device.c
index 46e6ee1e7..bfb00f967 100644
--- a/drivers/accel/rocket/rocket_device.c
+++ b/drivers/accel/rocket/rocket_device.c
@@ -31,6 +31,10 @@ struct rocket_device *rocket_device_init(struct platform_device *pdev,
if (of_device_is_available(core_node))
num_cores++;
+ for_each_compatible_node(core_node, NULL, "rockchip,rk3576-rknn-core")
+ if (of_device_is_available(core_node))
+ num_cores++;
+
rdev->cores = devm_kcalloc(dev, num_cores, sizeof(*rdev->cores), GFP_KERNEL);
if (!rdev->cores)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/accel/rocket/rocket_drv.c b/drivers/accel/rocket/rocket_drv.c
index 8bbbce594..7f7dfa374 100644
--- a/drivers/accel/rocket/rocket_drv.c
+++ b/drivers/accel/rocket/rocket_drv.c
@@ -176,6 +176,7 @@ static int rocket_probe(struct platform_device *pdev)
rdev->cores[core].rdev = rdev;
rdev->cores[core].dev = &pdev->dev;
+ rdev->cores[core].soc = of_device_get_match_data(&pdev->dev);
rdev->cores[core].index = core;
rdev->num_cores++;
@@ -213,8 +214,23 @@ static void rocket_remove(struct platform_device *pdev)
}
}
+static const struct rocket_soc_data rk3588_soc_data = {
+ .num_clks = 4,
+ .num_resets = 2,
+ .multi_power_domain = false,
+ .poll_completion = false,
+};
+
+static const struct rocket_soc_data rk3576_soc_data = {
+ .num_clks = 6,
+ .num_resets = 1,
+ .multi_power_domain = true,
+ .poll_completion = true,
+};
+
static const struct of_device_id dt_match[] = {
- { .compatible = "rockchip,rk3588-rknn-core" },
+ { .compatible = "rockchip,rk3588-rknn-core", .data = &rk3588_soc_data },
+ { .compatible = "rockchip,rk3576-rknn-core", .data = &rk3576_soc_data },
{}
};
MODULE_DEVICE_TABLE(of, dt_match);
@@ -240,7 +256,7 @@ static int rocket_device_runtime_resume(struct device *dev)
if (core < 0)
return -ENODEV;
- err = clk_bulk_prepare_enable(ARRAY_SIZE(rdev->cores[core].clks), rdev->cores[core].clks);
+ err = clk_bulk_prepare_enable(rdev->cores[core].soc->num_clks, rdev->cores[core].clks);
if (err) {
dev_err(dev, "failed to enable (%d) clocks for core %d\n", err, core);
return err;
@@ -260,7 +276,7 @@ static int rocket_device_runtime_suspend(struct device *dev)
if (!rocket_job_is_idle(&rdev->cores[core]))
return -EBUSY;
- clk_bulk_disable_unprepare(ARRAY_SIZE(rdev->cores[core].clks), rdev->cores[core].clks);
+ clk_bulk_disable_unprepare(rdev->cores[core].soc->num_clks, rdev->cores[core].clks);
return 0;
}
diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c
index 2f1861f96..ce3f7c92e 100644
--- a/drivers/accel/rocket/rocket_job.c
+++ b/drivers/accel/rocket/rocket_job.c
@@ -7,6 +7,7 @@
#include <drm/drm_file.h>
#include <drm/drm_gem.h>
#include <drm/rocket_accel.h>
+#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
#include <linux/platform_device.h>
@@ -20,6 +21,16 @@
#define JOB_TIMEOUT_MS 500
+/*
+ * RK3576: INTERRUPT_MASK bits 28-29 are read-only (hardware rejects the write),
+ * so the PC_DONE completion signal cannot be routed to the GIC via the normal
+ * interrupt-mask path. We poll OPERATION_ENABLE every RK3576_POLL_INTERVAL_NS
+ * instead of waiting for a completion IRQ.
+ */
+#define PC_INTERRUPT_MASK_RK3576_PC_DONE_0 0x10000000u
+#define PC_INTERRUPT_MASK_RK3576_PC_DONE_1 0x20000000u
+#define RK3576_POLL_INTERVAL_NS 1000000LL /* 1 ms */
+
static struct rocket_job *
to_rocket_job(struct drm_sched_job *sched_job)
{
@@ -137,8 +148,24 @@ static void rocket_job_hw_submit(struct rocket_core *core, struct rocket_job *jo
rocket_pc_writel(core, REGISTER_AMOUNTS,
PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT((task->regcmd_count + 1) / 2 - 1));
- rocket_pc_writel(core, INTERRUPT_MASK, PC_INTERRUPT_MASK_DPU_0 | PC_INTERRUPT_MASK_DPU_1);
- rocket_pc_writel(core, INTERRUPT_CLEAR, PC_INTERRUPT_CLEAR_DPU_0 | PC_INTERRUPT_CLEAR_DPU_1);
+ if (core->soc->poll_completion) {
+ /*
+ * RK3576: PC_DONE (bits 28-29) is read-only in INTERRUPT_MASK, so
+ * it cannot be routed to the GIC; enable the DMA-error interrupts
+ * and poll PC_DONE via the hrtimer started below.
+ */
+ rocket_pc_writel(core, INTERRUPT_MASK,
+ PC_INTERRUPT_MASK_DMA_READ_ERROR |
+ PC_INTERRUPT_MASK_DMA_WRITE_ERROR);
+ rocket_pc_writel(core, INTERRUPT_CLEAR,
+ PC_INTERRUPT_MASK_RK3576_PC_DONE_0 |
+ PC_INTERRUPT_MASK_RK3576_PC_DONE_1);
+ } else {
+ rocket_pc_writel(core, INTERRUPT_MASK,
+ PC_INTERRUPT_MASK_DPU_0 | PC_INTERRUPT_MASK_DPU_1);
+ rocket_pc_writel(core, INTERRUPT_CLEAR,
+ PC_INTERRUPT_CLEAR_DPU_0 | PC_INTERRUPT_CLEAR_DPU_1);
+ }
rocket_pc_writel(core, TASK_CON, PC_TASK_CON_RESERVED_0(1) |
PC_TASK_CON_TASK_COUNT_CLEAR(1) |
@@ -149,7 +176,14 @@ static void rocket_job_hw_submit(struct rocket_core *core, struct rocket_job *jo
rocket_pc_writel(core, OPERATION_ENABLE, PC_OPERATION_ENABLE_OP_EN(1));
- dev_dbg(core->dev, "Submitted regcmd at 0x%llx to core %d", task->regcmd, core->index);
+ if (core->soc->poll_completion) {
+ atomic_set(&core->poll_active, 1);
+ hrtimer_start(&core->poll_timer, ns_to_ktime(RK3576_POLL_INTERVAL_NS),
+ HRTIMER_MODE_REL);
+ } else {
+ dev_dbg(core->dev, "Submitted regcmd at 0x%llx to core %d",
+ task->regcmd, core->index);
+ }
}
static int rocket_acquire_object_fences(struct drm_gem_object **bos,
@@ -326,12 +360,55 @@ static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job)
return fence;
}
+static void rocket_job_handle_irq(struct rocket_core *core);
+
+static enum hrtimer_restart rocket_poll_timer_fn(struct hrtimer *timer)
+{
+ struct rocket_core *core = container_of(timer, struct rocket_core, poll_timer);
+
+ if (!atomic_read(&core->poll_active))
+ return HRTIMER_NORESTART;
+
+ /*
+ * On RK3576, OPERATION_ENABLE is not cleared by hardware on completion;
+ * check INTERRUPT_RAW_STATUS bits 28-29 (PC_DONE_0/1) instead.
+ */
+ if (rocket_pc_readl(core, OPERATION_ENABLE) == 0 ||
+ (rocket_pc_readl(core, INTERRUPT_RAW_STATUS) &
+ (PC_INTERRUPT_MASK_RK3576_PC_DONE_0 |
+ PC_INTERRUPT_MASK_RK3576_PC_DONE_1))) {
+ atomic_set(&core->poll_active, 0);
+ schedule_work(&core->poll_work);
+ return HRTIMER_NORESTART;
+ }
+
+ hrtimer_forward_now(timer, ns_to_ktime(RK3576_POLL_INTERVAL_NS));
+ return HRTIMER_RESTART;
+}
+
+static void rocket_poll_work_fn(struct work_struct *work)
+{
+ struct rocket_core *core = container_of(work, struct rocket_core, poll_work);
+
+ rocket_job_handle_irq(core);
+}
+
static void rocket_job_handle_irq(struct rocket_core *core)
{
+ u32 clear = 0x1ffff;
+
+ if (core->soc->poll_completion) {
+ /* Stop the completion poll -- we're handling it now. */
+ atomic_set(&core->poll_active, 0);
+ hrtimer_cancel(&core->poll_timer);
+ clear |= PC_INTERRUPT_MASK_RK3576_PC_DONE_0 |
+ PC_INTERRUPT_MASK_RK3576_PC_DONE_1;
+ }
+
pm_runtime_mark_last_busy(core->dev);
rocket_pc_writel(core, OPERATION_ENABLE, 0x0);
- rocket_pc_writel(core, INTERRUPT_CLEAR, 0x1ffff);
+ rocket_pc_writel(core, INTERRUPT_CLEAR, clear);
scoped_guard(mutex, &core->job_lock)
if (core->in_flight_job) {
@@ -353,6 +430,12 @@ rocket_reset(struct rocket_core *core, struct drm_sched_job *bad)
if (!atomic_read(&core->reset.pending))
return;
+ if (core->soc->poll_completion) {
+ atomic_set(&core->poll_active, 0);
+ hrtimer_cancel(&core->poll_timer);
+ cancel_work_sync(&core->poll_work);
+ }
+
drm_sched_stop(&core->sched, bad);
/*
@@ -385,7 +468,14 @@ static enum drm_gpu_sched_stat rocket_job_timedout(struct drm_sched_job *sched_j
struct rocket_device *rdev = job->rdev;
struct rocket_core *core = sched_to_core(rdev, sched_job->sched);
- dev_err(core->dev, "NPU job timed out");
+ if (pm_runtime_active(core->dev))
+ dev_err(core->dev,
+ "NPU job timed out: RAW_STATUS=0x%08x MASK=0x%08x OP_EN=0x%08x\n",
+ rocket_pc_readl(core, INTERRUPT_RAW_STATUS),
+ rocket_pc_readl(core, INTERRUPT_MASK),
+ rocket_pc_readl(core, OPERATION_ENABLE));
+ else
+ dev_err(core->dev, "NPU job timed out (device not active)\n");
atomic_set(&core->reset.pending, 1);
rocket_reset(core, sched_job);
@@ -424,9 +514,22 @@ static irqreturn_t rocket_job_irq_handler(int irq, void *data)
WARN_ON(raw_status & PC_INTERRUPT_RAW_STATUS_DMA_READ_ERROR);
WARN_ON(raw_status & PC_INTERRUPT_RAW_STATUS_DMA_WRITE_ERROR);
- if (!(raw_status & PC_INTERRUPT_RAW_STATUS_DPU_0 ||
- raw_status & PC_INTERRUPT_RAW_STATUS_DPU_1))
- return IRQ_NONE;
+ if (core->soc->poll_completion) {
+ /*
+ * RK3576: completion is polled (rocket_poll_timer_fn); only the
+ * DMA-error bits (0-13) can raise this shared IRQ.
+ */
+ u32 active = raw_status & 0x3fff;
+
+ if (!active)
+ return IRQ_NONE;
+
+ rocket_pc_writel(core, INTERRUPT_CLEAR, active);
+ } else {
+ if (!(raw_status & PC_INTERRUPT_RAW_STATUS_DPU_0 ||
+ raw_status & PC_INTERRUPT_RAW_STATUS_DPU_1))
+ return IRQ_NONE;
+ }
rocket_pc_writel(core, INTERRUPT_MASK, 0x0);
@@ -445,6 +548,10 @@ int rocket_job_init(struct rocket_core *core)
int ret;
INIT_WORK(&core->reset.work, rocket_reset_work);
+ INIT_WORK(&core->poll_work, rocket_poll_work_fn);
+ hrtimer_setup(&core->poll_timer, rocket_poll_timer_fn, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ atomic_set(&core->poll_active, 0);
spin_lock_init(&core->fence_lock);
mutex_init(&core->job_lock);
@@ -486,6 +593,10 @@ int rocket_job_init(struct rocket_core *core)
void rocket_job_fini(struct rocket_core *core)
{
+ atomic_set(&core->poll_active, 0);
+ hrtimer_cancel(&core->poll_timer);
+ cancel_work_sync(&core->poll_work);
+
drm_sched_fini(&core->sched);
cancel_work_sync(&core->reset.work);
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v2 5/8] iommu/rockchip: clear stale page faults before enabling stall
From: Jiaxing Hu @ 2026-07-18 3:11 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, joro, will, robin.murphy,
ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, dri-devel, linux-rockchip, iommu, linux-pm,
devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
In-Reply-To: <20260718031146.3368811-1-gahing@gahingwoo.com>
Boot firmware can leave an IOMMU bank in PAGE_FAULT_ACTIVE before the
driver has configured paging:
PAGE_FAULT_ACTIVE=1 STALL_ACTIVE=0 IDLE=1
Such a bank ignores CMD_ENABLE_STALL and never reaches STALL_ACTIVE, so
rk_iommu_enable_stall()'s readx_poll_timeout() spins until it times out
(seen on the RK3576 NPU, whose MMUs share this poll across banks).
Rather than special-casing these banks in the stall path, acknowledge the
stale fault with CMD_PAGE_FAULT_DONE before enabling stall, so every bank
starts from a clean state and the normal stall sequence applies to all of
them. Banks without a pending fault are untouched.
Signed-off-by: Jiaxing Hu <gahing@gahingwoo.com>
---
drivers/iommu/rockchip-iommu.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index 62cd6b022..68bd55433 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -415,6 +415,19 @@ static int rk_iommu_enable_stall(struct rk_iommu *iommu)
if (!rk_iommu_is_paging_enabled(iommu))
return 0;
+ /*
+ * Boot firmware can leave a bank in PAGE_FAULT_ACTIVE with no handler
+ * (PAGE_FAULT_ACTIVE & !STALL_ACTIVE & IDLE). Such a bank ignores
+ * CMD_ENABLE_STALL and never reaches STALL_ACTIVE, timing out the poll
+ * below. Acknowledge any stale fault first so every bank starts clean.
+ */
+ for (i = 0; i < iommu->num_mmu; i++) {
+ if (rk_iommu_read(iommu->bases[i], RK_MMU_STATUS) &
+ RK_MMU_STATUS_PAGE_FAULT_ACTIVE)
+ writel(RK_MMU_CMD_PAGE_FAULT_DONE,
+ iommu->bases[i] + RK_MMU_COMMAND);
+ }
+
rk_iommu_command(iommu, RK_MMU_CMD_ENABLE_STALL);
ret = readx_poll_timeout(rk_iommu_is_stall_active, iommu, val,
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v2 4/8] iommu/rockchip: take all DT clocks
From: Jiaxing Hu @ 2026-07-18 3:11 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, joro, will, robin.murphy,
ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, dri-devel, linux-rockchip, iommu, linux-pm,
devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
In-Reply-To: <20260718031146.3368811-1-gahing@gahingwoo.com>
rk_iommu only enabled a fixed {aclk,iface} pair. On the RK3576 NPU the
MMU sits behind the CBUF/DSU gates, so writes to DTE_ADDR are silently
dropped until those clocks run too (reads work, writes need more clocks).
Use devm_clk_bulk_get_all() to take every clock the devicetree provides
instead of a fixed pair, so a platform can list the full set it needs.
Clocks stay optional for the older clock-less devicetrees.
Signed-off-by: Jiaxing Hu <gahing@gahingwoo.com>
---
drivers/iommu/rockchip-iommu.c | 28 +++++++++-------------------
1 file changed, 9 insertions(+), 19 deletions(-)
diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index 0013cf196..62cd6b022 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -93,11 +93,6 @@ struct rk_iommu_domain {
struct iommu_domain domain;
};
-/* list of clocks required by IOMMU */
-static const char * const rk_iommu_clocks[] = {
- "aclk", "iface",
-};
-
struct rk_iommu_ops {
phys_addr_t (*pt_address)(u32 dte);
u32 (*mk_dtentries)(dma_addr_t pt_dma);
@@ -1246,25 +1241,20 @@ static int rk_iommu_probe(struct platform_device *pdev)
iommu->reset_disabled = device_property_read_bool(dev,
"rockchip,disable-mmu-reset");
- iommu->num_clocks = ARRAY_SIZE(rk_iommu_clocks);
- iommu->clocks = devm_kcalloc(iommu->dev, iommu->num_clocks,
- sizeof(*iommu->clocks), GFP_KERNEL);
- if (!iommu->clocks)
- return -ENOMEM;
-
- for (i = 0; i < iommu->num_clocks; ++i)
- iommu->clocks[i].id = rk_iommu_clocks[i];
-
/*
- * iommu clocks should be present for all new devices and devicetrees
- * but there are older devicetrees without clocks out in the wild.
- * So clocks as optional for the time being.
+ * Take every clock the devicetree provides. Most IOMMU instances
+ * need exactly "aclk" + "iface", but e.g. the RK3576 NPU IOMMUs sit
+ * behind additional gates (CBUF/DSU) whose clocks must be running
+ * for register writes to land. Clocks stay optional because there
+ * are older devicetrees without clocks out in the wild.
*/
- err = devm_clk_bulk_get(iommu->dev, iommu->num_clocks, iommu->clocks);
+ err = devm_clk_bulk_get_all(iommu->dev, &iommu->clocks);
if (err == -ENOENT)
iommu->num_clocks = 0;
- else if (err)
+ else if (err < 0)
return err;
+ else
+ iommu->num_clocks = err;
err = clk_bulk_prepare(iommu->num_clocks, iommu->clocks);
if (err)
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v2 3/8] pmdomain/rockchip: cycle optional power-domain resets on power-on
From: Jiaxing Hu @ 2026-07-18 3:11 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, joro, will, robin.murphy,
ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, dri-devel, linux-rockchip, iommu, linux-pm,
devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
In-Reply-To: <20260718031146.3368811-1-gahing@gahingwoo.com>
Some power domains contain hardware bus interfaces that require an
explicit assert/deassert edge on their reset line after the domain is
powered on. The CRU preserves reset state across power cycles (stays
deasserted), so the hardware never sees the edge it needs unless the
driver pulses it explicitly.
Add an optional resets property to rockchip_pm_domain. When present,
the resets are asserted and deasserted (with a 10 us hold) inside
rockchip_pd_power() after the idle bits are cleared and the delay_us
settling time has elapsed, but before any device in the domain resumes.
The resets are released in rockchip_pm_remove_one_domain().
Signed-off-by: Jiaxing Hu <gahing@gahingwoo.com>
---
drivers/pmdomain/rockchip/pm-domains.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c
index 1787f62ba..8ce0cc3de 100644
--- a/drivers/pmdomain/rockchip/pm-domains.c
+++ b/drivers/pmdomain/rockchip/pm-domains.c
@@ -18,6 +18,7 @@
#include <linux/of_address.h>
#include <linux/of_clk.h>
#include <linux/clk.h>
+#include <linux/reset.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/mfd/syscon.h>
@@ -102,6 +103,7 @@ struct rockchip_pm_domain {
struct clk_bulk_data *clks;
struct device_node *node;
struct regulator *supply;
+ struct reset_control *resets;
};
struct rockchip_pmu {
@@ -671,6 +673,12 @@ static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
if (pd->info->delay_us)
udelay(pd->info->delay_us);
+ if (pd->resets) {
+ reset_control_assert(pd->resets);
+ udelay(10);
+ reset_control_deassert(pd->resets);
+ }
+
rockchip_pmu_restore_qos(pd);
}
@@ -840,6 +848,14 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
if (error)
goto err_put_clocks;
+ pd->resets = of_reset_control_array_get_optional_exclusive(node);
+ if (IS_ERR(pd->resets)) {
+ error = PTR_ERR(pd->resets);
+ dev_err(pmu->dev, "%pOFn: failed to get resets: %d\n", node, error);
+ pd->resets = NULL;
+ goto err_unprepare_clocks;
+ }
+
pd->num_qos = of_count_phandle_with_args(node, "pm_qos",
NULL);
@@ -910,6 +926,7 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
clk_bulk_unprepare(pd->num_clks, pd->clks);
err_put_clocks:
clk_bulk_put(pd->num_clks, pd->clks);
+ reset_control_put(pd->resets);
return error;
}
@@ -928,6 +945,7 @@ static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd)
clk_bulk_unprepare(pd->num_clks, pd->clks);
clk_bulk_put(pd->num_clks, pd->clks);
+ reset_control_put(pd->resets);
/* protect the zeroing of pm->num_clks */
mutex_lock(&pd->pmu->mutex);
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v2 2/8] pmdomain/rockchip: add optional per-domain power-on settle delay
From: Jiaxing Hu @ 2026-07-18 3:11 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, joro, will, robin.murphy,
ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, dri-devel, linux-rockchip, iommu, linux-pm,
devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
In-Reply-To: <20260718031146.3368811-1-gahing@gahingwoo.com>
The RK3576 NPU domains need a short settle time after the idle
request is released before the QoS registers behind the domain are
written: restoring QoS immediately after de-idle can raise an SError
on the register access while the NoC is still settling (observed on
ROCK 4D as a panic inside rockchip_pd_power() during NPU runtime-PM
cycling).
The vendor kernel handles this with a per-domain delay_us field
applied between the de-idle request and the QoS restore, carrying
15us for the RK3576 NPUTOP domain. Mirror that: add delay_us to
rockchip_domain_info, honor it in rockchip_pd_power() right before
rockchip_pmu_restore_qos(), and set 15us for NPUTOP, NPU0 and NPU1
(the two core domains power-cycle constantly under runtime PM and
showed the same symptom during bring-up). All other domains keep 0,
so nothing changes for them.
Signed-off-by: Jiaxing Hu <gahing@gahingwoo.com>
---
drivers/pmdomain/rockchip/pm-domains.c | 51 ++++++++++++++------------
1 file changed, 28 insertions(+), 23 deletions(-)
diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c
index 490bbb1d1..1787f62ba 100644
--- a/drivers/pmdomain/rockchip/pm-domains.c
+++ b/drivers/pmdomain/rockchip/pm-domains.c
@@ -59,6 +59,7 @@ struct rockchip_domain_info {
u32 pwr_offset;
u32 mem_offset;
u32 req_offset;
+ u32 delay_us;
};
struct rockchip_pmu_info {
@@ -185,7 +186,7 @@ struct rockchip_pmu {
.need_regulator = regulator, \
}
-#define DOMAIN_M_O_R_G(_name, p_offset, pwr, status, m_offset, m_status, r_status, r_offset, req, idle, ack, g_mask, wakeup) \
+#define DOMAIN_M_O_R_G(_name, p_offset, pwr, status, m_offset, m_status, r_status, r_offset, req, idle, ack, g_mask, delay, wakeup) \
{ \
.name = _name, \
.pwr_offset = p_offset, \
@@ -200,6 +201,7 @@ struct rockchip_pmu {
.req_mask = (req), \
.idle_mask = (idle), \
.clk_ungate_mask = (g_mask), \
+ .delay_us = (delay), \
.ack_mask = (ack), \
.active_wakeup = wakeup, \
}
@@ -244,8 +246,8 @@ struct rockchip_pmu {
#define DOMAIN_RK3568(name, pwr, req, wakeup) \
DOMAIN_M(name, pwr, pwr, req, req, req, wakeup)
-#define DOMAIN_RK3576(name, p_offset, pwr, status, r_status, r_offset, req, idle, g_mask, wakeup) \
- DOMAIN_M_O_R_G(name, p_offset, pwr, status, 0, r_status, r_status, r_offset, req, idle, idle, g_mask, wakeup)
+#define DOMAIN_RK3576(name, p_offset, pwr, status, r_status, r_offset, req, idle, g_mask, delay, wakeup) \
+ DOMAIN_M_O_R_G(name, p_offset, pwr, status, 0, r_status, r_status, r_offset, req, idle, idle, g_mask, delay, wakeup)
/*
* Dynamic Memory Controller may need to coordinate with us -- see
@@ -612,7 +614,6 @@ static int rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd,
return ret;
}
-
ret = readx_poll_timeout_atomic(rockchip_pmu_domain_is_on, pd, is_on,
is_on == on, 0, 10000);
if (ret) {
@@ -667,6 +668,9 @@ static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
if (ret < 0)
goto out;
+ if (pd->info->delay_us)
+ udelay(pd->info->delay_us);
+
rockchip_pmu_restore_qos(pd);
}
@@ -1286,25 +1290,26 @@ static const struct rockchip_domain_info rk3568_pm_domains[] = {
};
static const struct rockchip_domain_info rk3576_pm_domains[] = {
- [RK3576_PD_NPU] = DOMAIN_RK3576("npu", 0x0, BIT(0), BIT(0), 0, 0x0, 0, 0, 0, false),
- [RK3576_PD_NVM] = DOMAIN_RK3576("nvm", 0x0, BIT(6), 0, BIT(6), 0x4, BIT(2), BIT(18), BIT(2), false),
- [RK3576_PD_SDGMAC] = DOMAIN_RK3576("sdgmac", 0x0, BIT(7), 0, BIT(7), 0x4, BIT(1), BIT(17), 0x6, false),
- [RK3576_PD_AUDIO] = DOMAIN_RK3576("audio", 0x0, BIT(8), 0, BIT(8), 0x4, BIT(0), BIT(16), BIT(0), false),
- [RK3576_PD_PHP] = DOMAIN_RK3576("php", 0x0, BIT(9), 0, BIT(9), 0x0, BIT(15), BIT(15), BIT(15), false),
- [RK3576_PD_SUBPHP] = DOMAIN_RK3576("subphp", 0x0, BIT(10), 0, BIT(10), 0x0, 0, 0, 0, false),
- [RK3576_PD_VOP] = DOMAIN_RK3576("vop", 0x0, BIT(11), 0, BIT(11), 0x0, 0x6000, 0x6000, 0x6000, false),
- [RK3576_PD_VO1] = DOMAIN_RK3576("vo1", 0x0, BIT(14), 0, BIT(14), 0x0, BIT(12), BIT(12), 0x7000, false),
- [RK3576_PD_VO0] = DOMAIN_RK3576("vo0", 0x0, BIT(15), 0, BIT(15), 0x0, BIT(11), BIT(11), 0x6800, false),
- [RK3576_PD_USB] = DOMAIN_RK3576("usb", 0x4, BIT(0), 0, BIT(16), 0x0, BIT(10), BIT(10), 0x6400, true),
- [RK3576_PD_VI] = DOMAIN_RK3576("vi", 0x4, BIT(1), 0, BIT(17), 0x0, BIT(9), BIT(9), BIT(9), false),
- [RK3576_PD_VEPU0] = DOMAIN_RK3576("vepu0", 0x4, BIT(2), 0, BIT(18), 0x0, BIT(7), BIT(7), 0x280, false),
- [RK3576_PD_VEPU1] = DOMAIN_RK3576("vepu1", 0x4, BIT(3), 0, BIT(19), 0x0, BIT(8), BIT(8), BIT(8), false),
- [RK3576_PD_VDEC] = DOMAIN_RK3576("vdec", 0x4, BIT(4), 0, BIT(20), 0x0, BIT(6), BIT(6), BIT(6), false),
- [RK3576_PD_VPU] = DOMAIN_RK3576("vpu", 0x4, BIT(5), 0, BIT(21), 0x0, BIT(5), BIT(5), BIT(5), false),
- [RK3576_PD_NPUTOP] = DOMAIN_RK3576("nputop", 0x4, BIT(6), 0, BIT(22), 0x0, 0x18, 0x18, 0x18, false),
- [RK3576_PD_NPU0] = DOMAIN_RK3576("npu0", 0x4, BIT(7), 0, BIT(23), 0x0, BIT(1), BIT(1), 0x1a, false),
- [RK3576_PD_NPU1] = DOMAIN_RK3576("npu1", 0x4, BIT(8), 0, BIT(24), 0x0, BIT(2), BIT(2), 0x1c, false),
- [RK3576_PD_GPU] = DOMAIN_RK3576("gpu", 0x4, BIT(9), 0, BIT(25), 0x0, BIT(0), BIT(0), BIT(0), false),
+ /* name p_offset pwr status r_status r_offset req idle g_mask delay wakeup */
+ [RK3576_PD_NPU] = DOMAIN_RK3576("npu", 0x0, BIT(0), BIT(0), 0, 0x0, 0, 0, 0, 0, false),
+ [RK3576_PD_NVM] = DOMAIN_RK3576("nvm", 0x0, BIT(6), 0, BIT(6), 0x4, BIT(2), BIT(18), BIT(2), 0, false),
+ [RK3576_PD_SDGMAC] = DOMAIN_RK3576("sdgmac", 0x0, BIT(7), 0, BIT(7), 0x4, BIT(1), BIT(17), 0x6, 0, false),
+ [RK3576_PD_AUDIO] = DOMAIN_RK3576("audio", 0x0, BIT(8), 0, BIT(8), 0x4, BIT(0), BIT(16), BIT(0), 0, false),
+ [RK3576_PD_PHP] = DOMAIN_RK3576("php", 0x0, BIT(9), 0, BIT(9), 0x0, BIT(15), BIT(15), BIT(15), 0, false),
+ [RK3576_PD_SUBPHP] = DOMAIN_RK3576("subphp", 0x0, BIT(10), 0, BIT(10), 0x0, 0, 0, 0, 0, false),
+ [RK3576_PD_VOP] = DOMAIN_RK3576("vop", 0x0, BIT(11), 0, BIT(11), 0x0, 0x6000, 0x6000, 0x6000, 0, false),
+ [RK3576_PD_VO1] = DOMAIN_RK3576("vo1", 0x0, BIT(14), 0, BIT(14), 0x0, BIT(12), BIT(12), 0x7000, 0, false),
+ [RK3576_PD_VO0] = DOMAIN_RK3576("vo0", 0x0, BIT(15), 0, BIT(15), 0x0, BIT(11), BIT(11), 0x6800, 0, false),
+ [RK3576_PD_USB] = DOMAIN_RK3576("usb", 0x4, BIT(0), 0, BIT(16), 0x0, BIT(10), BIT(10), 0x6400, 0, true),
+ [RK3576_PD_VI] = DOMAIN_RK3576("vi", 0x4, BIT(1), 0, BIT(17), 0x0, BIT(9), BIT(9), BIT(9), 0, false),
+ [RK3576_PD_VEPU0] = DOMAIN_RK3576("vepu0", 0x4, BIT(2), 0, BIT(18), 0x0, BIT(7), BIT(7), 0x280, 0, false),
+ [RK3576_PD_VEPU1] = DOMAIN_RK3576("vepu1", 0x4, BIT(3), 0, BIT(19), 0x0, BIT(8), BIT(8), BIT(8), 0, false),
+ [RK3576_PD_VDEC] = DOMAIN_RK3576("vdec", 0x4, BIT(4), 0, BIT(20), 0x0, BIT(6), BIT(6), BIT(6), 0, false),
+ [RK3576_PD_VPU] = DOMAIN_RK3576("vpu", 0x4, BIT(5), 0, BIT(21), 0x0, BIT(5), BIT(5), BIT(5), 0, false),
+ [RK3576_PD_NPUTOP] = DOMAIN_RK3576("nputop", 0x4, BIT(6), 0, BIT(22), 0x0, 0x18, 0x18, 0x18, 15, false),
+ [RK3576_PD_NPU0] = DOMAIN_RK3576("npu0", 0x4, BIT(7), 0, BIT(23), 0x0, BIT(1), BIT(1), 0x1a, 15, false),
+ [RK3576_PD_NPU1] = DOMAIN_RK3576("npu1", 0x4, BIT(8), 0, BIT(24), 0x0, BIT(2), BIT(2), 0x1c, 15, false),
+ [RK3576_PD_GPU] = DOMAIN_RK3576("gpu", 0x4, BIT(9), 0, BIT(25), 0x0, BIT(0), BIT(0), BIT(0), 0, false),
};
static const struct rockchip_domain_info rk3588_pm_domains[] = {
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v2 1/8] dt-bindings: npu: rockchip: add rockchip,rk3576-rknn-core
From: Jiaxing Hu @ 2026-07-18 3:11 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, joro, will, robin.murphy,
ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, dri-devel, linux-rockchip, iommu, linux-pm,
devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
In-Reply-To: <20260718031146.3368811-1-gahing@gahingwoo.com>
Document the RK3576 NPU core: add rockchip,rk3576-rknn-core to the
compatible enum. The RK3576 has no NPU SRAM supply, so make sram-supply
required only for rockchip,rk3588-rknn-core via an allOf/if-then instead
of dropping it from the shared required list (which would have weakened
validation for RK3588).
Signed-off-by: Jiaxing Hu <gahing@gahingwoo.com>
---
.../bindings/npu/rockchip,rk3588-rknn-core.yaml | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml b/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml
index caca2a490..985cde6b2 100644
--- a/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml
+++ b/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml
@@ -21,6 +21,7 @@ properties:
compatible:
enum:
+ - rockchip,rk3576-rknn-core
- rockchip,rk3588-rknn-core
reg:
@@ -75,7 +76,16 @@ required:
- resets
- reset-names
- npu-supply
- - sram-supply
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: rockchip,rk3588-rknn-core
+ then:
+ required:
+ - sram-supply
additionalProperties: false
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v2 0/8] accel/rocket: RK3576 NPU (RKNN) enablement
From: Jiaxing Hu @ 2026-07-18 3:11 UTC (permalink / raw)
To: tomeu, heiko, robh, krzk+dt, conor+dt, joro, will, robin.murphy,
ulfh, p.zabel, ogabbay
Cc: chaoyi.chen, dri-devel, linux-rockchip, iommu, linux-pm,
devicetree, linux-arm-kernel, linux-kernel, Jiaxing Hu
This adds RK3576 NPU support to accel/rocket. RFC: the NPU brings up and
runs, but full inference is not correct yet, and I would rather get
feedback now than sit on it.
Tested on a Radxa ROCK 4D. The NPU probes, powers on, brings up its
IOMMUs, and runs jobs to completion (rocket + Mesa Teflon). Getting there
took a few fixes outside the driver too: a power-domain settle delay and
reset cycling (else a cold power-on throws an async SError), the full
clock set on the MMU nodes, clearing a stale firmware page fault, and both
NPU power domains on the core.
Not solved: only the first operation in a power session gives the right
answer. Every later one engages and DMAs its input, but the compute
output never lands, so the readback is the zero-point. I chased it
through the whole software surface (registers, IOMMU/TLB, cache,
power/clock/reset, dispatch) and it reproduces even with the driver
environment matched to the vendor's on the same kernel, so it looks to be
below software, in the fixed-function block. Traces available.
Two asks: review of the bring-up and the shared pmdomain/iommu changes;
and if anyone knows the RK3576 NPU internals, whether that
per-power-session arm can be driven from software at all.
The series is split by subsystem (binding, pmdomain, iommu, driver, DT).
The board DT keeps vdd_npu_s0 always-on for now; happy to switch to
proper runtime control if preferred.
Changes in v2 (thanks to Chaoyi Chen and the Sashiko review bot):
- dt-bindings: keep sram-supply required for rk3588 via allOf/if-then
instead of dropping it from the shared required list
- pmdomain: release the resets in rockchip_pm_remove_one_domain()
- iommu: drop the redundant "err == 0" check in the clock probe
- iommu: replace the two "skip orphaned-fault banks" patches with one
that clears the stale firmware page fault (CMD_PAGE_FAULT_DONE) before
enabling stall, so no bank needs special-casing -- Chaoyi asked
whether dealing with the fault avoids the skip, and it does
- accel/rocket: gate every RK3576-specific difference (extra clocks,
single reset, multi power-domain attach, polled completion) behind
of_device_id match data so the RK3588 path is byte-for-byte unchanged;
drop the unused DPU register mapping (the driver did no pre-arm)
- series is now 8 patches (was 9)
I used Claude Opus 4.8 to help trim this series out of my debugging tree
and generate the diffs.
Jiaxing Hu (8):
dt-bindings: npu: rockchip: add rockchip,rk3576-rknn-core
pmdomain/rockchip: add optional per-domain power-on settle delay
pmdomain/rockchip: cycle optional power-domain resets on power-on
iommu/rockchip: take all DT clocks
iommu/rockchip: clear stale page faults before enabling stall
accel/rocket: add RK3576 NPU (RKNN) support
arm64: dts: rockchip: rk3576: add NPU (RKNN) nodes
arm64: dts: rockchip: rk3576-rock-4d: enable NPU
.../npu/rockchip,rk3588-rknn-core.yaml | 12 +-
.../boot/dts/rockchip/rk3576-rock-4d.dts | 16 +++
arch/arm64/boot/dts/rockchip/rk3576.dtsi | 78 ++++++++++-
drivers/accel/rocket/rocket_core.c | 39 +++++-
drivers/accel/rocket/rocket_core.h | 22 ++-
drivers/accel/rocket/rocket_device.c | 4 +
drivers/accel/rocket/rocket_drv.c | 22 ++-
drivers/accel/rocket/rocket_job.c | 127 ++++++++++++++++--
drivers/iommu/rockchip-iommu.c | 41 +++---
drivers/pmdomain/rockchip/pm-domains.c | 69 ++++++----
10 files changed, 371 insertions(+), 59 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH v4 2/2] iio: adc: add Axiado SARADC driver
From: Jonathan Cameron @ 2026-07-18 1:40 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Petar Stepanovic, Akhila Kavi, Prasad Bolisetty, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Harshit Shah, linux-iio, devicetree,
linux-arm-kernel, linux-kernel
In-Reply-To: <alnpMFK6_ZkdWlLi@ashevche-desk.local>
On Fri, 17 Jul 2026 11:34:56 +0300
Andy Shevchenko <andriy.shevchenko@intel.com> wrote:
> On Thu, Jul 16, 2026 at 10:53:02PM -0700, Petar Stepanovic wrote:
> > Add support for the SARADC controller found on Axiado AX3000 and
> > AX3005 SoCs.
> >
> > The driver supports single-shot voltage reads through the IIO
> > subsystem. The number of available input channels is selected from
> > the SoC match data, allowing AX3000 and AX3005 variants to use the
> > same driver.
>
Andy already commented everywhere I'd have said things, so just
a few places where I'd take a different approach to resolving stuff.
Thanks,
Jonathan
>
> ...
>
> > +/* GLOBAL_CTRL register values */
> > +#define AX_SARADC_GLOBAL_CTRL_SAMPLE_16 \
> > + FIELD_PREP(AX_SARADC_GLOBAL_CTRL_SAMPLE_MASK, 0)
> > +
> > +#define AX_SARADC_GLOBAL_CTRL_MODE_MANUAL \
> > + FIELD_PREP(AX_SARADC_GLOBAL_CTRL_MODE_MASK, 1)
>
> FIELD_PREP_CONST() in both cases.
>
For this second one I'd rather see defines for the values the
field takes and then FIELD_PREP only used inline.
#define AX_SARADC_GLOBAL_CTL_MODE_something 0
#define AX_SARADC_GLOBAL_CTL_MODE_MANUAL 1
To me that acts as clearer documentation of what is
going on at the point of the write.
Similar applies for the SAMPLE_MASK field where I'd define the
value 0 with a name then have the FIELD_PREP() inline.
> ...
>
> > +#define AX_SARADC_MANUAL_CTRL_EN(ch) \
> > + (AX_SARADC_MANUAL_CTRL_ENABLE | \
> > + FIELD_PREP(AX_SARADC_MANUAL_CTRL_CH_SEL_MASK, ch))
>
> Missing parentheses for ch. Also wrong indentation of the second line, missing
> one space.
>
I wonder if it would be more readable to just push that inline rather
than having this define at all.
^ permalink raw reply
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