* [PATCH v6 8/9] media: chips-media: wave6: Add Wave6 control driver
From: Nas Chung @ 2026-06-24 7:20 UTC (permalink / raw)
To: mchehab, hverkuil, robh, krzk+dt, conor+dt, shawnguo, s.hauer
Cc: linux-media, devicetree, linux-kernel, linux-imx,
linux-arm-kernel, jackson.lee, lafley.kim, marek.vasut, Nas Chung,
Ming Qian
In-Reply-To: <20260624072043.238-1-nas.chung@chipsnmedia.com>
Add the control driver for the Chips&Media Wave6 video codec IP.
The hardware contains one control register region and four interface
register regions for a shared video processing engine.
This driver handles the control region and manages shared resources such
as firmware loading, firmware memory allocation, and coordination required
by the interface register regions.
It also instantiates and coordinates with the `wave6-core` child devices
for firmware and power state management.
Signed-off-by: Nas Chung <nas.chung@chipsnmedia.com>
Tested-by: Ming Qian <ming.qian@oss.nxp.com>
Tested-by: Marek Vasut <marek.vasut@mailbox.org>
---
drivers/media/platform/chips-media/Kconfig | 1 +
drivers/media/platform/chips-media/Makefile | 1 +
.../media/platform/chips-media/wave6/Kconfig | 17 +
.../media/platform/chips-media/wave6/Makefile | 17 +
.../platform/chips-media/wave6/wave6-vpu.c | 821 ++++++++++++++++++
.../platform/chips-media/wave6/wave6-vpu.h | 143 +++
6 files changed, 1000 insertions(+)
create mode 100644 drivers/media/platform/chips-media/wave6/Kconfig
create mode 100644 drivers/media/platform/chips-media/wave6/Makefile
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu.c
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu.h
diff --git a/drivers/media/platform/chips-media/Kconfig b/drivers/media/platform/chips-media/Kconfig
index ad350eb6b1fc..8ef7fc8029a4 100644
--- a/drivers/media/platform/chips-media/Kconfig
+++ b/drivers/media/platform/chips-media/Kconfig
@@ -4,3 +4,4 @@ comment "Chips&Media media platform drivers"
source "drivers/media/platform/chips-media/coda/Kconfig"
source "drivers/media/platform/chips-media/wave5/Kconfig"
+source "drivers/media/platform/chips-media/wave6/Kconfig"
diff --git a/drivers/media/platform/chips-media/Makefile b/drivers/media/platform/chips-media/Makefile
index 6b5d99de8b54..b9a07a91c9d6 100644
--- a/drivers/media/platform/chips-media/Makefile
+++ b/drivers/media/platform/chips-media/Makefile
@@ -2,3 +2,4 @@
obj-y += coda/
obj-y += wave5/
+obj-y += wave6/
diff --git a/drivers/media/platform/chips-media/wave6/Kconfig b/drivers/media/platform/chips-media/wave6/Kconfig
new file mode 100644
index 000000000000..63d79c56c7fc
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave6/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config VIDEO_WAVE6_VPU
+ tristate "Chips&Media Wave6 Codec Driver"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV && OF
+ depends on ARCH_MXC || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ select GENERIC_ALLOCATOR
+ help
+ Chips&Media Wave6 stateful codec driver.
+ The wave6 driver manages shared resources such as firmware memory.
+ The wave6-core driver provides encoding and decoding capabilities
+ for H.264, HEVC, and other video formats.
+ To compile this driver as modules, choose M here: the
+ modules will be called wave6 and wave6-core.
diff --git a/drivers/media/platform/chips-media/wave6/Makefile b/drivers/media/platform/chips-media/wave6/Makefile
new file mode 100644
index 000000000000..06f8ac9bef14
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave6/Makefile
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# tell define_trace.h where to find the trace header
+CFLAGS_wave6-vpu-core.o := -I$(src)
+
+wave6-objs += wave6-vpu.o \
+ wave6-vpu-thermal.o
+obj-$(CONFIG_VIDEO_WAVE6_VPU) += wave6.o
+
+wave6-core-objs += wave6-vpu-core.o \
+ wave6-vpu-v4l2.o \
+ wave6-vpu-dbg.o \
+ wave6-vpuapi.o \
+ wave6-vpu-dec.o \
+ wave6-vpu-enc.o \
+ wave6-hw.o
+obj-$(CONFIG_VIDEO_WAVE6_VPU) += wave6-core.o
diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu.c b/drivers/media/platform/chips-media/wave6/wave6-vpu.c
new file mode 100644
index 000000000000..c6efc4578183
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave6/wave6-vpu.c
@@ -0,0 +1,821 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/*
+ * Wave6 series multi-standard codec IP - wave6 driver
+ *
+ * Copyright (C) 2025 CHIPS&MEDIA INC
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/property.h>
+#include <linux/of_irq.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/dma-mapping.h>
+#include <linux/iopoll.h>
+#include <linux/genalloc.h>
+
+#include "wave6-vpuconfig.h"
+#include "wave6-regdefine.h"
+#include "wave6-vpu.h"
+
+static const struct wave6_vpu_resource wave633c_data = {
+ .fw_name = "cnm/wave633c_imx9_codec_fw.bin",
+ /* For HEVC, AVC, 4096x4096, 8bit */
+ .sram_size = 0x14800,
+};
+
+static const struct wave6_vpu_core_resource wave633c_core_data = {
+ .codec_types = WAVE633_CODEC_TYPE,
+ .compatible_fw_version = WAVE633_COMPATIBLE_FW_VERSION,
+};
+
+static const char *wave6_vpu_state_name(enum wave6_vpu_state state)
+{
+ switch (state) {
+ case WAVE6_VPU_STATE_OFF:
+ return "off";
+ case WAVE6_VPU_STATE_PREPARE:
+ return "prepare";
+ case WAVE6_VPU_STATE_ON:
+ return "on";
+ case WAVE6_VPU_STATE_SLEEP:
+ return "sleep";
+ default:
+ return "unknown";
+ }
+}
+
+static bool wave6_vpu_valid_transition(struct wave6_vpu_device *vpu,
+ enum wave6_vpu_state next)
+{
+ switch (vpu->state) {
+ case WAVE6_VPU_STATE_OFF:
+ /* to PREPARE: first boot attempt */
+ /* to ON: already booted before, skipping boot */
+ if (next == WAVE6_VPU_STATE_PREPARE ||
+ next == WAVE6_VPU_STATE_ON)
+ return true;
+ break;
+ case WAVE6_VPU_STATE_PREPARE:
+ /* to OFF: boot failed */
+ /* to ON: boot successful */
+ if (next == WAVE6_VPU_STATE_OFF ||
+ next == WAVE6_VPU_STATE_ON)
+ return true;
+ break;
+ case WAVE6_VPU_STATE_ON:
+ /* to OFF: sleep failed */
+ /* to SLEEP: sleep successful */
+ if (next == WAVE6_VPU_STATE_OFF ||
+ next == WAVE6_VPU_STATE_SLEEP)
+ return true;
+ break;
+ case WAVE6_VPU_STATE_SLEEP:
+ /* to OFF: resume failed */
+ /* to ON: resume successful */
+ if (next == WAVE6_VPU_STATE_OFF ||
+ next == WAVE6_VPU_STATE_ON)
+ return true;
+ break;
+ }
+
+ dev_err(vpu->dev, "invalid transition: %s -> %s\n",
+ wave6_vpu_state_name(vpu->state), wave6_vpu_state_name(next));
+
+ return false;
+}
+
+static void wave6_vpu_set_state(struct wave6_vpu_device *vpu,
+ enum wave6_vpu_state state)
+{
+ if (!wave6_vpu_valid_transition(vpu, state))
+ return;
+
+ dev_dbg(vpu->dev, "set state: %s -> %s\n",
+ wave6_vpu_state_name(vpu->state), wave6_vpu_state_name(state));
+
+ vpu->state = state;
+}
+
+static int wave6_vpu_wait_busy(struct vpu_core_device *core)
+{
+ u32 val;
+
+ return read_poll_timeout(wave6_vdi_readl, val, !val,
+ W6_VPU_POLL_DELAY_US, W6_VPU_POLL_TIMEOUT,
+ false, core->reg_base, W6_VPU_BUSY_STATUS);
+}
+
+static int wave6_vpu_check_result(struct vpu_core_device *core)
+{
+ if (wave6_vdi_readl(core->reg_base, W6_RET_SUCCESS))
+ return 0;
+
+ return wave6_vdi_readl(core->reg_base, W6_RET_FAIL_REASON);
+}
+
+static u32 wave6_vpu_get_code_buf_size(struct wave6_vpu_device *vpu)
+{
+ return min_t(u32, vpu->code_buf.size, W6_MAX_CODE_BUF_SIZE);
+}
+
+static void wave6_vpu_remap_code_buf(struct wave6_vpu_device *vpu)
+{
+ dma_addr_t code_base = vpu->code_buf.dma_addr;
+ u32 i, reg_val;
+
+ for (i = 0; i < wave6_vpu_get_code_buf_size(vpu) / W6_MAX_REMAP_PAGE_SIZE; i++) {
+ reg_val = REMAP_CTRL_ON |
+ REMAP_CTRL_INDEX(i) |
+ REMAP_CTRL_PAGE_SIZE_ON |
+ REMAP_CTRL_PAGE_SIZE(W6_MAX_REMAP_PAGE_SIZE);
+ wave6_vdi_writel(vpu->reg_base, W6_VPU_REMAP_CTRL_GB, reg_val);
+ wave6_vdi_writel(vpu->reg_base, W6_VPU_REMAP_VADDR_GB,
+ i * W6_MAX_REMAP_PAGE_SIZE);
+ wave6_vdi_writel(vpu->reg_base, W6_VPU_REMAP_PADDR_GB,
+ code_base + i * W6_MAX_REMAP_PAGE_SIZE);
+ }
+}
+
+static void wave6_vpu_init_code_buf(struct wave6_vpu_device *vpu)
+{
+ if (vpu->code_buf.size < W6_CODE_BUF_SIZE) {
+ dev_warn(vpu->dev,
+ "code buf size (%zu) is too small\n", vpu->code_buf.size);
+ memset(&vpu->code_buf, 0, sizeof(vpu->code_buf));
+ return;
+ }
+
+ vpu->code_buf.vaddr = devm_memremap(vpu->dev,
+ vpu->code_buf.phys_addr,
+ vpu->code_buf.size,
+ MEMREMAP_WC);
+ if (!vpu->code_buf.vaddr) {
+ memset(&vpu->code_buf, 0, sizeof(vpu->code_buf));
+ return;
+ }
+
+ vpu->code_buf.dma_addr = dma_map_resource(vpu->dev,
+ vpu->code_buf.phys_addr,
+ vpu->code_buf.size,
+ DMA_BIDIRECTIONAL,
+ 0);
+ if (dma_mapping_error(vpu->dev, vpu->code_buf.dma_addr)) {
+ memset(&vpu->code_buf, 0, sizeof(vpu->code_buf));
+ return;
+ }
+}
+
+static void wave6_vpu_allocate_work_buffers(struct wave6_vpu_device *vpu)
+{
+ struct vpu_buf *buf;
+ int i;
+
+ for (i = 0; i < MAX_NUM_INSTANCE; i++) {
+ buf = &vpu->work_buffers[i];
+ buf->size = W637DEC_WORKBUF_SIZE_FOR_CQ;
+
+ if (wave6_vdi_alloc_dma(vpu->dev, buf)) {
+ dev_warn(vpu->dev, "Failed to allocate work_buffers\n");
+ return;
+ }
+
+ vpu->work_buffers_alloc++;
+ }
+}
+
+static void wave6_vpu_free_work_buffers(struct wave6_vpu_device *vpu)
+{
+ int i;
+
+ for (i = 0; i < vpu->work_buffers_alloc; i++)
+ wave6_vdi_free_dma(&vpu->work_buffers[i]);
+
+ vpu->work_buffers_alloc = 0;
+ vpu->work_buffers_avail = 0;
+}
+
+static void wave6_vpu_init_work_buf(struct wave6_vpu_device *vpu,
+ struct vpu_core_device *core)
+{
+ int ret;
+
+ lockdep_assert_held(&vpu->lock);
+
+ wave6_vdi_writel(core->reg_base, W6_VPU_BUSY_STATUS, BUSY_STATUS_SET);
+ wave6_vdi_writel(core->reg_base, W6_COMMAND, W6_CMD_INIT_WORK_BUF);
+ wave6_vdi_writel(core->reg_base, W6_VPU_HOST_INT_REQ, HOST_INT_REQ_ON);
+
+ ret = wave6_vpu_wait_busy(core);
+ if (ret) {
+ dev_err(vpu->dev, "init work buf failed\n");
+ return;
+ }
+
+ ret = wave6_vpu_check_result(core);
+ if (ret) {
+ dev_err(vpu->dev, "init work buf failed, reason 0x%x\n", ret);
+ return;
+ }
+
+ vpu->work_buffers_avail = vpu->work_buffers_alloc;
+}
+
+static int wave6_vpu_init_vpu(struct wave6_vpu_device *vpu,
+ struct vpu_core_device *core)
+{
+ int ret;
+
+ lockdep_assert_held(&vpu->lock);
+
+ /* try init directly as firmware is running */
+ if (wave6_vdi_readl(core->reg_base, W6_VPU_VCPU_CUR_PC))
+ goto init_done;
+
+ wave6_vpu_set_state(vpu, WAVE6_VPU_STATE_PREPARE);
+
+ wave6_vpu_remap_code_buf(vpu);
+
+ wave6_vdi_writel(core->reg_base, W6_VPU_BUSY_STATUS, BUSY_STATUS_SET);
+ wave6_vdi_writel(core->reg_base, W6_CMD_INIT_VPU_SEC_AXI_BASE_CORE0,
+ vpu->sram_buf.dma_addr);
+ wave6_vdi_writel(core->reg_base, W6_CMD_INIT_VPU_SEC_AXI_SIZE_CORE0,
+ vpu->sram_buf.size);
+ wave6_vdi_writel(vpu->reg_base, W6_COMMAND_GB, W6_CMD_INIT_VPU);
+ wave6_vdi_writel(vpu->reg_base, W6_VPU_REMAP_CORE_START_GB,
+ REMAP_CORE_START_ON);
+
+ ret = wave6_vpu_wait_busy(core);
+ if (ret) {
+ dev_err(vpu->dev, "init vpu timeout\n");
+ wave6_vpu_set_state(vpu, WAVE6_VPU_STATE_OFF);
+ return -EINVAL;
+ }
+
+ ret = wave6_vpu_check_result(core);
+ if (ret) {
+ dev_err(vpu->dev, "init vpu fail, reason 0x%x\n", ret);
+ wave6_vpu_set_state(vpu, WAVE6_VPU_STATE_OFF);
+ return -EIO;
+ }
+
+init_done:
+ wave6_vpu_init_work_buf(vpu, core);
+ wave6_vpu_set_state(vpu, WAVE6_VPU_STATE_ON);
+
+ return 0;
+}
+
+static int wave6_vpu_sleep(struct wave6_vpu_device *vpu,
+ struct vpu_core_device *core)
+{
+ int ret;
+
+ lockdep_assert_held(&vpu->lock);
+
+ if (!wave6_vdi_readl(core->reg_base, W6_VPU_VCPU_CUR_PC)) {
+ wave6_vpu_set_state(vpu, WAVE6_VPU_STATE_OFF);
+ return 0;
+ }
+
+ wave6_vdi_writel(core->reg_base, W6_VPU_BUSY_STATUS, BUSY_STATUS_SET);
+ wave6_vdi_writel(core->reg_base, W6_COMMAND, W6_CMD_SLEEP_VPU);
+ wave6_vdi_writel(core->reg_base, W6_VPU_HOST_INT_REQ, HOST_INT_REQ_ON);
+
+ ret = wave6_vpu_wait_busy(core);
+ if (ret) {
+ dev_err(vpu->dev, "sleep vpu timeout\n");
+ wave6_vpu_set_state(vpu, WAVE6_VPU_STATE_OFF);
+ return -EINVAL;
+ }
+
+ ret = wave6_vpu_check_result(core);
+ if (ret) {
+ dev_err(vpu->dev, "sleep vpu fail, reason 0x%x\n", ret);
+ wave6_vpu_set_state(vpu, WAVE6_VPU_STATE_OFF);
+ return -EIO;
+ }
+
+ wave6_vpu_set_state(vpu, WAVE6_VPU_STATE_SLEEP);
+
+ return 0;
+}
+
+static int wave6_vpu_wakeup(struct wave6_vpu_device *vpu,
+ struct vpu_core_device *core)
+{
+ int ret;
+
+ lockdep_assert_held(&vpu->lock);
+
+ /* try wakeup directly as firmware is running */
+ if (wave6_vdi_readl(core->reg_base, W6_VPU_VCPU_CUR_PC))
+ goto wakeup_done;
+
+ wave6_vpu_remap_code_buf(vpu);
+
+ wave6_vdi_writel(core->reg_base, W6_VPU_BUSY_STATUS, BUSY_STATUS_SET);
+ wave6_vdi_writel(core->reg_base, W6_CMD_INIT_VPU_SEC_AXI_BASE_CORE0,
+ vpu->sram_buf.dma_addr);
+ wave6_vdi_writel(core->reg_base, W6_CMD_INIT_VPU_SEC_AXI_SIZE_CORE0,
+ vpu->sram_buf.size);
+ wave6_vdi_writel(vpu->reg_base, W6_COMMAND_GB, W6_CMD_WAKEUP_VPU);
+ wave6_vdi_writel(vpu->reg_base, W6_VPU_REMAP_CORE_START_GB,
+ REMAP_CORE_START_ON);
+
+ ret = wave6_vpu_wait_busy(core);
+ if (ret) {
+ dev_err(vpu->dev, "wakeup vpu timeout\n");
+ wave6_vpu_set_state(vpu, WAVE6_VPU_STATE_OFF);
+ return -EINVAL;
+ }
+
+ ret = wave6_vpu_check_result(core);
+ if (ret) {
+ dev_err(vpu->dev, "wakeup vpu fail, reason 0x%x\n", ret);
+ wave6_vpu_set_state(vpu, WAVE6_VPU_STATE_OFF);
+ return -EIO;
+ }
+
+wakeup_done:
+ wave6_vpu_set_state(vpu, WAVE6_VPU_STATE_ON);
+
+ return 0;
+}
+
+static int wave6_vpu_try_boot(struct wave6_vpu_device *vpu,
+ struct vpu_core_device *core)
+{
+ u32 product_code;
+ int ret;
+
+ lockdep_assert_held(&vpu->lock);
+
+ if (vpu->state != WAVE6_VPU_STATE_OFF && vpu->state != WAVE6_VPU_STATE_SLEEP)
+ return 0;
+
+ product_code = wave6_vdi_readl(core->reg_base, W6_VPU_RET_PRODUCT_CODE);
+ if (!wave6_is_product_w_series(product_code)) {
+ dev_err(vpu->dev, "unknown product : %08x\n", product_code);
+ return -EINVAL;
+ }
+
+ if (vpu->state == WAVE6_VPU_STATE_SLEEP) {
+ ret = wave6_vpu_wakeup(vpu, core);
+ return ret;
+ }
+
+ ret = wave6_vpu_init_vpu(vpu, core);
+
+ return ret;
+}
+
+static int wave6_vpu_get(struct wave6_vpu_device *vpu,
+ struct vpu_core_device *core)
+{
+ int ret;
+
+ if (WARN_ON(!vpu || !core))
+ return -EINVAL;
+
+ guard(mutex)(&vpu->lock);
+
+ if (!vpu->fw_available)
+ return -EINVAL;
+
+ /* Only the first core executes boot; others return */
+ if (atomic_inc_return(&vpu->core_count) > 1)
+ return 0;
+
+ ret = pm_runtime_resume_and_get(vpu->dev);
+ if (ret)
+ goto error_pm;
+
+ ret = wave6_vpu_try_boot(vpu, core);
+ if (ret)
+ goto error_boot;
+
+ return 0;
+
+error_boot:
+ pm_runtime_put_sync(vpu->dev);
+error_pm:
+ atomic_dec(&vpu->core_count);
+
+ return ret;
+}
+
+static void wave6_vpu_put(struct wave6_vpu_device *vpu,
+ struct vpu_core_device *core)
+{
+ if (WARN_ON(!vpu || !core))
+ return;
+
+ guard(mutex)(&vpu->lock);
+
+ if (!vpu->fw_available)
+ return;
+
+ /* Only the last core executes sleep; others return */
+ if (atomic_dec_return(&vpu->core_count) > 0)
+ return;
+
+ wave6_vpu_sleep(vpu, core);
+
+ if (!pm_runtime_suspended(vpu->dev))
+ pm_runtime_put_sync(vpu->dev);
+}
+
+static void wave6_vpu_require_work_buffer(struct wave6_vpu_device *vpu,
+ struct vpu_core_device *core)
+{
+ struct vpu_buf *vb;
+ u32 size;
+
+ if (WARN_ON(!vpu || !core))
+ return;
+
+ size = wave6_vdi_readl(core->reg_base, W6_CMD_SET_WORK_BUF_SIZE);
+ if (!size)
+ return;
+
+ if (WARN_ON(size > W637DEC_WORKBUF_SIZE_FOR_CQ))
+ goto exit;
+
+ if (WARN_ON(vpu->work_buffers_avail == 0))
+ goto exit;
+
+ vpu->work_buffers_avail--;
+ vb = &vpu->work_buffers[vpu->work_buffers_avail];
+
+ wave6_vdi_writel(core->reg_base, W6_CMD_SET_WORK_BUF_ADDR, vb->daddr);
+
+exit:
+ wave6_vdi_writel(core->reg_base, W6_CMD_SET_WORK_BUF_SIZE, SET_WORK_BUF_SIZE_ACK);
+}
+
+static int wave6_vpu_create_cores(struct wave6_vpu_device *vpu)
+{
+ struct device_node *child;
+ int num_cores = 0;
+
+ for_each_available_child_of_node(vpu->dev->of_node, child) {
+ struct platform_device_info info = {};
+ struct platform_device *pdev;
+ struct resource res[2];
+ int irq;
+
+ if (num_cores >= W6_VPU_MAX_NUM_CORE) {
+ of_node_put(child);
+ break;
+ }
+
+ if (of_address_to_resource(child, 0, &res[0])) {
+ dev_warn(vpu->dev, "%pOF: missing reg property\n", child);
+ continue;
+ }
+
+ irq = of_irq_get(child, 0);
+ if (irq < 0) {
+ dev_warn(vpu->dev, "%pOF: missing interrupts property\n", child);
+ continue;
+ }
+ res[1] = DEFINE_RES_IRQ(irq);
+
+ info.fwnode = of_fwnode_handle(child);
+ info.parent = vpu->dev;
+ info.name = WAVE6_VPU_CORE_PLATFORM_DRIVER_NAME;
+ info.id = num_cores;
+ info.dma_mask = DMA_BIT_MASK(32);
+ info.res = res;
+ info.num_res = ARRAY_SIZE(res);
+ info.data = &wave633c_core_data;
+ info.size_data = sizeof(wave633c_core_data);
+
+ pdev = platform_device_register_full(&info);
+ if (IS_ERR(pdev)) {
+ dev_err(vpu->dev, "Failed to register core %d: %ld\n",
+ num_cores, PTR_ERR(pdev));
+ continue;
+ }
+
+ vpu->core_pdevs[num_cores] = pdev;
+ num_cores++;
+ }
+
+ return num_cores;
+}
+
+static void wave6_vpu_destroy_cores(struct wave6_vpu_device *vpu)
+{
+ int i;
+
+ for (i = 0; i < W6_VPU_MAX_NUM_CORE; i++) {
+ struct platform_device *pdev = vpu->core_pdevs[i];
+
+ if (!pdev)
+ continue;
+
+ platform_device_unregister(pdev);
+ vpu->core_pdevs[i] = NULL;
+ }
+}
+
+static void wave6_vpu_release(struct wave6_vpu_device *vpu)
+{
+ guard(mutex)(&vpu->lock);
+
+ vpu->fw_available = false;
+ wave6_vpu_destroy_cores(vpu);
+ wave6_vpu_free_work_buffers(vpu);
+ if (vpu->sram_pool && vpu->sram_buf.vaddr) {
+ dma_unmap_resource(vpu->dev,
+ vpu->sram_buf.dma_addr,
+ vpu->sram_buf.size,
+ DMA_BIDIRECTIONAL,
+ 0);
+ gen_pool_free(vpu->sram_pool,
+ (unsigned long)vpu->sram_buf.vaddr,
+ vpu->sram_buf.size);
+ }
+ if (vpu->code_buf.dma_addr)
+ dma_unmap_resource(vpu->dev,
+ vpu->code_buf.dma_addr,
+ vpu->code_buf.size,
+ DMA_BIDIRECTIONAL,
+ 0);
+}
+
+static void wave6_vpu_load_firmware(const struct firmware *fw, void *context)
+{
+ struct wave6_vpu_device *vpu = context;
+
+ guard(mutex)(&vpu->lock);
+
+ if (!fw || !fw->data) {
+ dev_err(vpu->dev, "No firmware.\n");
+ return;
+ }
+
+ if (!vpu->fw_available)
+ goto exit;
+
+ if (fw->size + W6_EXTRA_CODE_BUF_SIZE > wave6_vpu_get_code_buf_size(vpu)) {
+ dev_err(vpu->dev, "firmware size (%zd > %zd) is too big\n",
+ fw->size, vpu->code_buf.size);
+ vpu->fw_available = false;
+ goto exit;
+ }
+
+ memcpy(vpu->code_buf.vaddr, fw->data, fw->size);
+
+ vpu->get_vpu = wave6_vpu_get;
+ vpu->put_vpu = wave6_vpu_put;
+ vpu->req_work_buffer = wave6_vpu_require_work_buffer;
+
+ if (!wave6_vpu_create_cores(vpu)) {
+ dev_err(vpu->dev, "Failed to create VPU cores\n");
+ vpu->fw_available = false;
+ }
+
+exit:
+ release_firmware(fw);
+}
+
+static void wave6_vpu_detach_pm_domains(struct wave6_vpu_device *vpu)
+{
+ if (!vpu->num_pm_domains)
+ return;
+
+ for (int i = 0; i < vpu->num_pm_domains; i++) {
+ struct device *pd_dev = vpu->pd_list->pd_devs[i];
+
+ if (!IS_ERR_OR_NULL(pd_dev) && !pm_runtime_suspended(pd_dev))
+ pm_runtime_force_suspend(pd_dev);
+ }
+
+ dev_pm_domain_detach_list(vpu->pd_list);
+ vpu->pd_list = NULL;
+ vpu->num_pm_domains = 0;
+}
+
+static int wave6_vpu_attach_pm_domains(struct wave6_vpu_device *vpu)
+{
+ int ret;
+
+ vpu->num_pm_domains = of_count_phandle_with_args(vpu->dev->of_node,
+ "power-domains",
+ "#power-domain-cells");
+ if (vpu->num_pm_domains < 0) {
+ dev_err(vpu->dev, "No power domains defined for vpu node\n");
+ return vpu->num_pm_domains;
+ }
+
+ if (vpu->num_pm_domains == 1) {
+ /* genpd_dev_pm_attach() attach automatically if count is 1 */
+ vpu->num_pm_domains = 0;
+ return 0;
+ }
+
+ ret = dev_pm_domain_attach_list(vpu->dev, NULL, &vpu->pd_list);
+ if (ret < 0) {
+ dev_err(vpu->dev, "Can't attach pm domains, ret = %d\n", ret);
+ return ret;
+ }
+
+ vpu->num_pm_domains = ret;
+
+ return 0;
+}
+
+static struct device *wave6_vpu_get_performance_dev(struct wave6_vpu_device *vpu)
+{
+ int index;
+
+ if (!vpu->num_pm_domains)
+ return vpu->dev;
+
+ index = of_property_match_string(vpu->dev->of_node,
+ "power-domain-names", "perf");
+ if (index < 0 || index >= vpu->num_pm_domains)
+ return NULL;
+
+ return vpu->pd_list->pd_devs[index];
+}
+
+static int wave6_vpu_probe(struct platform_device *pdev)
+{
+ struct device_node *np;
+ struct wave6_vpu_device *vpu;
+ const struct wave6_vpu_resource *res;
+ int ret;
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to set DMA mask: %d\n", ret);
+ return ret;
+ }
+
+ res = device_get_match_data(&pdev->dev);
+ if (!res)
+ return -ENODEV;
+
+ vpu = devm_kzalloc(&pdev->dev, sizeof(*vpu), GFP_KERNEL);
+ if (!vpu)
+ return -ENOMEM;
+
+ ret = devm_mutex_init(&pdev->dev, &vpu->lock);
+ if (ret)
+ return ret;
+
+ atomic_set(&vpu->core_count, 0);
+ dev_set_drvdata(&pdev->dev, vpu);
+ vpu->dev = &pdev->dev;
+ vpu->res = res;
+ vpu->reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(vpu->reg_base))
+ return PTR_ERR(vpu->reg_base);
+
+ ret = devm_clk_bulk_get_all(&pdev->dev, &vpu->clks);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "failed to get clocks\n");
+
+ vpu->num_clks = ret;
+
+ ret = wave6_vpu_attach_pm_domains(vpu);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to attach pm domains\n");
+
+ np = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
+ if (np) {
+ struct resource mem;
+
+ ret = of_address_to_resource(np, 0, &mem);
+ of_node_put(np);
+ if (!ret) {
+ vpu->code_buf.phys_addr = mem.start;
+ vpu->code_buf.size = resource_size(&mem);
+ wave6_vpu_init_code_buf(vpu);
+ } else {
+ dev_warn(&pdev->dev, "memory-region is not available.\n");
+ }
+ }
+
+ vpu->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
+ if (vpu->sram_pool) {
+ vpu->sram_buf.size = vpu->res->sram_size;
+ vpu->sram_buf.vaddr = gen_pool_dma_alloc(vpu->sram_pool,
+ vpu->sram_buf.size,
+ &vpu->sram_buf.phys_addr);
+ if (!vpu->sram_buf.vaddr) {
+ vpu->sram_buf.size = 0;
+ } else {
+ vpu->sram_buf.dma_addr = dma_map_resource(&pdev->dev,
+ vpu->sram_buf.phys_addr,
+ vpu->sram_buf.size,
+ DMA_BIDIRECTIONAL,
+ 0);
+ if (dma_mapping_error(&pdev->dev, vpu->sram_buf.dma_addr)) {
+ gen_pool_free(vpu->sram_pool,
+ (unsigned long)vpu->sram_buf.vaddr,
+ vpu->sram_buf.size);
+ memset(&vpu->sram_buf, 0, sizeof(vpu->sram_buf));
+ }
+ }
+ }
+
+ vpu->thermal.dev = wave6_vpu_get_performance_dev(vpu);
+ if (vpu->thermal.dev) {
+ ret = wave6_vpu_cooling_init(vpu->dev, &vpu->thermal);
+ if (ret)
+ dev_err(&pdev->dev, "failed to initialize thermal cooling, %d\n", ret);
+ }
+
+ wave6_vpu_allocate_work_buffers(vpu);
+
+ pm_runtime_enable(&pdev->dev);
+ vpu->fw_available = true;
+
+ request_module("platform:%s", WAVE6_VPU_CORE_PLATFORM_DRIVER_NAME);
+
+ ret = firmware_request_nowait_nowarn(THIS_MODULE,
+ vpu->res->fw_name,
+ &pdev->dev,
+ GFP_KERNEL,
+ vpu,
+ wave6_vpu_load_firmware);
+ if (ret) {
+ dev_err(&pdev->dev, "request firmware fail, ret = %d\n", ret);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ pm_runtime_disable(&pdev->dev);
+ wave6_vpu_release(vpu);
+ wave6_vpu_detach_pm_domains(vpu);
+
+ return ret;
+}
+
+static void wave6_vpu_remove(struct platform_device *pdev)
+{
+ struct wave6_vpu_device *vpu = dev_get_drvdata(&pdev->dev);
+
+ pm_runtime_disable(vpu->dev);
+ wave6_vpu_release(vpu);
+ wave6_vpu_detach_pm_domains(vpu);
+}
+
+static int wave6_vpu_runtime_suspend(struct device *dev)
+{
+ struct wave6_vpu_device *vpu = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(vpu->num_clks, vpu->clks);
+
+ return 0;
+}
+
+static int wave6_vpu_runtime_resume(struct device *dev)
+{
+ struct wave6_vpu_device *vpu = dev_get_drvdata(dev);
+
+ return clk_bulk_prepare_enable(vpu->num_clks, vpu->clks);
+}
+
+static const struct dev_pm_ops wave6_vpu_pm_ops = {
+ RUNTIME_PM_OPS(wave6_vpu_runtime_suspend,
+ wave6_vpu_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static const struct of_device_id wave6_vpu_ids[] = {
+ { .compatible = "nxp,imx95-vpu", .data = &wave633c_data },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, wave6_vpu_ids);
+
+static struct platform_driver wave6_vpu_driver = {
+ .driver = {
+ .name = WAVE6_VPU_PLATFORM_DRIVER_NAME,
+ .of_match_table = wave6_vpu_ids,
+ .pm = pm_ptr(&wave6_vpu_pm_ops),
+ },
+ .probe = wave6_vpu_probe,
+ .remove = wave6_vpu_remove,
+};
+
+module_platform_driver(wave6_vpu_driver);
+MODULE_DESCRIPTION("chips&media Wave6 VPU driver");
+MODULE_AUTHOR("CHIPS&MEDIA INC");
+MODULE_FIRMWARE("cnm/wave633c_imx9_codec_fw.bin");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu.h b/drivers/media/platform/chips-media/wave6/wave6-vpu.h
new file mode 100644
index 000000000000..ec3c9299526b
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave6/wave6-vpu.h
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Wave6 series multi-standard codec IP - wave6 driver
+ *
+ * Copyright (C) 2025 CHIPS&MEDIA INC
+ */
+
+#ifndef __WAVE6_VPU_H__
+#define __WAVE6_VPU_H__
+
+#include <linux/device.h>
+#include "wave6-vpu-thermal.h"
+#include "wave6-vdi.h"
+#include "wave6-vpuapi.h"
+
+#define WAVE6_VPU_PLATFORM_DRIVER_NAME "wave6-vpu"
+#define WAVE6_VPU_CORE_PLATFORM_DRIVER_NAME "wave6-vpu-core"
+
+struct wave6_vpu_device;
+struct vpu_core_device;
+
+/**
+ * enum wave6_vpu_state - VPU states
+ * @WAVE6_VPU_STATE_OFF: VPU is powered off
+ * @WAVE6_VPU_STATE_PREPARE: VPU is booting
+ * @WAVE6_VPU_STATE_ON: VPU is running
+ * @WAVE6_VPU_STATE_SLEEP: VPU is in a sleep mode
+ */
+enum wave6_vpu_state {
+ WAVE6_VPU_STATE_OFF,
+ WAVE6_VPU_STATE_PREPARE,
+ WAVE6_VPU_STATE_ON,
+ WAVE6_VPU_STATE_SLEEP
+};
+
+/**
+ * struct wave6_vpu_dma_buf - VPU buffer from reserved memory or gen_pool
+ * @size: Buffer size
+ * @dma_addr: Mapped address for device access
+ * @vaddr: Kernel virtual address
+ * @phys_addr: Physical address of the reserved memory region or gen_pool
+ *
+ * Represents a buffer allocated from pre-reserved device memory regions or
+ * SRAM via gen_pool_dma_alloc(). Used for code and SRAM buffers only.
+ * Managed by the VPU device.
+ */
+struct wave6_vpu_dma_buf {
+ size_t size;
+ dma_addr_t dma_addr;
+ void *vaddr;
+ phys_addr_t phys_addr;
+};
+
+/**
+ * struct wave6_vpu_resource - VPU device compatible data
+ * @fw_name: Firmware name for the device
+ * @sram_size: Required SRAM size
+ */
+struct wave6_vpu_resource {
+ const char *fw_name;
+ u32 sram_size;
+};
+
+#define WAVE6_IS_ENC BIT(0)
+#define WAVE6_IS_DEC BIT(1)
+
+#define WAVE633_CODEC_TYPE (WAVE6_IS_ENC | WAVE6_IS_DEC)
+#define WAVE633_COMPATIBLE_FW_VERSION 0x2010000
+
+/**
+ * struct wave6_vpu_core_resource - VPU CORE device compatible data
+ * @codec_types: Bitmask of supported codec types
+ * @compatible_fw_version: Firmware version compatible with driver
+ */
+struct wave6_vpu_core_resource {
+ int codec_types;
+ u32 compatible_fw_version;
+};
+
+/**
+ * struct wave6_vpu_device - VPU driver structure
+ * @get_vpu: Function pointer, boot or wake the device
+ * @put_vpu: Function pointer, power off or suspend the device
+ * @req_work_buffer: Function pointer, request allocation of a work buffer
+ * @dev: Platform device pointer
+ * @reg_base: Base address of MMIO registers
+ * @clks: Array of clock handles
+ * @num_clks: Number of entries in @clks
+ * @state: Device state
+ * @lock: Mutex protecting device data, register access
+ * @fw_available: Firmware availability flag
+ * @res: Device compatible data
+ * @sram_pool: Genalloc pool for SRAM allocations
+ * @sram_buf: Optional SRAM buffer
+ * @code_buf: Firmware code buffer
+ * @work_buffers: Array of work buffers
+ * @work_buffers_alloc: Number of allocated work buffers
+ * @work_buffers_avail: Number of available work buffers
+ * @thermal: Thermal cooling device
+ * @core_count: Number of available VPU core devices
+ *
+ * @get_vpu, @put_vpu, @req_work_buffer are called by VPU core devices.
+ *
+ * Buffers such as @sram_buf, @code_buf, and @work_buffers are managed
+ * by the VPU device and accessed exclusively by the firmware.
+ */
+struct wave6_vpu_device {
+ int (*get_vpu)(struct wave6_vpu_device *vpu,
+ struct vpu_core_device *core);
+ void (*put_vpu)(struct wave6_vpu_device *vpu,
+ struct vpu_core_device *core);
+ void (*req_work_buffer)(struct wave6_vpu_device *vpu,
+ struct vpu_core_device *core);
+ struct device *dev;
+ void __iomem *reg_base;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ enum wave6_vpu_state state;
+ struct mutex lock; /* Protects device data, register access */
+
+ /* Prevents boot or sleep sequence if firmware is unavailable. */
+ bool fw_available;
+
+ const struct wave6_vpu_resource *res;
+ struct gen_pool *sram_pool;
+ struct wave6_vpu_dma_buf sram_buf;
+ struct wave6_vpu_dma_buf code_buf;
+
+ /* Allocates per-instance, used for storing instance-specific data. */
+ struct vpu_buf work_buffers[MAX_NUM_INSTANCE];
+ u32 work_buffers_alloc;
+ u32 work_buffers_avail;
+
+ struct vpu_thermal_cooling thermal;
+ atomic_t core_count;
+
+ int num_pm_domains;
+ struct dev_pm_domain_list *pd_list;
+
+ struct platform_device *core_pdevs[W6_VPU_MAX_NUM_CORE];
+};
+
+#endif /* __WAVE6_VPU_H__ */
--
2.31.1
^ permalink raw reply related
* [PATCH v6 9/9] arm64: dts: freescale: imx95: Add video codec node
From: Nas Chung @ 2026-06-24 7:20 UTC (permalink / raw)
To: mchehab, hverkuil, robh, krzk+dt, conor+dt, shawnguo, s.hauer
Cc: linux-media, devicetree, linux-kernel, linux-imx,
linux-arm-kernel, jackson.lee, lafley.kim, marek.vasut, Nas Chung
In-Reply-To: <20260624072043.238-1-nas.chung@chipsnmedia.com>
Add the Chips and Media wave633 video codec node on IMX95 SoCs.
Signed-off-by: Nas Chung <nas.chung@chipsnmedia.com>
---
.../boot/dts/freescale/imx95-19x19-evk.dts | 11 ++++++
arch/arm64/boot/dts/freescale/imx95.dtsi | 36 +++++++++++++++++++
2 files changed, 47 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
index 041fd838fabb..7edd1c69966a 100644
--- a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
+++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
@@ -76,6 +76,11 @@ linux_cma: linux,cma {
linux,cma-default;
reusable;
};
+
+ vpu_boot: memory@a0000000 {
+ reg = <0 0xa0000000 0 0x100000>;
+ no-map;
+ };
};
flexcan1_phy: can-phy0 {
@@ -1142,3 +1147,9 @@ &tpm6 {
pinctrl-0 = <&pinctrl_tpm6>;
status = "okay";
};
+
+&vpu {
+ memory-region = <&vpu_boot>;
+ sram = <&sram1>;
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/freescale/imx95.dtsi b/arch/arm64/boot/dts/freescale/imx95.dtsi
index 71394871d8dd..f0b2bc2be907 100644
--- a/arch/arm64/boot/dts/freescale/imx95.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx95.dtsi
@@ -2012,6 +2012,42 @@ vpu_blk_ctrl: clock-controller@4c410000 {
assigned-clock-rates = <133333333>, <667000000>, <500000000>;
};
+ vpu: video-codec@4c4c0000 {
+ compatible = "nxp,imx95-vpu";
+ reg = <0x0 0x4c4c0000 0x0 0x10000>;
+ clocks = <&scmi_clk IMX95_CLK_VPU>,
+ <&vpu_blk_ctrl IMX95_CLK_VPUBLK_WAVE>;
+ clock-names = "core", "vpublk";
+ power-domains = <&scmi_devpd IMX95_PD_VPU>,
+ <&scmi_perf IMX95_PERF_VPU>;
+ power-domain-names = "vpu", "perf";
+ #cooling-cells = <2>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ status = "disabled";
+
+ interface@4c480000 {
+ reg = <0x0 0x4c480000 0x0 0x10000>;
+ interrupts = <GIC_SPI 299 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ interface@4c490000 {
+ reg = <0x0 0x4c490000 0x0 0x10000>;
+ interrupts = <GIC_SPI 300 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ interface@4c4a0000 {
+ reg = <0x0 0x4c4a0000 0x0 0x10000>;
+ interrupts = <GIC_SPI 301 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ interface@4c4b0000 {
+ reg = <0x0 0x4c4b0000 0x0 0x10000>;
+ interrupts = <GIC_SPI 302 IRQ_TYPE_LEVEL_HIGH>;
+ };
+ };
+
jpegdec: jpegdec@4c500000 {
compatible = "nxp,imx95-jpgdec", "nxp,imx8qxp-jpgdec";
reg = <0x0 0x4C500000 0x0 0x00050000>;
--
2.31.1
^ permalink raw reply related
* [PATCH v6 7/9] media: chips-media: wave6: Add Wave6 thermal cooling device
From: Nas Chung @ 2026-06-24 7:20 UTC (permalink / raw)
To: mchehab, hverkuil, robh, krzk+dt, conor+dt, shawnguo, s.hauer
Cc: linux-media, devicetree, linux-kernel, linux-imx,
linux-arm-kernel, jackson.lee, lafley.kim, marek.vasut, Nas Chung,
Ming Qian
In-Reply-To: <20260624072043.238-1-nas.chung@chipsnmedia.com>
Add a thermal cooling device for the Wave6 VPU.
The device operates within the Linux thermal framework,
adjusting the VPU performance state based on thermal conditions.
Signed-off-by: Nas Chung <nas.chung@chipsnmedia.com>
Tested-by: Ming Qian <ming.qian@oss.nxp.com>
Tested-by: Marek Vasut <marek.vasut@mailbox.org>
---
.../chips-media/wave6/wave6-vpu-thermal.c | 139 ++++++++++++++++++
.../chips-media/wave6/wave6-vpu-thermal.h | 24 +++
2 files changed, 163 insertions(+)
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.c
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.h
diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.c b/drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.c
new file mode 100644
index 000000000000..91cd826e8119
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/*
+ * Wave6 series multi-standard codec IP - wave6 thermal cooling interface
+ *
+ * Copyright (C) 2025 CHIPS&MEDIA INC
+ *
+ */
+
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
+#include <linux/units.h>
+#include <linux/slab.h>
+#include "wave6-vpu-thermal.h"
+
+static int wave6_vpu_thermal_cooling_update(struct vpu_thermal_cooling *thermal,
+ int state)
+{
+ unsigned long new_clock_rate;
+ int ret;
+
+ if (state > thermal->thermal_max || !thermal->cooling)
+ return 0;
+
+ new_clock_rate = DIV_ROUND_UP(thermal->freq_table[state], HZ_PER_KHZ);
+ dev_dbg(thermal->dev, "receive cooling state: %d, new clock rate %ld\n",
+ state, new_clock_rate);
+
+ ret = dev_pm_genpd_set_performance_state(thermal->dev, new_clock_rate);
+ if (ret && !((ret == -ENODEV) || (ret == -EOPNOTSUPP))) {
+ dev_err(thermal->dev, "failed to set perf to %lu, ret = %d\n",
+ new_clock_rate, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wave6_vpu_cooling_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct vpu_thermal_cooling *thermal = cdev->devdata;
+
+ *state = thermal->thermal_max;
+
+ return 0;
+}
+
+static int wave6_vpu_cooling_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct vpu_thermal_cooling *thermal = cdev->devdata;
+
+ *state = thermal->thermal_event;
+
+ return 0;
+}
+
+static int wave6_vpu_cooling_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state)
+{
+ struct vpu_thermal_cooling *thermal = cdev->devdata;
+
+ thermal->thermal_event = state;
+ wave6_vpu_thermal_cooling_update(thermal, state);
+
+ return 0;
+}
+
+static const struct thermal_cooling_device_ops wave6_cooling_ops = {
+ .get_max_state = wave6_vpu_cooling_get_max_state,
+ .get_cur_state = wave6_vpu_cooling_get_cur_state,
+ .set_cur_state = wave6_vpu_cooling_set_cur_state,
+};
+
+int wave6_vpu_cooling_init(struct device *dev, struct vpu_thermal_cooling *thermal)
+{
+ int i;
+ int num_opps;
+ unsigned long freq;
+ int ret = -EINVAL;
+
+ if (WARN_ON(!thermal || !thermal->dev))
+ return -EINVAL;
+
+ num_opps = dev_pm_opp_get_opp_count(thermal->dev);
+ if (num_opps < 0) {
+ dev_err(thermal->dev, "fail to get pm opp count, ret = %d\n", num_opps);
+ return num_opps;
+ }
+ if (num_opps == 0) {
+ dev_err(thermal->dev, "no OPP entries found\n");
+ return -ENODEV;
+ }
+
+ thermal->freq_table = devm_kcalloc(dev, num_opps,
+ sizeof(*thermal->freq_table),
+ GFP_KERNEL);
+ if (!thermal->freq_table) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ for (i = 0, freq = ULONG_MAX; i < num_opps; i++, freq--) {
+ struct dev_pm_opp *opp;
+
+ opp = dev_pm_opp_find_freq_floor(thermal->dev, &freq);
+ if (IS_ERR(opp))
+ break;
+
+ dev_pm_opp_put(opp);
+
+ dev_dbg(thermal->dev, "[%d] = %lu\n", i, freq);
+ if (freq < 100 * HZ_PER_MHZ)
+ break;
+
+ thermal->freq_table[i] = freq;
+ thermal->thermal_max = i;
+ }
+
+ if (!thermal->thermal_max)
+ goto error;
+
+ thermal->thermal_event = 0;
+ thermal->cooling = devm_thermal_of_cooling_device_register(dev,
+ dev->of_node,
+ dev_name(thermal->dev),
+ thermal,
+ &wave6_cooling_ops);
+ if (IS_ERR(thermal->cooling)) {
+ dev_err(thermal->dev, "register cooling device failed\n");
+ ret = PTR_ERR(thermal->cooling);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return ret;
+}
diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.h b/drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.h
new file mode 100644
index 000000000000..7c5e8aed6ef7
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Wave6 series multi-standard codec IP - wave6 thermal cooling interface
+ *
+ * Copyright (C) 2025 CHIPS&MEDIA INC
+ *
+ */
+
+#ifndef __WAVE6_VPU_THERMAL_H__
+#define __WAVE6_VPU_THERMAL_H__
+
+#include <linux/thermal.h>
+
+struct vpu_thermal_cooling {
+ struct device *dev;
+ int thermal_event;
+ int thermal_max;
+ struct thermal_cooling_device *cooling;
+ unsigned long *freq_table;
+};
+
+int wave6_vpu_cooling_init(struct device *dev, struct vpu_thermal_cooling *thermal);
+
+#endif /* __WAVE6_VPU_THERMAL_H__ */
--
2.31.1
^ permalink raw reply related
* [PATCH v6 1/9] media: v4l2-common: Fix P010 format info
From: Nas Chung @ 2026-06-24 7:20 UTC (permalink / raw)
To: mchehab, hverkuil, robh, krzk+dt, conor+dt, shawnguo, s.hauer
Cc: linux-media, devicetree, linux-kernel, linux-imx,
linux-arm-kernel, jackson.lee, lafley.kim, marek.vasut, Nas Chung
In-Reply-To: <20260624072043.238-1-nas.chung@chipsnmedia.com>
V4L2_PIX_FMT_P010 is a 10-bit 4:2:0 semi-planar format, but its
v4l2_format_info() entry described a 4:2:2 layout with a half-width
chroma plane. Correct bpp and vdiv to match P010's real layout,
consistent with the other semi-planar formats.
Fixes: 5374d8fb75f3 ("media: Add P010 video format")
Signed-off-by: Nas Chung <nas.chung@chipsnmedia.com>
---
drivers/media/v4l2-core/v4l2-common.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 65db7340ad38..4de8aa3ef7d2 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -323,7 +323,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_NV20, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_NV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_NV42, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_P010, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 2, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_P010, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 2, 4, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
{ .format = V4L2_PIX_FMT_P012, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 2, 4, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
{ .format = V4L2_PIX_FMT_YUV410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 4, .vdiv = 4 },
--
2.31.1
^ permalink raw reply related
* [PATCH v6 2/9] dt-bindings: media: nxp: Add Wave6 video codec device
From: Nas Chung @ 2026-06-24 7:20 UTC (permalink / raw)
To: mchehab, hverkuil, robh, krzk+dt, conor+dt, shawnguo, s.hauer
Cc: linux-media, devicetree, linux-kernel, linux-imx,
linux-arm-kernel, jackson.lee, lafley.kim, marek.vasut, Nas Chung
In-Reply-To: <20260624072043.238-1-nas.chung@chipsnmedia.com>
Add documentation for the Chips&Media Wave6 video codec on NXP i.MX SoCs.
The hardware contains one control register region and four interface
register regions for a shared video processing engine. The control region
manages shared resources such as firmware memory, while each interface
region has its own MMIO range and interrupt.
The control region and each interface region are distinct DMA requesters
and can be associated with separate IOMMU stream IDs. Represent the
control region as the parent node and the interface register regions as
child nodes to describe these resources.
Signed-off-by: Nas Chung <nas.chung@chipsnmedia.com>
---
.../bindings/media/nxp,imx95-vpu.yaml | 163 ++++++++++++++++++
MAINTAINERS | 7 +
2 files changed, 170 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/nxp,imx95-vpu.yaml
diff --git a/Documentation/devicetree/bindings/media/nxp,imx95-vpu.yaml b/Documentation/devicetree/bindings/media/nxp,imx95-vpu.yaml
new file mode 100644
index 000000000000..9a5ca53e15a3
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/nxp,imx95-vpu.yaml
@@ -0,0 +1,163 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/nxp,imx95-vpu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Chips&Media Wave6 Series multi-standard codec IP on NXP i.MX SoCs
+
+maintainers:
+ - Nas Chung <nas.chung@chipsnmedia.com>
+ - Jackson Lee <jackson.lee@chipsnmedia.com>
+
+description:
+ The Chips&Media Wave6 codec IP is a multi-standard video encoder/decoder.
+ On NXP i.MX SoCs, the Wave6 codec IP exposes one control register region and
+ four interface register regions for a shared video processing engine.
+ The parent node describes the control region, which has its own MMIO range and
+ manages shared resources such as firmware memory. The child nodes describe the
+ interface register regions. Each interface region has its own MMIO range and
+ interrupt.
+ The control region and the interface regions are distinct DMA requesters.
+ The control region and each interface region can be associated with separate
+ IOMMU stream IDs, allowing DMA isolation between them.
+
+properties:
+ compatible:
+ enum:
+ - nxp,imx95-vpu
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: VPU core clock
+ - description: VPU associated block clock
+
+ clock-names:
+ items:
+ - const: core
+ - const: vpublk
+
+ power-domains:
+ items:
+ - description: Main VPU power domain
+ - description: Performance power domain
+
+ power-domain-names:
+ items:
+ - const: vpu
+ - const: perf
+
+ memory-region:
+ maxItems: 1
+
+ sram:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ phandle to the SRAM node used to store reference data, reducing DMA
+ memory bandwidth.
+
+ iommus:
+ maxItems: 1
+
+ "#cooling-cells":
+ const: 2
+
+ "#address-cells":
+ const: 2
+
+ "#size-cells":
+ const: 2
+
+ ranges: true
+
+patternProperties:
+ "^interface@[0-9a-f]+$":
+ type: object
+ description:
+ An interface register region within the Chips&Media Wave6 codec IP.
+ Each region has its own MMIO range and interrupt and can be associated
+ with a separate IOMMU stream ID for DMA isolation.
+ additionalProperties: false
+
+ properties:
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ iommus:
+ maxItems: 1
+
+ required:
+ - reg
+ - interrupts
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - power-domains
+ - power-domain-names
+ - memory-region
+ - "#address-cells"
+ - "#size-cells"
+ - ranges
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/nxp,imx95-clock.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ video-codec@4c4c0000 {
+ compatible = "nxp,imx95-vpu";
+ reg = <0x0 0x4c4c0000 0x0 0x10000>;
+ clocks = <&scmi_clk 115>,
+ <&vpu_blk_ctrl IMX95_CLK_VPUBLK_WAVE>;
+ clock-names = "core", "vpublk";
+ power-domains = <&scmi_devpd 21>,
+ <&scmi_perf 10>;
+ power-domain-names = "vpu", "perf";
+ memory-region = <&vpu_boot>;
+ sram = <&sram1>;
+ iommus = <&smmu 0x32>;
+ #cooling-cells = <2>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ interface@4c480000 {
+ reg = <0x0 0x4c480000 0x0 0x10000>;
+ interrupts = <GIC_SPI 299 IRQ_TYPE_LEVEL_HIGH>;
+ iommus = <&smmu 0x33>;
+ };
+
+ interface@4c490000 {
+ reg = <0x0 0x4c490000 0x0 0x10000>;
+ interrupts = <GIC_SPI 300 IRQ_TYPE_LEVEL_HIGH>;
+ iommus = <&smmu 0x34>;
+ };
+
+ interface@4c4a0000 {
+ reg = <0x0 0x4c4a0000 0x0 0x10000>;
+ interrupts = <GIC_SPI 301 IRQ_TYPE_LEVEL_HIGH>;
+ iommus = <&smmu 0x35>;
+ };
+
+ interface@4c4b0000 {
+ reg = <0x0 0x4c4b0000 0x0 0x10000>;
+ interrupts = <GIC_SPI 302 IRQ_TYPE_LEVEL_HIGH>;
+ iommus = <&smmu 0x36>;
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index efbf808063e5..77ea3a1a966b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -28688,6 +28688,13 @@ S: Maintained
F: Documentation/devicetree/bindings/media/cnm,wave521c.yaml
F: drivers/media/platform/chips-media/wave5/
+WAVE6 VPU CODEC DRIVER
+M: Nas Chung <nas.chung@chipsnmedia.com>
+M: Jackson Lee <jackson.lee@chipsnmedia.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/nxp,imx95-vpu.yaml
+
WHISKEYCOVE PMIC GPIO DRIVER
M: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
L: linux-gpio@vger.kernel.org
--
2.31.1
^ permalink raw reply related
* [PATCH v6 0/9] Add support for Wave6 video codec driver
From: Nas Chung @ 2026-06-24 7:20 UTC (permalink / raw)
To: mchehab, hverkuil, robh, krzk+dt, conor+dt, shawnguo, s.hauer
Cc: linux-media, devicetree, linux-kernel, linux-imx,
linux-arm-kernel, jackson.lee, lafley.kim, marek.vasut, Nas Chung
This patch series introduces support for the Chips&Media Wave6 video
codec IP, a completely different hardware architecture compared to Wave5.
The wave6 driver is a M2M stateful encoder/decoder driver.
It supports various video formats, including H.264 and H.265, for both encoding
and decoding.
While other versions of the Wave6 IP may support VP9 decoding and AV1 decoding
and encoding those formats are not implemented or validated in this driver at
this time.
On NXP i.MX SoCs, the Wave6 hardware exposes one control register region and
four interface register regions for one shared video processing engine.
VPU Control region, Manages shared resources such as firmware memory.
VPU Core region, Provides encoding and decoding capabilities.
The control and interface regions are distinct DMA requesters and can be
associated with separate IOMMU stream IDs, allowing DMA isolation between them.
The firmware tested by this driver has been upstreamed in linux-firmware:
- Path: cnm/wave633c_imx9_codec_fw.bin
This driver has been tested with GStreamer on:
- NXP i.MX95 board
- pre-silicon FPGA environment
Test results for decoder fluster with -j2 option:
- JVT-AVC_V1, Ran 77/135 tests successfully in 24.180 secs
- JVT-FR-EXT, Ran 25/69 tests successfully in 11.157 secs
- JCT-VC-HEVC_V1, Ran 132/147 tests successfully in 45.534 secs
- All failures are due to unsupported hardware features:
-- 10bit, Resolutions higher than 4K, FMO, MBAFF
-- Extended profile, Field encoding and High422 sreams.
Test results for v4l2-compliance:
v4l2-compliance 1.31.0-5386, 64 bits, 64-bit time_t
v4l2-compliance SHA: 48316b8a20aa 2025-08-12 12:44:56
Compliance test for wave6-dec device /dev/video0:
fail: v4l2-test-controls.cpp(1204): !have_source_change || !have_eos
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: FAIL
Total for wave6-dec device /dev/video0: 48, Succeeded: 47, Failed: 1, Warnings: 0
Compliance test for wave6-enc device /dev/video1:
fail: v4l2-test-controls.cpp(1193): node->codec_mask & STATEFUL_ENCODER
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: FAIL
Total for wave6-enc device /dev/video1: 48, Succeeded: 47, Failed: 1, Warnings: 0
Note: the failures are all related with the eos event.
Changelog:
v6:
- Fixed the existing P010 format_info entry
- Moved all encoder parameter validation from the HW layer into the V4L2 layer
- Removed redundant HW-abstraction wrappers from the VPU API layer
- Computed frame stride and sizeimage in the V4L2 layer
- Added explicit instance lifecycle helpers to the core driver
- Enabled the VPU only on the tested imx95-19x19-evk board
RFC v5:
- Move all shared resources to the parent node
- Drop child compatible and use data-only interface child nodes
- Update the VPU driver to create child devices and load the core driver
v4:
- Fixed build issues reported by CI tools
- Updated commit messages to use imperative mood
- Avoided using the same name for both nodes and labels in devicetree
- Removed unused labels from YAML examples
- Added description for child(vpu-core) node
- Added iommus property to both parent(vpu) and child(vpu-core) nodes
- Updated probe() functions to use dev_err_probe() when returning -EPROBE_DEFER
- Added wave6_vpu prefix to trace functions
- Updated HEVC decoder profile control to report MAIN_STILL profile
- Fixed bug in multiple instance creation by pre-allocating work buffer
- Fixed interrupt handling by checking INSTANCE_INFO register and instance list
v3:
- Removed ambiguous SUPPORT_FOLLOWER feature
- Used WARN_ON() for unexpected programming errors
- Split thermal device code into wave6-vpu-thermal.c/h
- Dropped wave6_cooling_disable module parameter
- Replaced mutex_lock() with guard()
- Added lockdep_assert_held() to clarify locking regions
- Removed exported function due to dual-license and used function pointer
- Added documentation and validation for state transitions
- Added documentation for device structures
- Added patch to enable VPU device in imx95 DTS
- Updated DT bindings and driver to align with parent(vpu) and child(vpu-core)
- Replaced magic numbers with mask and offset macros when accessing registers
- Placed goto statements after an empty line
- Printed HW info (e.g. product_code) via dev_dbg() for debugging
- Replaced wave6_vpu_dec_give_command() with dedicated functions
v2:
- Refined DT bindings to better represent the hardware
- Reworked driver to align with the parent(VPU) and child(CTRL, CORE)
- Fixed build issues reported by CI tools (Smatch, Sparse, TRACE)
- Improved commit messages with clearer descriptions
- Added kernel-doc for exported functions
- Removed redundant print statements and unused code
- Reordered patches to prevent build failures
Nas Chung (9):
media: v4l2-common: Fix P010 format info
dt-bindings: media: nxp: Add Wave6 video codec device
media: chips-media: wave6: Add Wave6 VPU interface
media: chips-media: wave6: Add v4l2 m2m driver support
media: chips-media: wave6: Add Wave6 core driver
media: chips-media: wave6: Improve debugging capabilities
media: chips-media: wave6: Add Wave6 thermal cooling device
media: chips-media: wave6: Add Wave6 control driver
arm64: dts: freescale: imx95: Add video codec node
.../bindings/media/nxp,imx95-vpu.yaml | 163 +
MAINTAINERS | 8 +
.../boot/dts/freescale/imx95-19x19-evk.dts | 11 +
arch/arm64/boot/dts/freescale/imx95.dtsi | 36 +
drivers/media/platform/chips-media/Kconfig | 1 +
drivers/media/platform/chips-media/Makefile | 1 +
.../media/platform/chips-media/wave6/Kconfig | 17 +
.../media/platform/chips-media/wave6/Makefile | 17 +
.../platform/chips-media/wave6/wave6-hw.c | 2086 +++++++++++++
.../platform/chips-media/wave6/wave6-hw.h | 56 +
.../chips-media/wave6/wave6-regdefine.h | 647 ++++
.../platform/chips-media/wave6/wave6-trace.h | 289 ++
.../platform/chips-media/wave6/wave6-vdi.h | 92 +
.../chips-media/wave6/wave6-vpu-core.c | 437 +++
.../chips-media/wave6/wave6-vpu-core.h | 126 +
.../chips-media/wave6/wave6-vpu-dbg.c | 177 ++
.../chips-media/wave6/wave6-vpu-dbg.h | 14 +
.../chips-media/wave6/wave6-vpu-dec.c | 1831 +++++++++++
.../chips-media/wave6/wave6-vpu-enc.c | 2758 +++++++++++++++++
.../chips-media/wave6/wave6-vpu-thermal.c | 139 +
.../chips-media/wave6/wave6-vpu-thermal.h | 24 +
.../chips-media/wave6/wave6-vpu-v4l2.c | 516 +++
.../platform/chips-media/wave6/wave6-vpu.c | 821 +++++
.../platform/chips-media/wave6/wave6-vpu.h | 143 +
.../platform/chips-media/wave6/wave6-vpuapi.c | 343 ++
.../platform/chips-media/wave6/wave6-vpuapi.h | 1005 ++++++
.../chips-media/wave6/wave6-vpuconfig.h | 72 +
.../chips-media/wave6/wave6-vpuerror.h | 262 ++
drivers/media/v4l2-core/v4l2-common.c | 2 +-
29 files changed, 12093 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/media/nxp,imx95-vpu.yaml
create mode 100644 drivers/media/platform/chips-media/wave6/Kconfig
create mode 100644 drivers/media/platform/chips-media/wave6/Makefile
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-hw.c
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-hw.h
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-regdefine.h
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-trace.h
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vdi.h
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-core.c
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-core.h
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-dbg.c
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-dbg.h
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-dec.c
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-enc.c
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.c
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.h
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-v4l2.c
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu.c
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu.h
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpuapi.c
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpuapi.h
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpuconfig.h
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpuerror.h
--
2.31.1
^ permalink raw reply
* Re: [PATCH v3 6/7] ARM: dts: aspeed: g6: Change vuart compatible string for ast2600
From: Krzysztof Kozlowski @ 2026-06-24 7:19 UTC (permalink / raw)
To: Grégoire Layet
Cc: joel, andrew, lkundrak, devicetree, gregkh, jirislaby, robh,
krzk+dt, conor+dt, andrew, jacky_chou, yh_chung, ninad,
anirudhsriniv, linux-serial, linux-aspeed, linux-arm-kernel,
linux-kernel
In-Reply-To: <30aedaa9ffd5ba2d763d8802a07b77ef2d5bfcf0.1782224060.git.gregoire.layet@9elements.com>
On Tue, Jun 23, 2026 at 02:25:44PM +0000, Grégoire Layet wrote:
> Use the ast2600 compatible string.
> This makes it more precise and enables specific ast2600 properties.
> Still use the ast2500 compatible string as a fallback.
>
> Signed-off-by: Grégoire Layet <gregoire.layet@9elements.com>
> ---
> arch/arm/boot/dts/aspeed/aspeed-g6.dtsi | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi
> index 56bb3b0444f7..7c02633f2bd6 100644
> --- a/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi
> +++ b/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi
> @@ -707,7 +707,7 @@ emmc: sdhci@1e750100 {
> };
>
> vuart1: serial@1e787000 {
> - compatible = "aspeed,ast2500-vuart";
> + compatible = "aspeed,ast2600-vuart", "aspeed,ast2500-vuart";
Please start testing your patches. This for sure fails tests.
It does not look like you tested the DTS against bindings. Please run
'make dtbs_check W=1' (see
Documentation/devicetree/bindings/writing-schema.rst or
https://www.linaro.org/blog/tips-and-tricks-for-validating-devicetree-sources-with-the-devicetree-schema/
for instructions).
Maybe you need to update your dtschema and yamllint. Don't rely on
distro packages for dtschema and be sure you are using the latest
released dtschema.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v3 1/7] dt-bindings: serial: 8250: aspeed: add compatible string for ast2600
From: Krzysztof Kozlowski @ 2026-06-24 7:15 UTC (permalink / raw)
To: Grégoire Layet
Cc: joel, andrew, lkundrak, devicetree, gregkh, jirislaby, robh,
krzk+dt, conor+dt, andrew, jacky_chou, yh_chung, ninad,
anirudhsriniv, linux-serial, linux-aspeed, linux-arm-kernel,
linux-kernel
In-Reply-To: <80d983887dfdfc7e70a6db95f8cb95b7312f3044.1782224059.git.gregoire.layet@9elements.com>
On Tue, Jun 23, 2026 at 02:25:39PM +0000, Grégoire Layet wrote:
> The ast2600 was using the ast2500 vuart compatible string.
> This change makes it possible to have ast2600-specific properties.
>
> Signed-off-by: Grégoire Layet <gregoire.layet@9elements.com>
Do not attach (thread) your patchsets to some other threads (unrelated
or older versions). This buries them deep in the mailbox and might
interfere with applying entire sets. See also:
https://elixir.bootlin.com/linux/v6.16-rc2/source/Documentation/process/submitting-patches.rst#L830
> ---
> .../devicetree/bindings/serial/8250.yaml | 20 +++++++++++--------
> 1 file changed, 12 insertions(+), 8 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/serial/8250.yaml b/Documentation/devicetree/bindings/serial/8250.yaml
> index bb7b9c87a807..3cbd0f532e15 100644
> --- a/Documentation/devicetree/bindings/serial/8250.yaml
> +++ b/Documentation/devicetree/bindings/serial/8250.yaml
> @@ -23,7 +23,9 @@ allOf:
> then:
> properties:
> compatible:
> - const: aspeed,ast2500-vuart
> + anyOf:
This should be oneOf (by convention and actually more accurate meaning).
> + - const: aspeed,ast2500-vuart
> + - const: aspeed,ast2600-vuart
> - if:
> properties:
> compatible:
> @@ -287,17 +289,19 @@ properties:
> aspeed,sirq-polarity-sense:
> $ref: /schemas/types.yaml#/definitions/phandle-array
> description: |
> - Phandle to aspeed,ast2500-scu compatible syscon alongside register
> - offset and bit number to identify how the SIRQ polarity should be
> - configured. One possible data source is the LPC/eSPI mode bit. Only
> - applicable to aspeed,ast2500-vuart.
> + Phandle to aspeed,ast2500-scu or aspeed,ast2600-scu compatible syscon
> + alongside register offset and bit number to identify how the SIRQ
> + polarity should be configured. One possible data source is the LPC/eSPI
> + mode bit. Only applicable to aspeed,ast2500-vuart and
> + aspeed,ast2600-vuart.
> deprecated: true
>
> aspeed,lpc-io-reg:
> $ref: /schemas/types.yaml#/definitions/uint32-array
> maxItems: 1
> description: |
> - The VUART LPC address. Only applicable to aspeed,ast2500-vuart.
> + The VUART LPC address. Only applicable to aspeed,ast2500-vuart and
> + aspeed,ast2600-vuart.
>
> aspeed,lpc-interrupts:
> $ref: /schemas/types.yaml#/definitions/uint32-array
> @@ -305,8 +309,8 @@ properties:
> maxItems: 2
> description: |
> A 2-cell property describing the VUART SIRQ number and SIRQ
> - polarity (IRQ_TYPE_LEVEL_LOW or IRQ_TYPE_LEVEL_HIGH). Only
> - applicable to aspeed,ast2500-vuart.
> + polarity (IRQ_TYPE_LEVEL_LOW or IRQ_TYPE_LEVEL_HIGH). Only
> + applicable to aspeed,ast2500-vuart and aspeed,ast2600-vuart.
>
More important, where is documenting of the actual compatible?
Best regards,
Krzysztof
^ permalink raw reply
* RE: [PATCH V2 1/8] PCI: imx6: Add skip_pwrctrl_off flag support
From: Sherry Sun @ 2026-06-24 7:09 UTC (permalink / raw)
To: Frank Li (OSS), Sherry Sun (OSS)
Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
Frank Li, s.hauer@pengutronix.de, kernel@pengutronix.de,
festevam@gmail.com, Amitkumar Karwar, Neeraj Sanjay Kale,
marcel@holtmann.org, luiz.dentz@gmail.com, Hongxing Zhu,
l.stach@pengutronix.de, lpieralisi@kernel.org,
kwilczynski@kernel.org, mani@kernel.org, bhelgaas@google.com,
brgl@kernel.org, imx@lists.linux.dev, linux-pci@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-bluetooth@vger.kernel.org,
linux-pm@vger.kernel.org
In-Reply-To: <ajqZBM6IkbDLiVu2@SMW015318>
> Subject: Re: [PATCH V2 1/8] PCI: imx6: Add skip_pwrctrl_off flag support
>
> On Tue, Jun 23, 2026 at 11:07:28AM +0800, Sherry Sun (OSS) wrote:
> > From: Sherry Sun <sherry.sun@nxp.com>
> >
> > Use dw_pcie_rp::skip_pwrctrl_off to avoid powering off devices during
> > suspend to preserve wakeup capability of the devices and also not to
> > power on the devices in the init path.
> > This allows controller power-off to be skipped when some devices(e.g.
> > M.2 cards key E without auxiliary power) required to support PCIe L2
> > link state and wake-up mechanisms.
> >
> > Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
> > ---
> > drivers/pci/controller/dwc/pci-imx6.c | 36
> > +++++++++++++++++----------
> > 1 file changed, 23 insertions(+), 13 deletions(-)
> >
> > diff --git a/drivers/pci/controller/dwc/pci-imx6.c
> > b/drivers/pci/controller/dwc/pci-imx6.c
> > index 0fa716d1ed75..ff5a9565dbbf 100644
> > --- a/drivers/pci/controller/dwc/pci-imx6.c
> > +++ b/drivers/pci/controller/dwc/pci-imx6.c
> > @@ -1382,16 +1382,20 @@ static int imx_pcie_host_init(struct dw_pcie_rp
> *pp)
> > }
> > }
> >
> > - ret = pci_pwrctrl_create_devices(dev);
> > - if (ret) {
> > - dev_err(dev, "failed to create pwrctrl devices\n");
> > - goto err_reg_disable;
> > + if (!pci->suspended) {
> > + ret = pci_pwrctrl_create_devices(dev);
>
> Is possible move pci_pwrctrl_create_devices() of pci_pwrctrl_create_devices
>
> and call it direct at probe() function, like other regulator_get function.
>
Hi Frank,
That makes sense. However, if we move pci_pwrctrl_create_devices () to
probe(), we may need to add the following goto err_pwrctrl_destroy path
in imx_pcie_probe() to properly handle errors from
pci_pwrctrl_power_on_devices(), is that acceptable?
@@ -1960,11 +1949,15 @@ static int imx_pcie_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ret = pci_pwrctrl_create_devices(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to create pwrctrl devices\n");
+
pci->use_parent_dt_ranges = true;
if (imx_pcie->drvdata->mode == DW_PCIE_EP_TYPE) {
ret = imx_add_pcie_ep(imx_pcie, pdev);
if (ret < 0)
- return ret;
+ goto err_pwrctrl_destroy;
/*
* FIXME: Only single Device (EPF) is supported due to the
@@ -1979,7 +1972,7 @@ static int imx_pcie_probe(struct platform_device *pdev)
pci->pp.use_atu_msg = true;
ret = dw_pcie_host_init(&pci->pp);
if (ret < 0)
- return ret;
+ goto err_pwrctrl_destroy;
if (pci_msi_enabled()) {
u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_MSI);
@@ -1991,6 +1984,11 @@ static int imx_pcie_probe(struct platform_device *pdev)
}
return 0;
+
+err_pwrctrl_destroy:
+ if (ret != -EPROBE_DEFER)
+ pci_pwrctrl_destroy_devices(dev);
+ return ret;
}
Best Regards
Sherry
>
> > + if (ret) {
> > + dev_err(dev, "failed to create pwrctrl devices\n");
> > + goto err_reg_disable;
> > + }
> > }
> >
> > - ret = pci_pwrctrl_power_on_devices(dev);
> > - if (ret) {
> > - dev_err(dev, "failed to power on pwrctrl devices\n");
> > - goto err_pwrctrl_destroy;
> > + if (!pp->skip_pwrctrl_off) {
> > + ret = pci_pwrctrl_power_on_devices(dev);
> > + if (ret) {
> > + dev_err(dev, "failed to power on pwrctrl devices\n");
> > + goto err_pwrctrl_destroy;
> > + }
> > }
> >
> > ret = imx_pcie_clk_enable(imx_pcie); @@ -1460,9 +1464,10 @@
> static
> > int imx_pcie_host_init(struct dw_pcie_rp *pp)
> > err_clk_disable:
> > imx_pcie_clk_disable(imx_pcie);
> > err_pwrctrl_power_off:
> > - pci_pwrctrl_power_off_devices(dev);
> > + if (!pp->skip_pwrctrl_off)
> > + pci_pwrctrl_power_off_devices(dev);
> > err_pwrctrl_destroy:
> > - if (ret != -EPROBE_DEFER)
> > + if (ret != -EPROBE_DEFER && !pci->suspended)
> > pci_pwrctrl_destroy_devices(dev);
> > err_reg_disable:
> > if (imx_pcie->vpcie)
> > @@ -1482,7 +1487,8 @@ static void imx_pcie_host_exit(struct dw_pcie_rp
> *pp)
> > }
> > imx_pcie_clk_disable(imx_pcie);
> >
> > - pci_pwrctrl_power_off_devices(pci->dev);
> > + if (!pci->pp.skip_pwrctrl_off)
> > + pci_pwrctrl_power_off_devices(pci->dev);
> > if (imx_pcie->vpcie)
> > regulator_disable(imx_pcie->vpcie);
> > }
> > @@ -1990,12 +1996,16 @@ static int imx_pcie_probe(struct
> > platform_device *pdev) static void imx_pcie_shutdown(struct
> > platform_device *pdev) {
> > struct imx_pcie *imx_pcie = platform_get_drvdata(pdev);
> > + struct dw_pcie *pci = imx_pcie->pci;
> > + struct dw_pcie_rp *pp = &pci->pp;
> >
> > /* bring down link, so bootloader gets clean state in case of reboot */
> > imx_pcie_assert_core_reset(imx_pcie);
> > imx_pcie_assert_perst(imx_pcie, true);
> > - pci_pwrctrl_power_off_devices(&pdev->dev);
> > - pci_pwrctrl_destroy_devices(&pdev->dev);
> > + if (!pp->skip_pwrctrl_off)
> > + pci_pwrctrl_power_off_devices(&pdev->dev);
> > + if (!pci->suspended)
> > + pci_pwrctrl_destroy_devices(&pdev->dev);
> > }
> >
> > static const struct imx_pcie_drvdata drvdata[] = {
> > --
> > 2.50.1
> >
> >
^ permalink raw reply
* Re: [RFC PATCH] irqchip/gic-v3-its: enable dynamic MSI-X allocation
From: Marc Zyngier @ 2026-06-24 7:07 UTC (permalink / raw)
To: Jinqian Yang
Cc: lpieralisi, tglx, alex, linux-kernel, linux-arm-kernel,
liuyonglong, wangzhou1, linuxarm
In-Reply-To: <20260624025345.458387-1-yangjinqian1@huawei.com>
On Wed, 24 Jun 2026 03:53:45 +0100,
Jinqian Yang <yangjinqian1@huawei.com> wrote:
>
> On ARM64 platforms with GICv3 ITS, VFIO PCI passthrough currently
> cannot dynamically allocate MSI-X vectors after MSI-X has been
> enabled. When QEMU needs to extend the vector range, it must
> disable MSI-X, free all interrupts, then re-enable with a larger
> allocation. This creates an interrupt loss window for already-active
> vectors.
>
> Consider HNS3 with RoCE: NIC and RDMA share one PCI device and
> ITS DeviceID, with MSI-X vectors partitioned as NIC (lower range)
> then RoCE (starting at base_vector = num_nic_msi). In VFIO
> passthrough, loading hns_roce after hns3 forces QEMU to tear down
> all interrupts before re-allocating the larger range. During this
> process, NIC interrupts may be lost. Testing confirmed that this
> occasionally occurs, causing the network port reset to fail.
Well, that's what you get for not exposing differentiated functions.
Eventually, you face the reality that this is a poor design.
>
> ITS_MSI_FLAGS_SUPPORTED lacks MSI_FLAG_PCI_MSIX_ALLOC_DYN, causing
> pci_msix_can_alloc_dyn() to return false. VFIO then sets
> has_dyn_msix=false and never clears VFIO_IRQ_INFO_NORESIZE for
> MSI-X, keeping the old "disable and reallocate" behavior.
>
> The essential prerequisite for enabling this flag is the fix to
> msi_prepare() call timing (commit 1396e89e09f0 ("genirq/msi: Move
> prepare() call to per-device allocation")): msi_prepare() is
> now called once at per-device domain creation with hwsize, so ITS
> creates an ITT with sufficient capacity for all MSI-X vectors.
> Without this fix, msi_prepare() was called per-allocation with
> semi-random nvec, maybe resulting in an ITT too small for dynamic
> vector addition.
How is this paragraph relevant? The kernel has had this fix for over a
year, and backporting this series is not something I plan to ever do.
>
> With this in place, dynamic MSI-X allocation works correctly:
> msi_domain_alloc_irq_at() uses populate_alloc_info() to copy the
> pre-prepared alloc_data without re-invoking msi_prepare(), so each
> new vector simply gets a LPI entry in the already-allocated ITT,
> without affecting existing vectors.
>
> Signed-off-by: Jinqian Yang <yangjinqian1@huawei.com>
> ---
> drivers/irqchip/irq-gic-its-msi-parent.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c
> index b9257103a999..b2b9d2068bb1 100644
> --- a/drivers/irqchip/irq-gic-its-msi-parent.c
> +++ b/drivers/irqchip/irq-gic-its-msi-parent.c
> @@ -18,7 +18,8 @@
>
> #define ITS_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \
> MSI_FLAG_PCI_MSIX | \
> - MSI_FLAG_MULTI_PCI_MSI)
> + MSI_FLAG_MULTI_PCI_MSI | \
> + MSI_FLAG_PCI_MSIX_ALLOC_DYN)
>
> static int its_translate_frame_address(struct fwnode_handle *msi_node, phys_addr_t *pa)
> {
What has this been tested with? In which conditions?
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply
* Re: [PATCH v6 17/21] RISC-V: perf: Add Qemu virt machine events
From: Atish Patra @ 2026-06-24 7:00 UTC (permalink / raw)
To: Charlie Jenkins
Cc: James Clark, Rob Herring, Arnaldo Carvalho de Melo, Jiri Olsa,
Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers, linux-riscv,
linux-kernel, linux-perf-users, Conor Dooley, devicetree,
linux-arm-kernel
In-Reply-To: <ajjZ5NljehUXERN1@blinky>
On 6/21/26 11:44 PM, Charlie Jenkins wrote:
> On Mon, Jun 08, 2026 at 11:01:31PM -0700, Atish Patra wrote:
>> From: Atish Patra <atishp@rivosinc.com>
>>
>> Qemu virt machine supports a very minimal set of legacy perf events.
>> Add them to the vendor table so that users can use them when
>> counter delegation is enabled.
>>
>> Signed-off-by: Atish Patra <atishp@rivosinc.com>
>> ---
>> arch/riscv/include/asm/vendorid_list.h | 4 ++++
>> drivers/perf/riscv_pmu_sbi.c | 36 ++++++++++++++++++++++++++++++++++
>> 2 files changed, 40 insertions(+)
>>
>> diff --git a/arch/riscv/include/asm/vendorid_list.h b/arch/riscv/include/asm/vendorid_list.h
>> index 7f5030ee1fcf..603aa2b21c0b 100644
>> --- a/arch/riscv/include/asm/vendorid_list.h
>> +++ b/arch/riscv/include/asm/vendorid_list.h
>> @@ -11,4 +11,8 @@
>> #define SIFIVE_VENDOR_ID 0x489
>> #define THEAD_VENDOR_ID 0x5b7
>>
>> +#define QEMU_VIRT_VENDOR_ID 0x000
>> +#define QEMU_VIRT_IMPL_ID 0x000
>> +#define QEMU_VIRT_ARCH_ID 0x000
> Palmer proposed a change to this a while ago to set the archid for qemu
> as 42 but it looks like it was never merged in qemu, but it was merged
> into the riscv spec.
>
> Here is the spec PR: https://github.com/riscv/riscv-isa-manual/pull/1213
> Here is the current spec: https://github.com/riscv/riscv-isa-manual/blob/main/marchid.md
> Here is the QEMU patch: https://lore.kernel.org/all/20240131182430.20174-1-palmer@rivosinc.com/
>
> Should we follow up with this/maybe this should be accounted for here as
> an alternate id?
Ahh yes. I remember that thread now. Thanks for digging this.
Yes. We should resurrect that patch and use that archid as an alternate ID.
> - Charlie
>
>> +
>> #endif
>> diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
>> index 00b84b28117a..74acac54328e 100644
>> --- a/drivers/perf/riscv_pmu_sbi.c
>> +++ b/drivers/perf/riscv_pmu_sbi.c
>> @@ -26,6 +26,7 @@
>> #include <asm/sbi.h>
>> #include <asm/cpufeature.h>
>> #include <asm/vendor_extensions.h>
>> +#include <asm/vendorid_list.h>
>> #include <asm/vendor_extensions/andes.h>
>> #include <asm/hwcap.h>
>> #include <asm/csr_ind.h>
>> @@ -453,7 +454,42 @@ struct riscv_vendor_pmu_events {
>> .hw_event_map = _hw_event_map, .cache_event_map = _cache_event_map, \
>> .attrs_events = _attrs },
>>
>> +/* QEMU virt PMU events */
>> +static const struct riscv_pmu_event qemu_virt_hw_event_map[PERF_COUNT_HW_MAX] = {
>> + PERF_MAP_ALL_UNSUPPORTED,
>> + [PERF_COUNT_HW_CPU_CYCLES] = {0x01, 0xFFFFFFF8},
>> + [PERF_COUNT_HW_INSTRUCTIONS] = {0x02, 0xFFFFFFF8}
>> +};
>> +
>> +static const struct riscv_pmu_event qemu_virt_cache_event_map[PERF_COUNT_HW_CACHE_MAX]
>> + [PERF_COUNT_HW_CACHE_OP_MAX]
>> + [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
>> + PERF_CACHE_MAP_ALL_UNSUPPORTED,
>> + [C(DTLB)][C(OP_READ)][C(RESULT_MISS)] = {0x10019, 0xFFFFFFF8},
>> + [C(DTLB)][C(OP_WRITE)][C(RESULT_MISS)] = {0x1001B, 0xFFFFFFF8},
>> +
>> + [C(ITLB)][C(OP_READ)][C(RESULT_MISS)] = {0x10021, 0xFFFFFFF8},
>> +};
>> +
>> +RVPMU_EVENT_CMASK_ATTR(cycles, cycles, 0x01, 0xFFFFFFF8);
>> +RVPMU_EVENT_CMASK_ATTR(instructions, instructions, 0x02, 0xFFFFFFF8);
>> +RVPMU_EVENT_CMASK_ATTR(dTLB-load-misses, dTLB_load_miss, 0x10019, 0xFFFFFFF8);
>> +RVPMU_EVENT_CMASK_ATTR(dTLB-store-misses, dTLB_store_miss, 0x1001B, 0xFFFFFFF8);
>> +RVPMU_EVENT_CMASK_ATTR(iTLB-load-misses, iTLB_load_miss, 0x10021, 0xFFFFFFF8);
>> +
>> +static struct attribute *qemu_virt_event_group[] = {
>> + RVPMU_EVENT_ATTR_PTR(cycles),
>> + RVPMU_EVENT_ATTR_PTR(instructions),
>> + RVPMU_EVENT_ATTR_PTR(dTLB_load_miss),
>> + RVPMU_EVENT_ATTR_PTR(dTLB_store_miss),
>> + RVPMU_EVENT_ATTR_PTR(iTLB_load_miss),
>> + NULL,
>> +};
>> +
>> static struct riscv_vendor_pmu_events pmu_vendor_events_table[] = {
>> + RISCV_VENDOR_PMU_EVENTS(QEMU_VIRT_VENDOR_ID, QEMU_VIRT_ARCH_ID, QEMU_VIRT_IMPL_ID,
>> + qemu_virt_hw_event_map, qemu_virt_cache_event_map,
>> + qemu_virt_event_group)
>> };
>>
>> static const struct riscv_pmu_event *current_pmu_hw_event_map;
>>
>> --
>> 2.53.0-Meta
>>
>>
^ permalink raw reply
* [PATCH] media: meson: vdec: fix NULL pointer deref in vdec_try_fmt_common
From: Christian Hewitt @ 2026-06-24 6:58 UTC (permalink / raw)
To: Neil Armstrong, Greg Kroah-Hartman, Kevin Hilman, Jerome Brunet,
Martin Blumenstingl, linux-media, linux-amlogic, linux-staging,
linux-arm-kernel, linux-kernel
When VIDIOC_TRY_FMT is called with an unsupported pixel format on the
OUTPUT queue, vdec_try_fmt_common() falls back to V4L2_PIX_FMT_MPEG2.
However, if a distro has locally patched MPEG2 support out (as it has
been broken for some time) the platform format table does not contain
MPEG2 so find_format() returns NULL and the subsequent dereference of
fmt_out->max_width triggers a NULL pointer dereference.
Fix this by falling back to the first format in the platform's format
array instead of hardcoding V4L2_PIX_FMT_MPEG2. This is always valid
since every platform defines at least one format.
Fixes: 3e7f51bd9607 ("media: meson: add v4l2 m2m video decoder driver")
Signed-off-by: Christian Hewitt <christianshewitt@gmail.com>
---
This fixes use of v4l2-ctl and v4l2-compliance tools when MPEG2 support
has been disabled via vdec_platform.c - NB: although this is a fix it's
also a rather niche scenario so there's no need to backport it to older
stable kernels.
drivers/staging/media/meson/vdec/vdec.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 4b77ec1af5a7..f3e7072f221a 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -504,8 +504,8 @@ vdec_try_fmt_common(struct amvdec_session *sess, u32 size,
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
fmt_out = find_format(fmts, size, pixmp->pixelformat);
if (!fmt_out) {
- pixmp->pixelformat = V4L2_PIX_FMT_MPEG2;
- fmt_out = find_format(fmts, size, pixmp->pixelformat);
+ pixmp->pixelformat = fmts[0].pixfmt;
+ fmt_out = &fmts[0];
}
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v6 04/21] RISC-V: Define indirect CSR access helpers
From: Atish Patra @ 2026-06-24 6:55 UTC (permalink / raw)
To: Charlie Jenkins
Cc: James Clark, Rob Herring, Arnaldo Carvalho de Melo, Jiri Olsa,
Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers, linux-riscv,
linux-kernel, linux-perf-users, Conor Dooley, devicetree,
linux-arm-kernel
In-Reply-To: <ajjZR-R11yPYWuDp@blinky>
On 6/21/26 11:42 PM, Charlie Jenkins wrote:
> On Mon, Jun 08, 2026 at 11:01:18PM -0700, Atish Patra wrote:
>> From: Atish Patra <atishp@rivosinc.com>
>>
>> The indriect CSR requires multiple instructions to read/write CSR.
> indirect
>
>> Add a few helper functions for ease of usage.
>>
>> Signed-off-by: Atish Patra <atishp@rivosinc.com>
>> ---
>> arch/riscv/include/asm/csr_ind.h | 44 ++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 44 insertions(+)
>>
>> diff --git a/arch/riscv/include/asm/csr_ind.h b/arch/riscv/include/asm/csr_ind.h
>> new file mode 100644
>> index 000000000000..6fd7d44dc640
>> --- /dev/null
>> +++ b/arch/riscv/include/asm/csr_ind.h
>> @@ -0,0 +1,44 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Copyright (C) 2024 Rivos Inc.
> I don't think it makes sense to introduce this copyright in new commits.
Yeah. I will update these.
> - Charlie
>
>> + */
>> +
>> +#ifndef _ASM_RISCV_CSR_IND_H
>> +#define _ASM_RISCV_CSR_IND_H
>> +
>> +#include <linux/irqflags.h>
>> +
>> +#include <asm/csr.h>
>> +
>> +#define csr_ind_read(iregcsr, iselbase, iseloff) ({ \
>> + unsigned long __value = 0; \
>> + unsigned long __flags; \
>> + local_irq_save(__flags); \
>> + csr_write(CSR_ISELECT, (iselbase) + (iseloff)); \
>> + __value = csr_read(iregcsr); \
>> + local_irq_restore(__flags); \
>> + __value; \
>> +})
>> +
>> +#define csr_ind_write(iregcsr, iselbase, iseloff, value) ({ \
>> + unsigned long __flags; \
>> + local_irq_save(__flags); \
>> + csr_write(CSR_ISELECT, (iselbase) + (iseloff)); \
>> + csr_write(iregcsr, (value)); \
>> + local_irq_restore(__flags); \
>> +})
>> +
>> +#define csr_ind_warl(iregcsr, iselbase, iseloff, warl_val) ({ \
>> + unsigned long __old_val = 0, __value = 0; \
>> + unsigned long __flags; \
>> + local_irq_save(__flags); \
>> + csr_write(CSR_ISELECT, (iselbase) + (iseloff)); \
>> + __old_val = csr_read(iregcsr); \
>> + csr_write(iregcsr, (warl_val)); \
>> + __value = csr_read(iregcsr); \
>> + csr_write(iregcsr, __old_val); \
>> + local_irq_restore(__flags); \
>> + __value; \
>> +})
>> +
>> +#endif
>>
>> --
>> 2.53.0-Meta
>>
>>
>> _______________________________________________
>> linux-riscv mailing list
>> linux-riscv@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-riscv
>>
^ permalink raw reply
* Re: [PATCH v5 4/6] dt-bindings: input: sun4i-lradc-keys: Add A100/A133 compatible
From: Krzysztof Kozlowski @ 2026-06-24 6:51 UTC (permalink / raw)
To: Alexander Sverdlin
Cc: linux-arm-kernel, linux-sunxi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Hans de Goede, Dmitry Torokhov, Andre Przywara, Jun Yan,
Lukas Schmid, J. Neuschäfer, Eric Biggers, Michal Simek,
Luca Weiss, Sven Peter, Maxime Ripard, devicetree, linux-kernel,
linux-input
In-Reply-To: <20260623204824.691832-5-alexander.sverdlin@gmail.com>
On Tue, Jun 23, 2026 at 10:48:16PM +0200, Alexander Sverdlin wrote:
> The Allwinner A100/A133 SoCs have an LRADC which is compatible with the
> versions in existing SoCs. Add a compatible string for A100, with the R329
> fallback.
>
> Signed-off-by: Alexander Sverdlin <alexander.sverdlin@gmail.com>
> ---
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v6 1/4] dt-bindings: soc: cix: add sky1 audss cru controller
From: Krzysztof Kozlowski @ 2026-06-24 6:49 UTC (permalink / raw)
To: joakim.zhang
Cc: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
gary.yang, cix-kernel-upstream, linux-clk, devicetree,
linux-kernel, linux-arm-kernel
In-Reply-To: <20260623070805.211019-2-joakim.zhang@cixtech.com>
On Tue, Jun 23, 2026 at 03:08:02PM +0800, joakim.zhang@cixtech.com wrote:
> From: Joakim Zhang <joakim.zhang@cixtech.com>
>
> The Cix Sky1 Audio Subsystem (AUDSS) Clock and Reset Unit (CRU)
> groups clock muxing, gating and block-level software reset control
> in a single register block.
>
> Signed-off-by: Joakim Zhang <joakim.zhang@cixtech.com>
> ---
> .../bindings/soc/cix/cix,sky1-audss-cru.yaml | 92 +++++++++++++++++++
> .../dt-bindings/clock/cix,sky1-audss-cru.h | 60 ++++++++++++
> .../dt-bindings/reset/cix,sky1-audss-cru.h | 25 +++++
> 3 files changed, 177 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml
> create mode 100644 include/dt-bindings/clock/cix,sky1-audss-cru.h
> create mode 100644 include/dt-bindings/reset/cix,sky1-audss-cru.h
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply
* [linux-next:master] BUILD REGRESSION 4e5dfb7c84012007c3c7061126491bbc92d71bf1
From: kernel test robot @ 2026-06-24 6:42 UTC (permalink / raw)
To: Andrew Morton
Cc: Linux Memory Management List, amd-gfx, imx, linux-arm-kernel,
Mark Brown
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git master
branch HEAD: 4e5dfb7c84012007c3c7061126491bbc92d71bf1 Add linux-next specific files for 20260623
Error/Warning (recently discovered and may have been fixed):
https://lore.kernel.org/oe-kbuild-all/202606240749.ZFFiISB2-lkp@intel.com
https://lore.kernel.org/oe-kbuild-all/202606240753.kYjobJVl-lkp@intel.com
https://lore.kernel.org/oe-kbuild-all/202606241110.iUga5vVw-lkp@intel.com
https://lore.kernel.org/oe-kbuild-all/202606241320.9gDd2s75-lkp@intel.com
https://lore.kernel.org/oe-kbuild-all/202606241431.L9VUbAKp-lkp@intel.com
drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/tests/amdgpu_dm_connector_test.c:120:1: warning: the frame size of 1384 bytes is larger than 1280 bytes [-Wframe-larger-than=]
drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/tests/amdgpu_dm_connector_test.c:120:1: warning: the frame size of 1388 bytes is larger than 1280 bytes [-Wframe-larger-than=]
drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/tests/amdgpu_dm_connector_test.c:120:1: warning: the frame size of 1424 bytes is larger than 1280 bytes [-Wframe-larger-than=]
drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/tests/amdgpu_dm_connector_test.c:120:1: warning: the frame size of 1696 bytes is larger than 1280 bytes [-Wframe-larger-than=]
drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/tests/amdgpu_dm_connector_test.c:120:1: warning: the frame size of 1696 bytes is larger than 1536 bytes [-Wframe-larger-than=]
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.o: error: objtool: amdgpu_vm_handle_fault+0x97: sibling call from callable instruction with modified stack frame
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.o: warning: objtool: amdgpu_vm_handle_fault+0x12d: sibling call from callable instruction with modified stack frame
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.o: warning: objtool: amdgpu_vm_handle_fault+0x140: sibling call from callable instruction with modified stack frame
Error/Warning ids grouped by kconfigs:
recent_errors
|-- arc-allmodconfig
| `-- drivers-gpu-drm-amd-amdgpu-..-display-amdgpu_dm-tests-amdgpu_dm_connector_test.c:warning:the-frame-size-of-bytes-is-larger-than-bytes
|-- arc-allyesconfig
| `-- drivers-gpu-drm-amd-amdgpu-..-display-amdgpu_dm-tests-amdgpu_dm_connector_test.c:warning:the-frame-size-of-bytes-is-larger-than-bytes
|-- arm-allyesconfig
| `-- drivers-gpu-drm-amd-amdgpu-..-display-amdgpu_dm-tests-amdgpu_dm_connector_test.c:warning:the-frame-size-of-bytes-is-larger-than-bytes
|-- csky-allmodconfig
| `-- drivers-gpu-drm-amd-amdgpu-..-display-amdgpu_dm-tests-amdgpu_dm_connector_test.c:warning:the-frame-size-of-bytes-is-larger-than-bytes
|-- i386-allmodconfig
| `-- drivers-gpu-drm-amd-amdgpu-..-display-amdgpu_dm-tests-amdgpu_dm_connector_test.c:warning:the-frame-size-of-bytes-is-larger-than-bytes
|-- i386-allyesconfig
| `-- drivers-gpu-drm-amd-amdgpu-..-display-amdgpu_dm-tests-amdgpu_dm_connector_test.c:warning:the-frame-size-of-bytes-is-larger-than-bytes
|-- loongarch-defconfig
| `-- drivers-gpu-drm-amd-amdgpu-amdgpu_vm.o:warning:objtool:amdgpu_vm_handle_fault:sibling-call-from-callable-instruction-with-modified-stack-frame
|-- microblaze-allyesconfig
| `-- drivers-gpu-drm-amd-amdgpu-..-display-amdgpu_dm-tests-amdgpu_dm_connector_test.c:warning:the-frame-size-of-bytes-is-larger-than-bytes
|-- mips-allmodconfig
| `-- drivers-gpu-drm-amd-amdgpu-..-display-amdgpu_dm-tests-amdgpu_dm_connector_test.c:warning:the-frame-size-of-bytes-is-larger-than-bytes
|-- mips-allyesconfig
| `-- drivers-gpu-drm-amd-amdgpu-..-display-amdgpu_dm-tests-amdgpu_dm_connector_test.c:warning:the-frame-size-of-bytes-is-larger-than-bytes
|-- openrisc-allmodconfig
| `-- drivers-gpu-drm-amd-amdgpu-..-display-amdgpu_dm-tests-amdgpu_dm_connector_test.c:warning:the-frame-size-of-bytes-is-larger-than-bytes
|-- powerpc64-randconfig-r052-20260624
| `-- drivers-firmware-imx-se_ctrl.c:WARNING:invalid-free-of-devm_-allocated-data
|-- sparc-randconfig-r133-20260624
| `-- drivers-gpu-drm-amd-amdgpu-..-display-amdgpu_dm-tests-..-..-..-amdgpu-amdgv_sriovmsg.h:sparse:sparse:static-assertion-failed:amd_sriov_msg_vf2pf_info-must-be-KB
|-- x86_64-randconfig-003-20260622
| `-- drivers-gpu-drm-amd-amdgpu-amdgpu_vm.o:warning:objtool:amdgpu_vm_handle_fault:sibling-call-from-callable-instruction-with-modified-stack-frame
|-- x86_64-randconfig-072-20250919
| `-- drivers-gpu-drm-amd-amdgpu-amdgpu_vm.o:error:objtool:amdgpu_vm_handle_fault:sibling-call-from-callable-instruction-with-modified-stack-frame
`-- xtensa-allyesconfig
`-- drivers-gpu-drm-amd-amdgpu-..-display-amdgpu_dm-tests-amdgpu_dm_connector_test.c:warning:the-frame-size-of-bytes-is-larger-than-bytes
elapsed time: 941m
configs tested: 278
configs skipped: 4
tested configs:
alpha allnoconfig gcc-16.1.0
alpha allyesconfig gcc-16.1.0
alpha defconfig gcc-16.1.0
arc allmodconfig clang-23
arc allmodconfig gcc-16.1.0
arc allnoconfig gcc-16.1.0
arc allyesconfig clang-23
arc allyesconfig gcc-16.1.0
arc defconfig gcc-16.1.0
arc randconfig-001-20260624 gcc-15.2.0
arc randconfig-001-20260624 gcc-9.5.0
arc randconfig-002-20260624 gcc-15.2.0
arm allnoconfig clang-17
arm allnoconfig gcc-16.1.0
arm allyesconfig clang-23
arm allyesconfig gcc-16.1.0
arm defconfig clang-23
arm defconfig gcc-16.1.0
arm randconfig-001-20260624 clang-19
arm randconfig-001-20260624 gcc-15.2.0
arm randconfig-002-20260624 gcc-15.2.0
arm randconfig-002-20260624 gcc-8.5.0
arm randconfig-003-20260624 gcc-15.2.0
arm randconfig-004-20260624 clang-17
arm randconfig-004-20260624 gcc-15.2.0
arm sama5_defconfig gcc-16.1.0
arm64 allmodconfig clang-23
arm64 allnoconfig gcc-16.1.0
arm64 defconfig gcc-16.1.0
arm64 randconfig-001-20260624 clang-21
arm64 randconfig-002-20260624 clang-21
arm64 randconfig-002-20260624 gcc-15.2.0
arm64 randconfig-003-20260624 clang-21
arm64 randconfig-003-20260624 clang-23
arm64 randconfig-004-20260624 clang-21
arm64 randconfig-004-20260624 clang-23
csky allmodconfig gcc-16.1.0
csky allnoconfig gcc-16.1.0
csky defconfig gcc-16.1.0
csky randconfig-001-20260624 clang-21
csky randconfig-001-20260624 gcc-16.1.0
csky randconfig-002-20260624 clang-21
csky randconfig-002-20260624 gcc-9.5.0
hexagon allmodconfig clang-23
hexagon allmodconfig gcc-16.1.0
hexagon allnoconfig clang-23
hexagon allnoconfig gcc-16.1.0
hexagon defconfig clang-23
hexagon defconfig gcc-16.1.0
hexagon randconfig-001 gcc-11.5.0
hexagon randconfig-001-20260624 clang-20
hexagon randconfig-001-20260624 gcc-11.5.0
hexagon randconfig-002 gcc-11.5.0
hexagon randconfig-002-20260624 clang-23
hexagon randconfig-002-20260624 gcc-11.5.0
i386 allmodconfig clang-22
i386 allnoconfig gcc-14
i386 allnoconfig gcc-16.1.0
i386 allyesconfig clang-22
i386 allyesconfig gcc-14
i386 buildonly-randconfig-001 gcc-12
i386 buildonly-randconfig-001-20260624 gcc-12
i386 buildonly-randconfig-001-20260624 gcc-14
i386 buildonly-randconfig-002 gcc-12
i386 buildonly-randconfig-002-20260624 gcc-12
i386 buildonly-randconfig-002-20260624 gcc-14
i386 buildonly-randconfig-003 gcc-12
i386 buildonly-randconfig-003-20260624 gcc-12
i386 buildonly-randconfig-004 gcc-12
i386 buildonly-randconfig-004-20260624 gcc-12
i386 buildonly-randconfig-004-20260624 gcc-14
i386 buildonly-randconfig-005 gcc-12
i386 buildonly-randconfig-005-20260624 gcc-12
i386 buildonly-randconfig-005-20260624 gcc-14
i386 buildonly-randconfig-006 gcc-12
i386 buildonly-randconfig-006-20260624 gcc-12
i386 buildonly-randconfig-006-20260624 gcc-14
i386 defconfig clang-22
i386 defconfig gcc-16.1.0
i386 randconfig-001-20260624 clang-22
i386 randconfig-001-20260624 gcc-14
i386 randconfig-002-20260624 clang-22
i386 randconfig-002-20260624 gcc-14
i386 randconfig-003-20260624 clang-22
i386 randconfig-003-20260624 gcc-14
i386 randconfig-004-20260624 clang-22
i386 randconfig-004-20260624 gcc-12
i386 randconfig-005-20260624 clang-22
i386 randconfig-006-20260624 clang-22
i386 randconfig-007-20260624 clang-22
i386 randconfig-007-20260624 gcc-14
i386 randconfig-011-20260624 clang-22
i386 randconfig-012-20260624 clang-22
i386 randconfig-012-20260624 gcc-14
i386 randconfig-013-20260624 clang-22
i386 randconfig-014-20260624 clang-22
i386 randconfig-015-20260624 clang-22
i386 randconfig-015-20260624 gcc-14
i386 randconfig-016-20260624 clang-22
i386 randconfig-017-20260624 clang-22
loongarch allmodconfig clang-19
loongarch allmodconfig clang-23
loongarch allnoconfig clang-20
loongarch allnoconfig gcc-16.1.0
loongarch defconfig clang-23
loongarch randconfig-001 gcc-11.5.0
loongarch randconfig-001-20260624 gcc-11.5.0
loongarch randconfig-001-20260624 gcc-16.1.0
loongarch randconfig-002 gcc-11.5.0
loongarch randconfig-002-20260624 gcc-11.5.0
loongarch randconfig-002-20260624 gcc-15.2.0
m68k allmodconfig gcc-16.1.0
m68k allnoconfig gcc-16.1.0
m68k allyesconfig clang-23
m68k allyesconfig gcc-16.1.0
m68k defconfig clang-23
m68k defconfig gcc-16.1.0
microblaze allnoconfig gcc-16.1.0
microblaze allyesconfig gcc-16.1.0
microblaze defconfig clang-23
microblaze defconfig gcc-16.1.0
mips allmodconfig gcc-16.1.0
mips allnoconfig gcc-16.1.0
mips allyesconfig gcc-16.1.0
nios2 allmodconfig clang-20
nios2 allmodconfig gcc-11.5.0
nios2 allnoconfig clang-23
nios2 allnoconfig gcc-11.5.0
nios2 defconfig clang-23
nios2 defconfig gcc-11.5.0
nios2 randconfig-001 gcc-11.5.0
nios2 randconfig-001-20260624 gcc-11.5.0
nios2 randconfig-001-20260624 gcc-9.5.0
nios2 randconfig-002 gcc-11.5.0
nios2 randconfig-002-20260624 gcc-11.5.0
openrisc allmodconfig clang-20
openrisc allmodconfig gcc-16.1.0
openrisc allnoconfig clang-23
openrisc allnoconfig gcc-16.1.0
openrisc defconfig gcc-16.1.0
parisc allmodconfig gcc-16.1.0
parisc allnoconfig clang-23
parisc allnoconfig gcc-16.1.0
parisc allyesconfig clang-17
parisc allyesconfig gcc-16.1.0
parisc defconfig gcc-16.1.0
parisc randconfig-001-20260624 gcc-16.1.0
parisc randconfig-002-20260624 gcc-14.3.0
parisc randconfig-002-20260624 gcc-16.1.0
parisc64 defconfig clang-23
parisc64 defconfig gcc-16.1.0
powerpc allmodconfig gcc-16.1.0
powerpc allnoconfig clang-23
powerpc allnoconfig gcc-16.1.0
powerpc asp8347_defconfig clang-23
powerpc randconfig-001-20260624 clang-17
powerpc randconfig-001-20260624 gcc-16.1.0
powerpc randconfig-002-20260624 gcc-16.1.0
powerpc randconfig-002-20260624 gcc-8.5.0
powerpc64 randconfig-001-20260624 gcc-16.1.0
powerpc64 randconfig-001-20260624 gcc-8.5.0
powerpc64 randconfig-002-20260624 gcc-11.5.0
powerpc64 randconfig-002-20260624 gcc-16.1.0
riscv allmodconfig clang-23
riscv allnoconfig clang-23
riscv allnoconfig gcc-16.1.0
riscv allyesconfig clang-23
riscv defconfig clang-23
riscv defconfig gcc-16.1.0
riscv randconfig-001-20260624 clang-18
riscv randconfig-001-20260624 gcc-13.4.0
riscv randconfig-002-20260624 clang-18
s390 allmodconfig clang-17
s390 allmodconfig clang-23
s390 allnoconfig clang-23
s390 allyesconfig gcc-16.1.0
s390 defconfig clang-18
s390 defconfig gcc-16.1.0
s390 randconfig-001-20260624 clang-18
s390 randconfig-001-20260624 gcc-8.5.0
s390 randconfig-002-20260624 clang-18
s390 randconfig-002-20260624 gcc-10.5.0
sh allmodconfig gcc-16.1.0
sh allnoconfig clang-23
sh allnoconfig gcc-16.1.0
sh allyesconfig clang-17
sh allyesconfig gcc-16.1.0
sh defconfig gcc-14
sh defconfig gcc-16.1.0
sh randconfig-001-20260624 clang-18
sh randconfig-001-20260624 gcc-15.2.0
sh randconfig-002-20260624 clang-18
sh randconfig-002-20260624 gcc-9.5.0
sparc allnoconfig clang-23
sparc allnoconfig gcc-16.1.0
sparc defconfig gcc-16.1.0
sparc randconfig-001-20260624 gcc-14.3.0
sparc randconfig-001-20260624 gcc-8.5.0
sparc randconfig-002-20260624 gcc-14.3.0
sparc randconfig-002-20260624 gcc-8.5.0
sparc64 allmodconfig clang-20
sparc64 defconfig clang-23
sparc64 defconfig gcc-14
sparc64 randconfig-001-20260624 clang-20
sparc64 randconfig-001-20260624 gcc-14.3.0
sparc64 randconfig-002-20260624 clang-20
sparc64 randconfig-002-20260624 gcc-14.3.0
um allmodconfig clang-17
um allnoconfig clang-17
um allnoconfig clang-23
um allyesconfig gcc-14
um allyesconfig gcc-16.1.0
um defconfig clang-23
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001-20260624 clang-23
um randconfig-001-20260624 gcc-14.3.0
um randconfig-002-20260624 clang-17
um randconfig-002-20260624 gcc-14.3.0
um x86_64_defconfig clang-23
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-22
x86_64 allnoconfig clang-22
x86_64 allnoconfig clang-23
x86_64 allyesconfig clang-22
x86_64 buildonly-randconfig-001-20260624 clang-22
x86_64 buildonly-randconfig-001-20260624 gcc-14
x86_64 buildonly-randconfig-002-20260624 clang-22
x86_64 buildonly-randconfig-003-20260624 clang-22
x86_64 buildonly-randconfig-003-20260624 gcc-14
x86_64 buildonly-randconfig-004-20260624 clang-22
x86_64 buildonly-randconfig-005-20260624 clang-22
x86_64 buildonly-randconfig-005-20260624 gcc-14
x86_64 buildonly-randconfig-006-20260624 clang-22
x86_64 buildonly-randconfig-006-20260624 gcc-14
x86_64 defconfig gcc-14
x86_64 kexec clang-22
x86_64 randconfig-001-20260624 clang-22
x86_64 randconfig-002-20260624 clang-22
x86_64 randconfig-003-20260624 clang-22
x86_64 randconfig-004-20260624 clang-22
x86_64 randconfig-004-20260624 gcc-14
x86_64 randconfig-005-20260624 clang-22
x86_64 randconfig-006-20260624 clang-22
x86_64 randconfig-006-20260624 gcc-14
x86_64 randconfig-011-20260624 gcc-14
x86_64 randconfig-012-20260624 gcc-14
x86_64 randconfig-013-20260624 gcc-12
x86_64 randconfig-013-20260624 gcc-14
x86_64 randconfig-014-20260624 gcc-14
x86_64 randconfig-015-20260624 gcc-14
x86_64 randconfig-016-20260624 gcc-14
x86_64 randconfig-071-20260624 gcc-14
x86_64 randconfig-072-20260624 clang-22
x86_64 randconfig-072-20260624 gcc-14
x86_64 randconfig-073-20260624 gcc-14
x86_64 randconfig-074-20260624 clang-22
x86_64 randconfig-074-20260624 gcc-14
x86_64 randconfig-075-20260624 gcc-14
x86_64 randconfig-076-20260624 clang-22
x86_64 randconfig-076-20260624 gcc-14
x86_64 rhel-9.4 clang-22
x86_64 rhel-9.4 gcc-14
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-22
x86_64 rhel-9.4-func gcc-14
x86_64 rhel-9.4-kselftests clang-22
x86_64 rhel-9.4-kselftests gcc-14
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-22
xtensa allnoconfig clang-23
xtensa allnoconfig gcc-16.1.0
xtensa allyesconfig clang-20
xtensa allyesconfig gcc-16.1.0
xtensa randconfig-001-20260624 gcc-14.3.0
xtensa randconfig-002-20260624 gcc-14.3.0
xtensa randconfig-002-20260624 gcc-16.1.0
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH net v2] net: ti: icssg-prueth: fix XDP_TX from the AF_XDP zero-copy RX path
From: Meghana Malladi @ 2026-06-24 6:30 UTC (permalink / raw)
To: David Carlier, danishanwar, rogerq, andrew+netdev, netdev
Cc: davem, edumazet, kuba, pabeni, horms, hawk, john.fastabend, sdf,
ast, daniel, bpf, linux-arm-kernel, linux-kernel, stable
In-Reply-To: <20260623112225.303930-1-devnexen@gmail.com>
Few nitpicks,
On 6/23/26 16:52, David Carlier wrote:
> On XDP_TX from the zero-copy RX path, emac_run_xdp() converts the xsk
> buffer via xdp_convert_zc_to_xdp_frame(), which clones the data into a
> fresh MEM_TYPE_PAGE_ORDER0 page that is not DMA mapped. Transmitting it
> as PRUETH_TX_BUFF_TYPE_XDP_TX derives the DMA address with
> page_pool_get_dma_addr(), reading an uninitialized page->dma_addr, so
> the device DMAs from a bogus address (corrupt TX, or an IOMMU fault).
>
> Pick the TX buffer type from the frame's memory type: keep
> PRUETH_TX_BUFF_TYPE_XDP_TX for page_pool frames and use
> PRUETH_TX_BUFF_TYPE_XDP_NDO for the cloned zero-copy frame, which is then
> DMA mapped through the NDO path and unmapped on completion.
>
> While at it, fix the page_pool XDP_TX completion path. A
> PRUETH_TX_BUFF_TYPE_XDP_TX frame carries a page_pool-owned DMA mapping
> (established against rx_chn->dma_dev), yet prueth_xmit_free()
> unconditionally calls dma_unmap_single() on it with tx_chn->dma_dev,
> tearing down a mapping the driver does not own; xdp_return_frame()
> already recycles the page back to the pool. Tag such frames with a
> dedicated PRUETH_SWDATA_XDPF_TX type so the completion path skips the
> unmap, the same way PRUETH_SWDATA_XSK buffers are handled.
>
> Fixes: 7a64bb388df3 ("net: ti: icssg-prueth: Add AF_XDP zero copy for RX")
> Fixes: 62aa3246f462 ("net: ti: icssg-prueth: Add XDP support")
> Cc: stable@vger.kernel.org
> Signed-off-by: David Carlier <devnexen@gmail.com>
> ---
> v2:
> - fold in the page_pool XDP_TX completion-path unmap fix raised by
> Meghana Malladi: tag page_pool TX frames with PRUETH_SWDATA_XDPF_TX
> so prueth_xmit_free() skips dma_unmap_single() on a pool-owned
> mapping; xdp_return_frame() already recycles the page.
> - add Fixes: 62aa3246f462 for that path.
> - no change to the original zero-copy fix.
> v1: https://lore.kernel.org/netdev/20260620213756.87499-1-devnexen@gmail.com
> drivers/net/ethernet/ti/icssg/icssg_common.c | 20 +++++++++++++++++---
> drivers/net/ethernet/ti/icssg/icssg_prueth.h | 1 +
> 2 files changed, 18 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c
> index 82ddef9c17d5..96c8bf5ef671 100644
> --- a/drivers/net/ethernet/ti/icssg/icssg_common.c
> +++ b/drivers/net/ethernet/ti/icssg/icssg_common.c
> @@ -185,7 +185,7 @@ void prueth_xmit_free(struct prueth_tx_chn *tx_chn,
> first_desc = desc;
> next_desc = first_desc;
> swdata = cppi5_hdesc_get_swdata(first_desc);
> - if (swdata->type == PRUETH_SWDATA_XSK)
> + if (swdata->type == PRUETH_SWDATA_XSK || swdata->type == PRUETH_SWDATA_XDPF_TX)
line length crosses 80 characters
> goto free_pool;
>
> cppi5_hdesc_get_obuf(first_desc, &buf_dma, &buf_dma_len);
> @@ -259,6 +259,7 @@ int emac_tx_complete_packets(struct prueth_emac *emac, int chn,
> napi_consume_skb(skb, budget);
> break;
> case PRUETH_SWDATA_XDPF:
> + case PRUETH_SWDATA_XDPF_TX:
> xdpf = swdata->data.xdpf;
> dev_sw_netstats_tx_add(ndev, 1, xdpf->len);
> total_bytes += xdpf->len;
> @@ -769,7 +770,8 @@ u32 emac_xmit_xdp_frame(struct prueth_emac *emac,
> k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma);
> cppi5_hdesc_attach_buf(first_desc, buf_dma, xdpf->len, buf_dma, xdpf->len);
> swdata = cppi5_hdesc_get_swdata(first_desc);
> - swdata->type = PRUETH_SWDATA_XDPF;
> + swdata->type = buff_type == PRUETH_TX_BUFF_TYPE_XDP_TX ?
> + PRUETH_SWDATA_XDPF_TX : PRUETH_SWDATA_XDPF;
Use braces for the condition please
> swdata->data.xdpf = xdpf;
>
> /* Report BQL before sending the packet */
> @@ -804,6 +806,7 @@ EXPORT_SYMBOL_GPL(emac_xmit_xdp_frame);
> */
> static u32 emac_run_xdp(struct prueth_emac *emac, struct xdp_buff *xdp, u32 *len)
> {
> + enum prueth_tx_buff_type tx_buff_type;
> struct net_device *ndev = emac->ndev;
> struct netdev_queue *netif_txq;
> int cpu = smp_processor_id();
> @@ -826,11 +829,21 @@ static u32 emac_run_xdp(struct prueth_emac *emac, struct xdp_buff *xdp, u32 *len
> goto drop;
> }
>
> + /* In AF_XDP zero-copy mode xdp_convert_buff_to_frame()
> + * clones the xsk buffer into a fresh MEM_TYPE_PAGE_ORDER0
> + * page that is not DMA mapped. Such a frame must be mapped
> + * via the NDO path; only a page pool-backed frame already
> + * carries a usable page_pool DMA address.
> + */
> + tx_buff_type = xdpf->mem_type == MEM_TYPE_PAGE_POOL ?
> + PRUETH_TX_BUFF_TYPE_XDP_TX :
> + PRUETH_TX_BUFF_TYPE_XDP_NDO;
> +
> q_idx = cpu % emac->tx_ch_num;
> netif_txq = netdev_get_tx_queue(ndev, q_idx);
> __netif_tx_lock(netif_txq, cpu);
> result = emac_xmit_xdp_frame(emac, xdpf, q_idx,
> - PRUETH_TX_BUFF_TYPE_XDP_TX);
> + tx_buff_type);
> __netif_tx_unlock(netif_txq);
> if (result == ICSSG_XDP_CONSUMED) {
> ndev->stats.tx_dropped++;
> @@ -1395,6 +1408,7 @@ void prueth_tx_cleanup(void *data, dma_addr_t desc_dma)
> dev_kfree_skb_any(skb);
> break;
> case PRUETH_SWDATA_XDPF:
> + case PRUETH_SWDATA_XDPF_TX:
> xdpf = swdata->data.xdpf;
> xdp_return_frame(xdpf);
> break;
> diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
> index df93d15c5b78..00bb760d68a9 100644
> --- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h
> +++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
> @@ -153,6 +153,7 @@ enum prueth_swdata_type {
> PRUETH_SWDATA_CMD,
> PRUETH_SWDATA_XDPF,
> PRUETH_SWDATA_XSK,
> + PRUETH_SWDATA_XDPF_TX,
> };
>
> enum prueth_tx_buff_type {
Reviewed-by: Meghana Malladi <m-malladi@ti.com>
^ permalink raw reply
* Re: chipidea: usbmisc_imx: i.MX93 support
From: Stefan Wahren @ 2026-06-24 6:30 UTC (permalink / raw)
To: Xu Yang
Cc: Xu Yang, Frank Li, Jun Li, Alexander Stein, Greg Kroah-Hartman,
Linux ARM, linux-usb@vger.kernel.org
In-Reply-To: <ibkjg6bymxoy5wcewrfn4y63f5izlh2yvdqjt42a63kvttr42w@tvqjjhfjg3u4>
Hi Xu,
Am 24.06.26 um 04:50 schrieb Xu Yang:
> On Tue, Jun 23, 2026 at 12:23:12PM +0200, Stefan Wahren wrote:
>> Hi,
>>
>> during debugging USB OTG on our custom i.MX93 board, we noticed remarkable
>> differences between the implementation of the chipidea/usbmisc_imx and the
>> official NXP i.MX93 Reference Manual [1].
>>
>> Is the USB OTG part including PHY of the i.MX93 officially supported in
>> Linux Mainline?
> Yes.
>
>> According to imx91_93_common.dtsi the USB IP of the i.MX93 should be
>> identical to i.MX8MM [2]
>>
>> usbmisc1: usbmisc@4c100200 {
>>
>> compatible = "fsl,imx8mm-usbmisc", "fsl,imx7d-usbmisc",
>> "fsl,imx6q-usbmisc";
>>
>> But looking at the PHY register definition and reset values in the NXP
>> i.MX93 Reference Manual,
>>
>> the registers are comparable to the i.MX95 [3] ones.
>>
>> Could you please clarify which source is correct (Mainline DTS vs Reference
>> Manual)?
> The Reference Manual is correct.
>
>> Looking deeper at chipidea/usbmisc_imx shows the usage of the following
>> register bits
>>
>> #define MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL BIT(0)
>>
>> #define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 BIT(1)
>>
>> #define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 BIT(2)
>>
>> #define MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB BIT(3)
>>
>> #define MX7D_USB_OTG_PHY_STATUS_LINE_STATE0 BIT(0)
>>
>> #define MX7D_USB_OTG_PHY_STATUS_LINE_STATE1 BIT(1)
>>
>> #define MX7D_USB_OTG_PHY_STATUS_CHRGDET BIT(29)
>>
>> According to NXP i.MX93 & i.MX95 Reference Manual, these are bits reserved.
>>
>> Is it correct that the chipidea/usbmisc_imx use these bits on i.MX93?
> i.MX93 & i.MX95 no longer claims to support Battery charger detection. So these
> bits are reserved. However, at the IP level, accessing these bits will not produce
> errors. We will remove .charger_detection hook for the i.MX9 series in the future.
I think the access to MX6_BM_NON_BURST_SETTING should be addressed, too.
> Do you want to use Battery charger detection on i.MX93?
No, we don't need this feature in our case. We suspect the cause for our
issue comes from the hardware design of the board.
Thanks
>
> Thanks,
> Xu Yang
>
>> Best regards
>>
>> [1] - https://www.nxp.com/docs/en/reference-manual/IMX93RM.pdf
>> <https://www.nxp.com/docs/en/reference-manual/IMX93RM.pdf>
>>
>> [2] - https://www.nxp.com/docs/en/reference-manual/IMX8MMRM.pdf
>> <https://www.nxp.com/docs/en/reference-manual/IMX8MMRM.pdf>
>>
>> [3] - https://www.nxp.com/docs/en/reference-manual/IMX95RM.pdf
>> <https://www.nxp.com/docs/en/reference-manual/IMX95RM.pdf>
>>
^ permalink raw reply
* [PATCH v2] clk: mediatek: mt6735: Unregister PLLs on probe failure
From: Myeonghun Pak @ 2026-06-24 6:23 UTC (permalink / raw)
To: Yassine Oudjana, Michael Turquette, Stephen Boyd
Cc: Matthias Brugger, AngeloGioacchino Del Regno, linux-clk,
linux-mediatek, linux-kernel, linux-arm-kernel, Myeonghun Pak,
Ijae Kim
mtk_clk_register_plls() registers the apmixedsys PLL clocks manually, while
clk_mt6735_apmixed_remove() unregisters them on driver removal.
If devm_of_clk_add_hw_provider() fails after the PLL registration succeeds,
probe returns the error directly and the remove callback is not run. This
leaves the registered PLL clocks behind on the probe failure path.
Unregister the PLLs in that failure branch before returning the error.
Fixes: 43c04ed79189 ("clk: mediatek: Add drivers for MediaTek MT6735 main clock and reset drivers")
Co-developed-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
---
Changes in v2:
- Unregister PLLs directly in the provider-registration failure branch.
- Wrap the commit message line flagged by checkpatch.
drivers/clk/mediatek/clk-mt6735-apmixedsys.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
index 9e30c089a2..69d9ce1210 100644
--- a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
+++ b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
@@ -102,9 +102,12 @@ static int clk_mt6735_apmixed_probe(struct platform_device *pdev)
ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
clk_data);
- if (ret)
+ if (ret) {
dev_err(&pdev->dev,
"Failed to register clock provider: %d\n", ret);
+ mtk_clk_unregister_plls(apmixedsys_plls, ARRAY_SIZE(apmixedsys_plls),
+ clk_data);
+ }
return ret;
}
--
2.47.1
^ permalink raw reply related
* Re: [PATCH] clk: mediatek: mt6735: Unregister PLLs on probe failure
From: Myeonghun Pak @ 2026-06-24 6:21 UTC (permalink / raw)
To: Brian Masney
Cc: Yassine Oudjana, Michael Turquette, Stephen Boyd,
Matthias Brugger, AngeloGioacchino Del Regno, linux-clk,
linux-mediatek, linux-kernel, linux-arm-kernel, Ijae Kim
In-Reply-To: <ajqZoOv-TvmOMLCh@redhat.com>
Hi,
Thanks for the review.
I will send a v2 with mtk_clk_unregister_plls() moved directly into the
devm_of_clk_add_hw_provider() failure branch, and I will make sure it passes
checkpatch before sending.
Best regards,
Myeonghun
2026년 6월 23일 (화) 오후 11:35, Brian Masney <bmasney@redhat.com>님이 작성:
>
> Hi Myeonghun,
>
> On Tue, Jun 23, 2026 at 06:41:11PM +0900, Myeonghun Pak wrote:
> > mtk_clk_register_plls() registers the apmixedsys PLL clocks manually, while
> > clk_mt6735_apmixed_remove() unregisters them on driver removal.
> >
> > If devm_of_clk_add_hw_provider() fails after the PLL registration succeeds,
> > probe returns the error directly and the remove callback is not run. This
> > leaves the registered PLL clocks behind on the probe failure path.
> >
> > Add an unregister_plls error path so provider registration failures unwind the
> > PLLs before returning the error.
>
> Please run your patches through checkpatch.pl before sending.
>
> >
> > Fixes: 43c04ed79189 ("clk: mediatek: Add drivers for MediaTek MT6735 main clock and reset drivers")
> > Co-developed-by: Ijae Kim <ae878000@gmail.com>
> > Signed-off-by: Ijae Kim <ae878000@gmail.com>
> > Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
> >
> > ---
> > drivers/clk/mediatek/clk-mt6735-apmixedsys.c | 9 ++++++++-
> > 1 file changed, 8 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
> > index 9e30c089a2..04cf9665ec 100644
> > --- a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
> > +++ b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
> > @@ -102,10 +102,17 @@ static int clk_mt6735_apmixed_probe(struct platform_device *pdev)
> >
> > ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
> > clk_data);
> > - if (ret)
> > + if (ret) {
> > dev_err(&pdev->dev,
> > "Failed to register clock provider: %d\n", ret);
> > + goto unregister_plls;
> > + }
> > +
> > + return 0;
> >
> > +unregister_plls:
> > + mtk_clk_unregister_plls(apmixedsys_plls, ARRAY_SIZE(apmixedsys_plls),
> > + clk_data);
> > return ret;
> > }
>
> This fix is correct. Since only one path will encounter this, personally
> I would just put the call to mtk_clk_unregister_plls() inside the if
> statement above to simplify this further.
>
> Brian
>
^ permalink raw reply
* Re: [PATCH v2] cpufreq: apple-soc: Fix OPP table cleanup
From: Viresh Kumar @ 2026-06-24 6:15 UTC (permalink / raw)
To: Haoxiang Li
Cc: sven, j, neal, rafael, linux-arm-kernel, asahi, linux-pm,
linux-kernel, sumit.semwal
In-Reply-To: <20260624033756.2702021-1-haoxiang_li2024@163.com>
On 24-06-26, 11:37, Haoxiang Li wrote:
> dev_pm_opp_of_add_table() adds the DT OPP table but not all failure
> paths remove it. The driver also uses dev_pm_opp_remove_all_dynamic(),
> which is not the right cleanup helper for OPPs loaded from firmware.
>
> Remove the DT OPP table with dev_pm_opp_of_remove_table() on init
> failure paths and from apple_soc_cpufreq_exit().
>
> Signed-off-by: Haoxiang Li <haoxiang_li2024@163.com>
Please add Fixes and Stable tags.
> diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c
> @@ -320,10 +320,10 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy)
> dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
> out_free_priv:
> kfree(priv);
> -out_free_opp:
> - dev_pm_opp_remove_all_dynamic(cpu_dev);
> out_iounmap:
> iounmap(reg_base);
> +out_remove_opp_table:
> + dev_pm_opp_of_remove_table(cpu_dev);
> return ret;
> }
>
> @@ -332,7 +332,7 @@ static void apple_soc_cpufreq_exit(struct cpufreq_policy *policy)
> struct apple_cpu_priv *priv = policy->driver_data;
>
> dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
> - dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
> + dev_pm_opp_of_remove_table(priv->cpu_dev);
> iounmap(priv->reg_base);
> kfree(priv);
The order of cleanup calls should match in the above two sequences and there are
other missing pieces as well.
I think this would fix it:
diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c
index 9396034167e5..150deae8c31d 100644
--- a/drivers/cpufreq/apple-soc-cpufreq.c
+++ b/drivers/cpufreq/apple-soc-cpufreq.c
@@ -251,21 +251,19 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy)
return -ENODEV;
}
- ret = dev_pm_opp_of_add_table(cpu_dev);
- if (ret < 0) {
- dev_err(cpu_dev, "%s: failed to add OPP table: %d\n", __func__, ret);
- return ret;
- }
+ priv = kzalloc_obj(*priv);
+ if (!priv)
+ return -ENOMEM;
ret = apple_soc_cpufreq_find_cluster(policy, ®_base, &info);
if (ret) {
dev_err(cpu_dev, "%s: failed to get cluster info: %d\n", __func__, ret);
- return ret;
+ goto out_priv;
}
- ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
- if (ret) {
- dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", __func__, ret);
+ ret = dev_pm_opp_of_cpumask_add_table(policy->cpus);
+ if (ret < 0) {
+ dev_err(cpu_dev, "%s: failed to add OPP table: %d\n", __func__, ret);
goto out_iounmap;
}
@@ -273,19 +271,13 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy)
if (ret <= 0) {
dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
ret = -EPROBE_DEFER;
- goto out_free_opp;
- }
-
- priv = kzalloc_obj(*priv);
- if (!priv) {
- ret = -ENOMEM;
- goto out_free_opp;
+ goto out_free_table;
}
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
if (ret) {
dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
- goto out_free_priv;
+ goto out_free_table;
}
/* Get OPP levels (p-state indexes) and stash them in driver_data */
@@ -320,12 +312,12 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy)
out_free_cpufreq_table:
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
-out_free_priv:
- kfree(priv);
-out_free_opp:
- dev_pm_opp_remove_all_dynamic(cpu_dev);
+out_free_table:
+ dev_pm_opp_of_cpumask_remove_table(policy->cpus);
out_iounmap:
iounmap(reg_base);
+out_free_priv:
+ kfree(priv);
return ret;
}
@@ -334,7 +326,7 @@ static void apple_soc_cpufreq_exit(struct cpufreq_policy *policy)
struct apple_cpu_priv *priv = policy->driver_data;
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
- dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
+ dev_pm_opp_of_cpumask_remove_table(policy->cpus);
iounmap(priv->reg_base);
kfree(priv);
}
--
viresh
^ permalink raw reply related
* Re: [PATCH 0/3] KVM: arm64: nv: Shadow ptdump fixes
From: Itaru Kitayama @ 2026-06-24 6:02 UTC (permalink / raw)
To: Wei-Lin Chang
Cc: linux-arm-kernel, kvmarm, linux-kernel, Marc Zyngier,
Oliver Upton, Joey Gouly, Steffen Eiden, Suzuki K Poulose,
Zenghui Yu, Catalin Marinas, Will Deacon
In-Reply-To: <20260623142443.648972-1-weilin.chang@arm.com>
Hi Wei-Lin,
On Tue, Jun 23, 2026 at 03:24:40PM +0100, Wei-Lin Chang wrote:
> Hi,
>
> This series fixes two bugs regarding the shadow ptdump debugfs files.
> It is based on kvmarm/fixes + [1] ("KVM: arm64: Reassign nested_mmus
> array behind mmu_lock").
>
> The first is a UAF. A nested mmu can still be accessed when the debugfs
> file is being closed, after the nested mmus are freed. I can observe
> this by turning on CONFIG_KASAN and closing the file after the VM is
> destroyed. To fix this, mmu access is avoided in the .release()
> callback.
>
> The second is sleeping in atomic context, found by Itaru [2] (thanks).
> Originally the code creates a debugfs file whenever a context gets bound
> to an s2 mmu instance, and deletes it when it gets unbound. Problem is
> the bind/unbind is done with the mmu_lock held, and debugfs file
> creation and deletion can sleep. This is observable by using
> CONFIG_DEBUG_ATOMIC_SLEEP. The new approach is just have one debugfs
> file for each s2 mmu instance, and show their state + information when
> requested, which can be invalid, or VTCR + VTTBR + whether s2 enabled +
> ptdump.
>
> The fixes are tested with CONFIG_PROVE_LOCKING,
> CONFIG_DEBUG_ATOMIC_SLEEP, and CONFIG_KASAN.
>
> Thanks!
> Wei-Lin Chang
>
> [1]: https://lore.kernel.org/kvmarm/aiKIVVeIr1aAB1yp@v4bel/
> [2]: https://lore.kernel.org/kvmarm/aiuF0KSvvv-ZozI1@sm-arm-grace07/
>
> Wei-Lin Chang (3):
> KVM: arm64: nv: Print nested mmu info in kvm_ptdump_guest_show()
> KVM: arm64: ptdump: Store both mmu and kvm pointers in
> kvm_ptdump_guest_state
> KVM: arm64: nv: Move to per nested mmu ptdump files
>
> arch/arm64/kvm/nested.c | 16 +++++++++++-----
> arch/arm64/kvm/ptdump.c | 29 +++++++++++++++++++----------
> 2 files changed, 30 insertions(+), 15 deletions(-)
>
> --
> 2.43.0
At end of the execution of the shadow stage 2 selftest I see:
[ 569.228448] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000098
[ 569.228712] Mem abort info:
[ 569.229091] ESR = 0x0000000096000046
[ 569.229165] EC = 0x25: DABT (current EL), IL = 32 bits
[ 569.229213] SET = 0, FnV = 0
[ 569.229244] EA = 0, S1PTW = 0
[ 569.229276] FSC = 0x06: level 2 translation fault
[ 569.229312] Data abort info:
[ 569.229341] ISV = 0, ISS = 0x00000046, ISS2 = 0x00000000
[ 569.229369] CM = 0, WnR = 1, TnD = 0, TagAccess = 0
[ 569.229397] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0
[ 569.229458] user pgtable: 4k pages, 48-bit VAs, pgdp=000000006dce3000
[ 569.229545] [0000000000000098] pgd=0800000048b63403, p4d=0800000048b63403, pud=0800000048b7f403, pmd=0000000000000
** replaying previous printk message **
[ 569.229545] [0000000000000098] pgd=0800000048b63403, p4d=0800000048b63403, pud=0800000048b7f403, pmd=0000000000000000
[ 569.236428] Internal error: Oops: 0000000096000046 [#1] SMP
[ 569.237974] Modules linked in:
[ 569.238644] CPU: 1 UID: 0 PID: 824 Comm: shadow_stage2 Not tainted 7.1.0-rc4+ #59 PREEMPT(full)
[ 569.239139] Hardware name: QEMU QEMU Virtual Machine, BIOS 2024.02-2ubuntu0.7 11/27/2025
[ 569.239632] pstate: 61402009 (nZCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)
[ 569.240004] pc : down_write+0x50/0xe8
[ 569.240450] lr : down_write+0x34/0xe8
[ 569.240696] sp : ffff80008252ba20
[ 569.240965] x29: ffff80008252ba20 x28: 0000000000000000 x27: 0000000040000200
[ 569.241346] x26: 0000000000000200 x25: ffffd1bf542891a0 x24: 0000000000000001
[ 569.241625] x23: 0000000000000098 x22: ffff000000637480 x21: ffffd1bf57abc518
[ 569.241985] x20: 0000000000000000 x19: 0000000000000098 x18: ffff80008253d090
[ 569.242261] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 569.242568] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000
[ 569.242904] x11: 0000000000000000 x10: 0000000000000000 x9 : ffffd1bf5532388c
[ 569.243335] x8 : 0000000000000000 x7 : 0000000000000000 x6 : 0000000000000000
[ 569.243638] x5 : 0000000000000000 x4 : 0000000000000000 x3 : 0000000000000000
[ 569.244056] x2 : 0000000000000000 x1 : 0000000000000001 x0 : 0000000000000000
[ 569.244507] Call trace:
[ 569.244778] down_write+0x50/0xe8 (P)
[ 569.245094] __simple_recursive_removal+0x68/0x230
[ 569.245322] simple_recursive_removal+0x20/0x50
[ 569.245498] debugfs_remove+0x64/0xc0
[ 569.245655] kvm_nested_s2_ptdump_remove_debugfs+0x20/0x48
[ 569.245960] kvm_arch_flush_shadow_all+0x4c/0xc0
[ 569.246100] kvm_mmu_notifier_release+0x3c/0x90
[ 569.246344] mmu_notifier_unregister+0x68/0x148
[ 569.246594] kvm_destroy_vm+0x130/0x2d8
[ 569.246829] kvm_device_release+0xf8/0x170
[ 569.246969] __fput+0xf4/0x350
[ 569.247147] fput_close_sync+0x4c/0x138
[ 569.247291] __arm64_sys_close+0x44/0xa0
[ 569.247493] invoke_syscall+0xa8/0x138
[ 569.247727] el0_svc_common.constprop.0+0x4c/0x140
[ 569.248059] do_el0_svc+0x28/0x58
[ 569.248236] el0_svc+0x48/0x218
[ 569.248420] el0t_64_sync_handler+0xc0/0x108
[ 569.248690] el0t_64_sync+0x1b4/0x1b8
[ 569.249737] Code: b9000820 d503201f d2800000 d2800021 (c8e07e61)
[ 569.250624] ---[ end trace 0000000000000000 ]---
[ 569.251589] note: shadow_stage2[824] exited with preempt_count 1
[ 569.253677] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000098
[ 569.254391] Mem abort info:
[ 569.254416] ESR = 0x0000000096000046
[ 569.254436] EC = 0x25: DABT (current EL), IL = 32 bits
[ 569.254479] SET = 0, FnV = 0
[ 569.254493] EA = 0, S1PTW = 0
[ 569.254506] FSC = 0x06: level 2 translation fault
[ 569.254522] Data abort info:
[ 569.254530] ISV = 0, ISS = 0x00000046, ISS2 = 0x00000000
[ 569.254544] CM = 0, WnR = 1, TnD = 0, TagAccess = 0
[ 569.254559] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0
[ 569.254574] user pgtable: 4k pages, 48-bit VAs, pgdp=000000006dce3000
[ 569.254602] [0000000000000098] pgd=0800000048b63403, p4d=0800000048b63403, pud=0800000048b7f403, pmd=0000000000000000
[ 569.254709] Internal error: Oops: 0000000096000046 [#2] SMP
[ 569.257747] Modules linked in:
[ 569.258124] CPU: 1 UID: 0 PID: 824 Comm: shadow_stage2 Tainted: G D 7.1.0-rc4+ #59 PREEMPT(full)
[ 569.258642] Tainted: [D]=DIE
[ 569.258862] Hardware name: QEMU QEMU Virtual Machine, BIOS 2024.02-2ubuntu0.7 11/27/2025
[ 569.259232] pstate: 60402009 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 569.259549] pc : down_write+0x50/0xe8
[ 569.259814] lr : down_write+0x34/0xe8
[ 569.259960] sp : ffff80008252b310
[ 569.260175] x29: ffff80008252b310 x28: 0000000000000000 x27: 0000000040000200
[ 569.260507] x26: 0000000000000200 x25: ffffd1bf542891a0 x24: 0000000000000001
[ 569.260891] x23: 0000000000000098 x22: ffff000000637480 x21: ffffd1bf57abc518
[ 569.261278] x20: 0000000000000000 x19: 0000000000000098 x18: ffff80008253d138
[ 569.261652] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 569.262180] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000
[ 569.262572] x11: 0000000000000000 x10: 0000000000000000 x9 : ffffd1bf5532388c
[ 569.263299] x8 : ffff80008252b508 x7 : 0000000000000000 x6 : 0000000000000000
[ 569.263950] x5 : 0000000000000000 x4 : 0000000000000000 x3 : 0000000000000000
[ 569.264428] x2 : 0000000000000000 x1 : 0000000000000001 x0 : 0000000000000000
[ 569.264799] Call trace:
[ 569.265039] down_write+0x50/0xe8 (P)
[ 569.265441] __simple_recursive_removal+0x68/0x230
[ 569.265817] simple_recursive_removal+0x20/0x50
[ 569.266132] debugfs_remove+0x64/0xc0
[ 569.266411] kvm_nested_s2_ptdump_remove_debugfs+0x20/0x48
[ 569.266782] kvm_arch_flush_shadow_all+0x4c/0xc0
[ 569.267059] kvm_mmu_notifier_release+0x3c/0x90
[ 569.267564] __mmu_notifier_release+0x88/0x2a0
[ 569.267736] exit_mmap+0x430/0x490
[ 569.267943] __mmput+0x3c/0x178
[ 569.268068] mmput+0xa4/0xd8
[ 569.268221] do_exit+0x274/0xb00
[ 569.268335] make_task_dead+0x98/0x1f0
[ 569.268634] die+0x194/0x1a0
[ 569.268893] die_kernel_fault+0x1d0/0x3c0
[ 569.269139] __do_kernel_fault+0x280/0x290
[ 569.269348] do_page_fault+0x128/0x7d8
[ 569.269550] do_translation_fault+0x74/0xc0
[ 569.269767] do_mem_abort+0x50/0xd0
[ 569.269945] el1_abort+0x44/0x80
[ 569.270122] el1h_64_sync_handler+0x54/0xd0
[ 569.270306] el1h_64_sync+0x80/0x88
[ 569.270683] down_write+0x50/0xe8 (P)
[ 569.270997] __simple_recursive_removal+0x68/0x230
[ 569.271217] simple_recursive_removal+0x20/0x50
[ 569.271704] debugfs_remove+0x64/0xc0
[ 569.271948] kvm_nested_s2_ptdump_remove_debugfs+0x20/0x48
[ 569.272212] kvm_arch_flush_shadow_all+0x4c/0xc0
[ 569.272510] kvm_mmu_notifier_release+0x3c/0x90
[ 569.272731] mmu_notifier_unregister+0x68/0x148
[ 569.272960] kvm_destroy_vm+0x130/0x2d8
[ 569.273210] kvm_device_release+0xf8/0x170
[ 569.273490] __fput+0xf4/0x350
[ 569.273748] fput_close_sync+0x4c/0x138
[ 569.274023] __arm64_sys_close+0x44/0xa0
[ 569.274289] invoke_syscall+0xa8/0x138
[ 569.274560] el0_svc_common.constprop.0+0x4c/0x140
[ 569.274838] do_el0_svc+0x28/0x58
[ 569.275066] el0_svc+0x48/0x218
[ 569.275321] el0t_64_sync_handler+0xc0/0x108
[ 569.275556] el0t_64_sync+0x1b4/0x1b8
[ 569.275844] Code: b9000820 d503201f d2800000 d2800021 (c8e07e61)
[ 569.276068] ---[ end trace 0000000000000000 ]---
[ 569.277042] note: shadow_stage2[824] exited with preempt_count 1
[ 569.277234] Fixing recursive fault but reboot is needed!
the kernel is based off of kvmarm/fixes, applied your series and
Hyunwoo's patch as well. Could you take a look at this?
Thanks,
Itaru.
>
^ permalink raw reply
* Re: [PATCH] remoteproc: xlnx: refactor start & stop ops
From: Michal Simek @ 2026-06-24 6:12 UTC (permalink / raw)
To: tanmay.shah, andersson, mathieu.poirier
Cc: linux-arm-kernel, linux-kernel, linux-remoteproc
In-Reply-To: <a9baf2c9-d4d9-4152-9188-c33fce2daf15@amd.com>
On 6/23/26 00:29, Shah, Tanmay wrote:
> Hello,
>
>
> On 6/22/2026 7:25 AM, Michal Simek wrote:
>>
>>
>> On 6/19/26 18:38, Tanmay Shah wrote:
>>> Current _start and _stop ops are implemented using various APIs from the
>>> platform management firmware driver. Instead provide respective RPU
>>> start and stop API in the firmware driver and move the logic to interact
>>> with the PM firmware in the firmware driver. The remoteproc driver
>>> doesn't
>>> need to know actual logic, but only the final result i.e. RPU start/stop
>>> was success or not. This refactor keeps the remoteproc driver simple and
>>> moves firmware interaction logic to the firmware driver.
>>>
>>> Signed-off-by: Tanmay Shah <tanmay.shah@amd.com>
>>> ---
>>> drivers/firmware/xilinx/zynqmp.c | 93 +++++++++++++++++++++++++
>>> drivers/remoteproc/xlnx_r5_remoteproc.c | 68 ++----------------
>>> include/linux/firmware/xlnx-zynqmp.h | 12 ++++
>>> 3 files changed, 110 insertions(+), 63 deletions(-)
>>>
>>> diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/
>>> xilinx/zynqmp.c
>>> index af838b2dc327..f9a3a95b0638 100644
>>> --- a/drivers/firmware/xilinx/zynqmp.c
>>> +++ b/drivers/firmware/xilinx/zynqmp.c
>>> @@ -1513,6 +1513,99 @@ int zynqmp_pm_request_wake(const u32 node,
>>> }
>>> EXPORT_SYMBOL_GPL(zynqmp_pm_request_wake);
>>> +/**
>>> + * zynqmp_pm_start_rpu - Boot Real-time Processing Unit (Cortex-R) on
>>> SoC
>>> + *
>>> + * @node: power-domains id of the core
>>> + * @bootaddr: Boot address of elf
>>> + *
>>> + * Return: status, either success or error+reason
>>> + */
>>> +int zynqmp_pm_start_rpu(const u32 node, const u64 bootaddr)
>>> +{
>>> + enum rpu_boot_mem bootmem;
>>> + int ret;
>>> +
>>> + /*
>>> + * The exception vector pointers (EVP) refer to the base-address of
>>> + * exception vectors (for reset, IRQ, FIQ, etc). The reset-vector
>>> + * starts at the base-address and subsequent vectors are on 4-byte
>>> + * boundaries.
>>> + *
>>> + * Exception vectors can start either from 0x0000_0000 (LOVEC) or
>>> + * from 0xFFFF_0000 (HIVEC) which is mapped in the OCM (On-Chip
>>> Memory)
>>
>> here
>>
>>> + *
>>> + * Usually firmware will put Exception vectors at LOVEC.
>>> + *
>>> + * It is not recommend that you change the exception vector.
>>> + * Changing the EVP to HIVEC will result in increased interrupt
>>> latency
>>> + * and jitter. Also, if the OCM is secured and the Cortex-R5F
>>> processor
>>> + * is non-secured, then the Cortex-R5F processor cannot access the
>>> + * HIVEC exception vectors in the OCM.
>>> + */
>>> + bootmem = (bootaddr >= 0xFFFC0000) ?
>>
>> and here you have different values without any explanation why.
>>
>
> The value in the comment is correct, but the check is done for all of
> OCM address range. This is just refactoring of the interfaces and not
> the actual logic. There is a separate patch which actually refactors the
> logic, I will send it later. I would like to keep this as it is because
> this was originally there, and the intent of the commit is not to modify it.
ok.
Acked-by: Michal Simek <michal.simek@amd.com>
Thanks,
Michal
^ permalink raw reply
* [PATCH] usb: dwc3: st: Propagate reset deassert failures
From: Pengpeng Hou @ 2026-06-24 5:57 UTC (permalink / raw)
To: Patrice Chotard, Thinh Nguyen, Greg Kroah-Hartman, Philipp Zabel
Cc: linux-arm-kernel, linux-usb, linux-kernel, Pengpeng Hou
The ST DWC3 glue driver treats the powerdown and softreset reset
controls as required resources, but ignores reset_control_deassert()
failures before populating the child DWC3 device. Resume ignores the
same failures before returning success.
Check the deassert operations and unwind the already deasserted
powerdown reset if softreset deassertion fails.
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
drivers/usb/dwc3/dwc3-st.c | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c
index 5d513decaacd..bbabfd933798 100644
--- a/drivers/usb/dwc3/dwc3-st.c
+++ b/drivers/usb/dwc3/dwc3-st.c
@@ -242,7 +242,9 @@ static int st_dwc3_probe(struct platform_device *pdev)
"could not get power controller\n");
/* Manage PowerDown */
- reset_control_deassert(dwc3_data->rstc_pwrdn);
+ ret = reset_control_deassert(dwc3_data->rstc_pwrdn);
+ if (ret)
+ return ret;
dwc3_data->rstc_rst =
devm_reset_control_get_shared(dev, "softreset");
@@ -253,7 +255,9 @@ static int st_dwc3_probe(struct platform_device *pdev)
}
/* Manage SoftReset */
- reset_control_deassert(dwc3_data->rstc_rst);
+ ret = reset_control_deassert(dwc3_data->rstc_rst);
+ if (ret)
+ goto undo_powerdown;
/* Allocate and initialize the core */
ret = of_platform_populate(node, NULL, NULL, dev);
@@ -328,8 +332,15 @@ static int st_dwc3_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
- reset_control_deassert(dwc3_data->rstc_pwrdn);
- reset_control_deassert(dwc3_data->rstc_rst);
+ ret = reset_control_deassert(dwc3_data->rstc_pwrdn);
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(dwc3_data->rstc_rst);
+ if (ret) {
+ reset_control_assert(dwc3_data->rstc_pwrdn);
+ return ret;
+ }
ret = st_dwc3_drd_init(dwc3_data);
if (ret) {
--
2.50.1 (Apple Git-155)
^ permalink raw reply related
* [PATCH] rtc: zynqmp: Return optional clock lookup errors
From: Pengpeng Hou @ 2026-06-24 5:55 UTC (permalink / raw)
To: Alexandre Belloni, Michal Simek
Cc: linux-rtc, linux-arm-kernel, linux-kernel, Pengpeng Hou
devm_clk_get_optional() returns NULL when the optional clock is absent,
but returns an ERR_PTR when the clock provider lookup fails. Probe
currently keeps the ERR_PTR and then passes it to clk_get_rate().
Return the lookup error instead. A truly absent optional clock still
reaches the existing calibration fallback through clk_get_rate(NULL).
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
drivers/rtc/rtc-zynqmp.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c
index 2ae54804b87a..5bcb7536e973 100644
--- a/drivers/rtc/rtc-zynqmp.c
+++ b/drivers/rtc/rtc-zynqmp.c
@@ -334,10 +334,9 @@ static int xlnx_rtc_probe(struct platform_device *pdev)
/* Getting the rtc info */
xrtcdev->rtc_clk = devm_clk_get_optional(&pdev->dev, "rtc");
- if (IS_ERR(xrtcdev->rtc_clk)) {
- if (PTR_ERR(xrtcdev->rtc_clk) != -EPROBE_DEFER)
- dev_warn(&pdev->dev, "Device clock not found.\n");
- }
+ if (IS_ERR(xrtcdev->rtc_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(xrtcdev->rtc_clk),
+ "Failed to get rtc clock\n");
xrtcdev->freq = clk_get_rate(xrtcdev->rtc_clk);
if (!xrtcdev->freq) {
ret = of_property_read_u32(pdev->dev.of_node, "calibration",
--
2.50.1 (Apple Git-155)
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox