[v2,12/34] media: iris: add video processing unit(VPU) specific register handling
Commit Message
Registers are defined differently for different VPUs.
Define ops for VPU specific handling to accommodate
different VPUs. Implement boot sequence of firmware and interrupt
programming.
Signed-off-by: Dikshita Agarwal <quic_dikshita@quicinc.com>
---
drivers/media/platform/qcom/vcodec/iris/Makefile | 4 +-
.../media/platform/qcom/vcodec/iris/iris_core.h | 3 +
drivers/media/platform/qcom/vcodec/iris/iris_hfi.c | 7 +
.../media/platform/qcom/vcodec/iris/iris_probe.c | 7 +
.../media/platform/qcom/vcodec/iris/vpu_common.c | 71 +++++++++
.../media/platform/qcom/vcodec/iris/vpu_common.h | 32 ++++
.../media/platform/qcom/vcodec/iris/vpu_iris3.c | 166 +++++++++++++++++++++
.../media/platform/qcom/vcodec/iris/vpu_iris3.h | 13 ++
8 files changed, 302 insertions(+), 1 deletion(-)
create mode 100644 drivers/media/platform/qcom/vcodec/iris/vpu_common.c
create mode 100644 drivers/media/platform/qcom/vcodec/iris/vpu_common.h
create mode 100644 drivers/media/platform/qcom/vcodec/iris/vpu_iris3.c
create mode 100644 drivers/media/platform/qcom/vcodec/iris/vpu_iris3.h
Comments
On 18/12/2023 12:32, Dikshita Agarwal wrote:
> Registers are defined differently for different VPUs.
> Define ops for VPU specific handling to accommodate
> different VPUs. Implement boot sequence of firmware and interrupt
> programming.
>
...
> +
> +int write_register(struct iris_core *core, u32 reg, u32 value)
> +{
> + void __iomem *base_addr;
> + int ret;
> +
> + ret = check_core_lock(core);
> + if (ret)
> + return ret;
> +
> + base_addr = core->reg_base;
> + base_addr += reg;
> + writel_relaxed(value, base_addr);
> +
> + /* Make sure value is written into the register */
> + wmb();
Just don't use relaxed method. The same applies to other places like that.
> +
> + return ret;
> +}
> +
> +int read_register(struct iris_core *core, u32 reg, u32 *value)
> +{
> + void __iomem *base_addr;
> +
> + base_addr = core->reg_base;
> +
> + *value = readl_relaxed(base_addr + reg);
> +
> + /* Make sure value is read correctly from the register */
> + rmb();
> +
> + return 0;
> +}
> +
> +static const struct compat_handle compat_handle[] = {
> + {
> + .compat = "qcom,sm8550-iris",
> + .init = init_iris3,
Uh...
> + },
> +};
> +
> +int init_vpu(struct iris_core *core)
> +{
> + struct device *dev = NULL;
> + int i, ret = 0;
> +
> + dev = core->dev;
> +
> + for (i = 0; i < ARRAY_SIZE(compat_handle); i++) {
> + if (of_device_is_compatible(dev->of_node, compat_handle[i].compat)) {
> + ret = compat_handle[i].init(core);
This does not look good. Use flags, quirks, type, pointer ops in
structures. Just look at existing code in Linux kernel. Do not
reimplement driver match data.
Best regards,
Krzysztof
On 12/18/23 12:32, Dikshita Agarwal wrote:
> Registers are defined differently for different VPUs.
> Define ops for VPU specific handling to accommodate
> different VPUs. Implement boot sequence of firmware and interrupt
> programming.
>
> Signed-off-by: Dikshita Agarwal <quic_dikshita@quicinc.com>
> ---
[...]
> +int write_register(struct iris_core *core, u32 reg, u32 value)
> +{
> + void __iomem *base_addr;
> + int ret;
> +
> + ret = check_core_lock(core);
> + if (ret)
> + return ret;
> +
> + base_addr = core->reg_base;
> + base_addr += reg;
> + writel_relaxed(value, base_addr);> +
> + /* Make sure value is written into the register */
IIUC barriers only ensure the prior writes need to be submitted
before the next ones, they don't actually guarantee the value
arrives at the destination. You would probably want to read the
register back here to guarantee that.
> + wmb();
> +
> + return ret;
> +}
> +
> +int read_register(struct iris_core *core, u32 reg, u32 *value)
> +{
> + void __iomem *base_addr;
> +
> + base_addr = core->reg_base;
> +
> + *value = readl_relaxed(base_addr + reg);
> +
> + /* Make sure value is read correctly from the register */
> + rmb();
You can drop _relaxed for that and simply use readl() instead of
this entire wrapper..
> +
> + return 0;
> +}
> +
> +static const struct compat_handle compat_handle[] = {
> + {
> + .compat = "qcom,sm8550-iris",
> + .init = init_iris3,
> + },
> +};
> +
> +int init_vpu(struct iris_core *core)
> +{
> + struct device *dev = NULL;
> + int i, ret = 0;
> +
> + dev = core->dev;
> +
> + for (i = 0; i < ARRAY_SIZE(compat_handle); i++) {
> + if (of_device_is_compatible(dev->of_node, compat_handle[i].compat)) {
> + ret = compat_handle[i].init(core);
> + if (ret)
> + return ret;
> + break;
> + }
> + }
> +
> + if (i == ARRAY_SIZE(compat_handle))
> + return -EINVAL;
> +
> + return ret;
> +}
what's wrong with of_match_data?
[...]
> +
> +#define call_vpu_op(d, op, ...) \
> + (((d) && (d)->vpu_ops && (d)->vpu_ops->op) ? \
> + ((d)->vpu_ops->op(__VA_ARGS__)) : 0)
> +
> +struct compat_handle {
> + const char *compat;
> + int (*init)(struct iris_core *core);
> +};
> +
> +struct vpu_ops {
> + int (*boot_firmware)(struct iris_core *core);
> + int (*raise_interrupt)(struct iris_core *core);
> +};
or you can simply create functions like
boot_firmware(...)
raise_interrupt(...)
that call other functions as needed (unless there's no need if e.g.
the cores are so similar)
and drop this sugar (well, bitter sugar at least to my taste) syntax
[...]
> + int ret;
> + u32 value;
reverse-Christmas-tree, please
(Christmas is in a week, get festive! :D)
> +
> + value = (u32)core->iface_q_table.device_addr;
> + ret = write_register(core, UC_REGION_ADDR_IRIS3, value);
> + if (ret)
> + return ret;
> +
> + value = SHARED_QSIZE;
> + ret = write_register(core, UC_REGION_SIZE_IRIS3, value);
> + if (ret)
> + return ret;
> +
> + value = (u32)core->iface_q_table.device_addr;
> + ret = write_register(core, QTBL_ADDR_IRIS3, value);
> + if (ret)
> + return ret;
> +
> + ret = write_register(core, QTBL_INFO_IRIS3, 0x01);
> + if (ret)
> + return ret;
> +
> + value = (u32)((u64)core->iface_q_table.kernel_vaddr);
lower_32_bits()
> + ret = write_register(core, CPU_CS_VCICMDARG0_IRIS3, value);
> + if (ret)
> + return ret;
> +
> + value = (u32)((u64)core->iface_q_table.kernel_vaddr >> 32);
upper_32_bits()
> + ret = write_register(core, CPU_CS_VCICMDARG1_IRIS3, value);
> + if (ret)
> + return ret;
> +
> + if (core->sfr.device_addr) {
> + value = (u32)core->sfr.device_addr + VIDEO_ARCH_LX;
> + ret = write_register(core, SFR_ADDR_IRIS3, value);
> + if (ret)
> + return ret;
you're returning ret 3 lines below anyway
> + }
> +
> + return ret;
> +}
> +
> +static int boot_firmware_iris3(struct iris_core *core)
> +{
> + u32 ctrl_init = 0, ctrl_status = 0, count = 0, max_tries = 1000;
> + int ret;
> +
> + ret = setup_ucregion_memory_map_iris3(core);
> + if (ret)
> + return ret;
> +
> + ctrl_init = BIT(0);
this should be a named #define used inline
> +
> + ret = write_register(core, CTRL_INIT_IRIS3, ctrl_init);
> + if (ret)
> + return ret;
> +
> + while (!ctrl_status && count < max_tries) {
if you take the previous feedback into account, this can become
readl_poll_timeout()
> + ret = read_register(core, CTRL_STATUS_IRIS3, &ctrl_status);
> + if (ret)
> + return ret;
> +
> + if ((ctrl_status & CTRL_ERROR_STATUS__M_IRIS3) == 0x4) {
> + dev_err(core->dev, "invalid setting for UC_REGION\n");
> + break;
> + }
> +
> + usleep_range(50, 100);
> + count++;
> + }
> +
> + if (count >= max_tries) {
> + dev_err(core->dev, "Error booting up vidc firmware\n");
> + return -ETIME;
> + }
> +
> + ret = write_register(core, CPU_CS_H2XSOFTINTEN_IRIS3, 0x1);
0x1? BIT(0)? probably a named BIT(0) that deserves its own #define?
> + if (ret)
> + return ret;
> +
> + ret = write_register(core, CPU_CS_X2RPMH_IRIS3, 0x0);
similarly here
btw you can just return this
> +
> + return ret;
> +}
> +
> +static int raise_interrupt_iris3(struct iris_core *core)
> +{
> + return write_register(core, CPU_IC_SOFTINT_IRIS3, 1 << CPU_IC_SOFTINT_H2A_SHFT_IRIS3);
FIELD_PREP/GET, please
> +}
> +
> +static const struct vpu_ops iris3_ops = {
> + .boot_firmware = boot_firmware_iris3,
> + .raise_interrupt = raise_interrupt_iris3,
> +};
> +
> +int init_iris3(struct iris_core *core)
> +{
> + core->vpu_ops = &iris3_ops;
> +
> + return 0;
> +}
what is this dead function for?
Konrad
@@ -7,6 +7,8 @@ iris-objs += iris_probe.o \
iris_helpers.o \
iris_hfi.o \
iris_hfi_packet.o \
- resources.o
+ resources.o \
+ vpu_common.o \
+ vpu_iris3.o
obj-$(CONFIG_VIDEO_QCOM_IRIS) += iris.o
@@ -12,6 +12,7 @@
#include "../hfi_queue.h"
#include "iris_state.h"
#include "resources.h"
+#include "vpu_common.h"
/**
* struct iris_core - holds core parameters valid for all instances
@@ -44,6 +45,7 @@
* @sys_init_id: id of sys init packet
* @header_id: id of packet header
* @packet_id: id of packet
+ * @vpu_ops: a pointer to vpu ops
*/
struct iris_core {
@@ -75,6 +77,7 @@ struct iris_core {
u32 sys_init_id;
u32 header_id;
u32 packet_id;
+ const struct vpu_ops *vpu_ops;
};
int iris_core_init(struct iris_core *core);
@@ -8,6 +8,7 @@
#include "iris_helpers.h"
#include "iris_hfi.h"
#include "iris_hfi_packet.h"
+#include "vpu_common.h"
static int iris_hfi_queue_cmd_write(struct iris_core *core, void *pkt)
{
@@ -33,6 +34,8 @@ static int iris_hfi_queue_cmd_write(struct iris_core *core, void *pkt)
packet_size = header->size;
if (!write_queue(q_info, pkt, packet_size, &rx_req)) {
+ call_vpu_op(core, raise_interrupt, core);
+ } else {
dev_err(core->dev, "queue full\n");
return -ENODATA;
}
@@ -108,6 +111,10 @@ int iris_hfi_core_init(struct iris_core *core)
if (ret)
goto error;
+ ret = call_vpu_op(core, boot_firmware, core);
+ if (ret)
+ goto error;
+
ret = sys_init(core);
if (ret)
goto error;
@@ -92,6 +92,13 @@ static int iris_probe(struct platform_device *pdev)
if (core->irq < 0)
return core->irq;
+ ret = init_vpu(core);
+ if (ret) {
+ dev_err_probe(core->dev, ret,
+ "%s: init vpu failed with %d\n", __func__, ret);
+ return ret;
+ }
+
ret = init_resources(core);
if (ret) {
dev_err_probe(core->dev, ret,
new file mode 100644
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "vpu_iris3.h"
+#include "iris_core.h"
+#include "iris_helpers.h"
+#include "vpu_common.h"
+
+int write_register(struct iris_core *core, u32 reg, u32 value)
+{
+ void __iomem *base_addr;
+ int ret;
+
+ ret = check_core_lock(core);
+ if (ret)
+ return ret;
+
+ base_addr = core->reg_base;
+ base_addr += reg;
+ writel_relaxed(value, base_addr);
+
+ /* Make sure value is written into the register */
+ wmb();
+
+ return ret;
+}
+
+int read_register(struct iris_core *core, u32 reg, u32 *value)
+{
+ void __iomem *base_addr;
+
+ base_addr = core->reg_base;
+
+ *value = readl_relaxed(base_addr + reg);
+
+ /* Make sure value is read correctly from the register */
+ rmb();
+
+ return 0;
+}
+
+static const struct compat_handle compat_handle[] = {
+ {
+ .compat = "qcom,sm8550-iris",
+ .init = init_iris3,
+ },
+};
+
+int init_vpu(struct iris_core *core)
+{
+ struct device *dev = NULL;
+ int i, ret = 0;
+
+ dev = core->dev;
+
+ for (i = 0; i < ARRAY_SIZE(compat_handle); i++) {
+ if (of_device_is_compatible(dev->of_node, compat_handle[i].compat)) {
+ ret = compat_handle[i].init(core);
+ if (ret)
+ return ret;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(compat_handle))
+ return -EINVAL;
+
+ return ret;
+}
new file mode 100644
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _VPU_COMMON_H_
+#define _VPU_COMMON_H_
+
+#include <linux/types.h>
+
+struct iris_core;
+
+#define call_vpu_op(d, op, ...) \
+ (((d) && (d)->vpu_ops && (d)->vpu_ops->op) ? \
+ ((d)->vpu_ops->op(__VA_ARGS__)) : 0)
+
+struct compat_handle {
+ const char *compat;
+ int (*init)(struct iris_core *core);
+};
+
+struct vpu_ops {
+ int (*boot_firmware)(struct iris_core *core);
+ int (*raise_interrupt)(struct iris_core *core);
+};
+
+int init_vpu(struct iris_core *core);
+
+int write_register(struct iris_core *core, u32 reg, u32 value);
+int read_register(struct iris_core *core, u32 reg, u32 *value);
+
+#endif
new file mode 100644
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/delay.h>
+
+#include "vpu_iris3.h"
+
+#define VIDEO_ARCH_LX 1
+
+#define CPU_BASE_OFFS_IRIS3 0x000A0000
+
+#define CPU_CS_BASE_OFFS_IRIS3 (CPU_BASE_OFFS_IRIS3)
+#define CPU_IC_BASE_OFFS_IRIS3 (CPU_BASE_OFFS_IRIS3)
+
+#define CPU_CS_VCICMDARG0_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x24)
+#define CPU_CS_VCICMDARG1_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x28)
+
+/* HFI_CTRL_INIT */
+#define CPU_CS_SCIACMD_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x48)
+
+/* HFI_CTRL_STATUS */
+#define CPU_CS_SCIACMDARG0_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x4C)
+#define CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK_IRIS3 0x40000000
+
+#define CPU_CS_H2XSOFTINTEN_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x148)
+
+#define CPU_CS_X2RPMH_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x168)
+
+/* UC_REGION_ADDR */
+#define CPU_CS_SCIBARG1_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x64)
+
+/* UC_REGION_ADDR */
+#define CPU_CS_SCIBARG2_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x68)
+
+/* HFI_QTBL_INFO */
+#define CPU_CS_SCIACMDARG1_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x50)
+
+/* HFI_QTBL_ADDR */
+#define CPU_CS_SCIACMDARG2_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x54)
+
+/* SFR_ADDR */
+#define CPU_CS_SCIBCMD_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x5C)
+
+#define UC_REGION_ADDR_IRIS3 CPU_CS_SCIBARG1_IRIS3
+#define UC_REGION_SIZE_IRIS3 CPU_CS_SCIBARG2_IRIS3
+
+#define QTBL_INFO_IRIS3 CPU_CS_SCIACMDARG1_IRIS3
+#define QTBL_ADDR_IRIS3 CPU_CS_SCIACMDARG2_IRIS3
+
+#define SFR_ADDR_IRIS3 CPU_CS_SCIBCMD_IRIS3
+
+#define CTRL_INIT_IRIS3 CPU_CS_SCIACMD_IRIS3
+
+#define CTRL_STATUS_IRIS3 CPU_CS_SCIACMDARG0_IRIS3
+#define CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK_IRIS3 0xfe
+#define CTRL_ERROR_STATUS__M_IRIS3 \
+ CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK_IRIS3
+
+#define CPU_IC_SOFTINT_IRIS3 (CPU_IC_BASE_OFFS_IRIS3 + 0x150)
+#define CPU_IC_SOFTINT_H2A_SHFT_IRIS3 0x0
+
+static int setup_ucregion_memory_map_iris3(struct iris_core *core)
+{
+ int ret;
+ u32 value;
+
+ value = (u32)core->iface_q_table.device_addr;
+ ret = write_register(core, UC_REGION_ADDR_IRIS3, value);
+ if (ret)
+ return ret;
+
+ value = SHARED_QSIZE;
+ ret = write_register(core, UC_REGION_SIZE_IRIS3, value);
+ if (ret)
+ return ret;
+
+ value = (u32)core->iface_q_table.device_addr;
+ ret = write_register(core, QTBL_ADDR_IRIS3, value);
+ if (ret)
+ return ret;
+
+ ret = write_register(core, QTBL_INFO_IRIS3, 0x01);
+ if (ret)
+ return ret;
+
+ value = (u32)((u64)core->iface_q_table.kernel_vaddr);
+ ret = write_register(core, CPU_CS_VCICMDARG0_IRIS3, value);
+ if (ret)
+ return ret;
+
+ value = (u32)((u64)core->iface_q_table.kernel_vaddr >> 32);
+ ret = write_register(core, CPU_CS_VCICMDARG1_IRIS3, value);
+ if (ret)
+ return ret;
+
+ if (core->sfr.device_addr) {
+ value = (u32)core->sfr.device_addr + VIDEO_ARCH_LX;
+ ret = write_register(core, SFR_ADDR_IRIS3, value);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int boot_firmware_iris3(struct iris_core *core)
+{
+ u32 ctrl_init = 0, ctrl_status = 0, count = 0, max_tries = 1000;
+ int ret;
+
+ ret = setup_ucregion_memory_map_iris3(core);
+ if (ret)
+ return ret;
+
+ ctrl_init = BIT(0);
+
+ ret = write_register(core, CTRL_INIT_IRIS3, ctrl_init);
+ if (ret)
+ return ret;
+
+ while (!ctrl_status && count < max_tries) {
+ ret = read_register(core, CTRL_STATUS_IRIS3, &ctrl_status);
+ if (ret)
+ return ret;
+
+ if ((ctrl_status & CTRL_ERROR_STATUS__M_IRIS3) == 0x4) {
+ dev_err(core->dev, "invalid setting for UC_REGION\n");
+ break;
+ }
+
+ usleep_range(50, 100);
+ count++;
+ }
+
+ if (count >= max_tries) {
+ dev_err(core->dev, "Error booting up vidc firmware\n");
+ return -ETIME;
+ }
+
+ ret = write_register(core, CPU_CS_H2XSOFTINTEN_IRIS3, 0x1);
+ if (ret)
+ return ret;
+
+ ret = write_register(core, CPU_CS_X2RPMH_IRIS3, 0x0);
+
+ return ret;
+}
+
+static int raise_interrupt_iris3(struct iris_core *core)
+{
+ return write_register(core, CPU_IC_SOFTINT_IRIS3, 1 << CPU_IC_SOFTINT_H2A_SHFT_IRIS3);
+}
+
+static const struct vpu_ops iris3_ops = {
+ .boot_firmware = boot_firmware_iris3,
+ .raise_interrupt = raise_interrupt_iris3,
+};
+
+int init_iris3(struct iris_core *core)
+{
+ core->vpu_ops = &iris3_ops;
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _VPU_IRIS3_H_
+#define _VPU_IRIS3_H_
+
+#include "iris_core.h"
+
+int init_iris3(struct iris_core *core);
+
+#endif