2021-11-10 12:11:55

by Dafna Hirschfeld

[permalink] [raw]
Subject: [PATCH v3 2/6] staging: media: wave5: Add the vdi layer

Add the vdi directory of the wave5 codec driver.
The vdi.h header defines common helper functions
such as writing/reading register and handling endianness.

Signed-off-by: Robert Beckett <[email protected]>
Signed-off-by: Dafna Hirschfeld <[email protected]>
---
drivers/staging/media/wave5/vdi.c | 260 ++++++++++++++++++++++++++++++
drivers/staging/media/wave5/vdi.h | 76 +++++++++
2 files changed, 336 insertions(+)
create mode 100644 drivers/staging/media/wave5/vdi.c
create mode 100644 drivers/staging/media/wave5/vdi.h

diff --git a/drivers/staging/media/wave5/vdi.c b/drivers/staging/media/wave5/vdi.c
new file mode 100644
index 000000000000..6049ef76c948
--- /dev/null
+++ b/drivers/staging/media/wave5/vdi.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/*
+ * Wave5 series multi-standard codec IP - low level access functions
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+#include <linux/bug.h>
+#include "vdi.h"
+#include "vpu.h"
+#include "wave5_regdefine.h"
+#include <linux/delay.h>
+#define VDI_SRAM_BASE_ADDR 0x00
+
+#define VDI_SYSTEM_ENDIAN VDI_LITTLE_ENDIAN
+#define VDI_128BIT_BUS_SYSTEM_ENDIAN VDI_128BIT_LITTLE_ENDIAN
+
+static int wave5_vdi_allocate_common_memory(struct device *dev)
+{
+ int ret;
+ struct vpu_device *vpu_dev = dev_get_drvdata(dev);
+
+ if (!vpu_dev->common_mem.vaddr) {
+ vpu_dev->common_mem.size = SIZE_COMMON;
+ ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vpu_dev->common_mem);
+ if (ret) {
+ dev_err(dev, "unable to allocate common buffer\n");
+ return ret;
+ }
+ }
+
+ dev_dbg(dev, "common_mem: daddr=%pad size=%zu vaddr=0x%p\n",
+ &vpu_dev->common_mem.daddr, vpu_dev->common_mem.size,
+ vpu_dev->common_mem.vaddr);
+
+ return 0;
+}
+
+int wave5_vdi_init(struct device *dev)
+{
+ struct vpu_device *vpu_dev = dev_get_drvdata(dev);
+ int i;
+ int ret;
+
+ ret = wave5_vdi_allocate_common_memory(dev);
+ if (ret < 0) {
+ dev_err(dev, "[VDI] fail to get vpu common buffer from driver\n");
+ return ret;
+ }
+
+ if (PRODUCT_CODE_W_SERIES(vpu_dev->product_code)) {
+ // if BIT processor is not running.
+ if (wave5_vdi_read_register(vpu_dev, W5_VCPU_CUR_PC) == 0) {
+ for (i = 0; i < 64; i++)
+ wave5_vdi_write_register(vpu_dev, (i * 4) + 0x100, 0x0);
+ }
+ } else {
+ WARN_ONCE(1, "unsupported product code 0x%x\n", vpu_dev->product_code);
+ }
+
+ dev_dbg(dev, "[VDI] success to init driver\n");
+
+ return 0;
+
+ wave5_vdi_release(dev);
+ return ret;
+}
+
+int wave5_vdi_release(struct device *dev)
+{
+ struct vpu_device *vpu_dev = dev_get_drvdata(dev);
+
+ memset(&vpu_dev->vdb_register, 0x00, sizeof(vpu_dev->vdb_register));
+ wave5_vdi_free_dma_memory(vpu_dev, &vpu_dev->common_mem);
+
+ return 0;
+}
+
+void wave5_vdi_write_register(struct vpu_device *vpu_dev, unsigned int addr, unsigned int data)
+{
+ writel(data, vpu_dev->vdb_register.vaddr + addr);
+}
+
+unsigned int wave5_vdi_read_register(struct vpu_device *vpu_dev, unsigned int addr)
+{
+ return readl(vpu_dev->vdb_register.vaddr + addr);
+}
+
+int wave5_vdi_clear_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb)
+{
+ if (!vb || !vb->vaddr) {
+ dev_err(vpu_dev->dev, "%s(): unable to clear unmapped buffer\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(vb->vaddr, 0, vb->size);
+ return vb->size;
+}
+
+static int wave5_swap_endian(struct vpu_device *vpu_dev, u8 *data, int len, int endian);
+
+int wave5_vdi_write_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb, size_t offset,
+ u8 *data, int len, int endian)
+{
+ if (!vb || !vb->vaddr) {
+ dev_err(vpu_dev->dev, "%s(): unable to write to unmapped buffer\n", __func__);
+ return -EINVAL;
+ }
+
+ if ((offset + len) > vb->size) {
+ dev_err(vpu_dev->dev, "%s(): buffer too small\n", __func__);
+ return -ENOSPC;
+ }
+
+ wave5_swap_endian(vpu_dev, data, len, endian);
+ memcpy(vb->vaddr + offset, data, len);
+
+ return len;
+}
+
+int wave5_vdi_allocate_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb)
+{
+ void *vaddr;
+ dma_addr_t daddr;
+
+ if (!vb->size) {
+ dev_err(vpu_dev->dev, "%s(): requested size==0\n", __func__);
+ return -EINVAL;
+ }
+
+ vaddr = dma_alloc_coherent(vpu_dev->dev, vb->size, &daddr, GFP_KERNEL);
+ if (!vaddr)
+ return -ENOMEM;
+ vb->vaddr = vaddr;
+ vb->daddr = daddr;
+
+ return 0;
+}
+
+void wave5_vdi_free_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb)
+{
+ if (vb->size == 0)
+ return;
+
+ if (!vb->vaddr)
+ dev_err(vpu_dev->dev, "%s(): requested free of unmapped buffer\n", __func__);
+ else
+ dma_free_coherent(vpu_dev->dev, vb->size, vb->vaddr, vb->daddr);
+
+ memset(vb, 0, sizeof(*vb));
+}
+
+int wave5_vdi_convert_endian(struct vpu_device *vpu_dev, unsigned int endian)
+{
+ if (PRODUCT_CODE_W_SERIES(vpu_dev->product_code)) {
+ switch (endian) {
+ case VDI_LITTLE_ENDIAN:
+ endian = 0x00;
+ break;
+ case VDI_BIG_ENDIAN:
+ endian = 0x0f;
+ break;
+ case VDI_32BIT_LITTLE_ENDIAN:
+ endian = 0x04;
+ break;
+ case VDI_32BIT_BIG_ENDIAN:
+ endian = 0x03;
+ break;
+ }
+ }
+
+ return (endian & 0x0f);
+}
+
+static void byte_swap(unsigned char *data, int len)
+{
+ u8 temp;
+ s32 i;
+
+ for (i = 0; i < len; i += 2) {
+ temp = data[i];
+ data[i] = data[i + 1];
+ data[i + 1] = temp;
+ }
+}
+
+static void word_swap(unsigned char *data, int len)
+{
+ u16 temp;
+ u16 *ptr = (uint16_t *)data;
+ s32 i, size = len / (int)sizeof(uint16_t);
+
+ for (i = 0; i < size; i += 2) {
+ temp = ptr[i];
+ ptr[i] = ptr[i + 1];
+ ptr[i + 1] = temp;
+ }
+}
+
+static void dword_swap(unsigned char *data, int len)
+{
+ u32 temp;
+ u32 *ptr = (uint32_t *)data;
+ s32 i, size = len / (int)sizeof(uint32_t);
+
+ for (i = 0; i < size; i += 2) {
+ temp = ptr[i];
+ ptr[i] = ptr[i + 1];
+ ptr[i + 1] = temp;
+ }
+}
+
+static void lword_swap(unsigned char *data, int len)
+{
+ u64 temp;
+ u64 *ptr = (uint64_t *)data;
+ s32 i, size = len / (int)sizeof(uint64_t);
+
+ for (i = 0; i < size; i += 2) {
+ temp = ptr[i];
+ ptr[i] = ptr[i + 1];
+ ptr[i + 1] = temp;
+ }
+}
+
+static int wave5_swap_endian(struct vpu_device *vpu_dev, u8 *data, int len, int endian)
+{
+ int changes;
+ int sys_endian;
+ bool byte_change, word_change, dword_change, lword_change;
+
+ if (PRODUCT_CODE_W_SERIES(vpu_dev->product_code)) {
+ sys_endian = VDI_128BIT_BUS_SYSTEM_ENDIAN;
+ } else {
+ dev_err(vpu_dev->dev, "unknown product id : %08x\n", vpu_dev->product_code);
+ return -1;
+ }
+
+ endian = wave5_vdi_convert_endian(vpu_dev, endian);
+ sys_endian = wave5_vdi_convert_endian(vpu_dev, sys_endian);
+ if (endian == sys_endian)
+ return 0;
+
+ changes = endian ^ sys_endian;
+ byte_change = changes & 0x01;
+ word_change = ((changes & 0x02) == 0x02);
+ dword_change = ((changes & 0x04) == 0x04);
+ lword_change = ((changes & 0x08) == 0x08);
+
+ if (byte_change)
+ byte_swap(data, len);
+ if (word_change)
+ word_swap(data, len);
+ if (dword_change)
+ dword_swap(data, len);
+ if (lword_change)
+ lword_swap(data, len);
+
+ return 1;
+}
+
diff --git a/drivers/staging/media/wave5/vdi.h b/drivers/staging/media/wave5/vdi.h
new file mode 100644
index 000000000000..e293891f0199
--- /dev/null
+++ b/drivers/staging/media/wave5/vdi.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Wave5 series multi-standard codec IP - low level access functions
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+
+#ifndef _VDI_H_
+#define _VDI_H_
+
+#include "vpuconfig.h"
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+/************************************************************************/
+/* COMMON REGISTERS */
+/************************************************************************/
+#define VPU_PRODUCT_CODE_REGISTER 0x1044
+
+/* system register write */
+#define vpu_write_reg(VPU_INST, ADDR, DATA) wave5_vdi_write_register(VPU_INST, ADDR, DATA)
+// system register read
+#define vpu_read_reg(CORE, ADDR) wave5_vdi_read_register(CORE, ADDR)
+
+struct vpu_buf {
+ size_t size;
+ dma_addr_t daddr;
+ void __iomem *vaddr;
+};
+
+enum endian_mode {
+ VDI_LITTLE_ENDIAN = 0, /* 64bit LE */
+ VDI_BIG_ENDIAN, /* 64bit BE */
+ VDI_32BIT_LITTLE_ENDIAN,
+ VDI_32BIT_BIG_ENDIAN,
+ /* WAVE PRODUCTS */
+ VDI_128BIT_LITTLE_ENDIAN = 16,
+ VDI_128BIT_LE_BYTE_SWAP,
+ VDI_128BIT_LE_WORD_SWAP,
+ VDI_128BIT_LE_WORD_BYTE_SWAP,
+ VDI_128BIT_LE_DWORD_SWAP,
+ VDI_128BIT_LE_DWORD_BYTE_SWAP,
+ VDI_128BIT_LE_DWORD_WORD_SWAP,
+ VDI_128BIT_LE_DWORD_WORD_BYTE_SWAP,
+ VDI_128BIT_BE_DWORD_WORD_BYTE_SWAP,
+ VDI_128BIT_BE_DWORD_WORD_SWAP,
+ VDI_128BIT_BE_DWORD_BYTE_SWAP,
+ VDI_128BIT_BE_DWORD_SWAP,
+ VDI_128BIT_BE_WORD_BYTE_SWAP,
+ VDI_128BIT_BE_WORD_SWAP,
+ VDI_128BIT_BE_BYTE_SWAP,
+ VDI_128BIT_BIG_ENDIAN = 31,
+ VDI_ENDIAN_MAX
+};
+
+#define VDI_128BIT_ENDIAN_MASK 0xf
+
+int wave5_vdi_init(struct device *dev);
+int wave5_vdi_release(struct device *dev); //this function may be called only at system off.
+
+/**
+ * @brief make clock stable before changing clock frequency
+ * @detail before invoking vdi_set_clock_freg caller MUST invoke vdi_ready_change_clock
+ * function.
+ * after changing clock frequency caller also invoke wave5_vdi_done_change_clock() function.
+ * @return 0 failure
+ * 1 success
+ */
+int wave5_vdi_ready_change_clock(unsigned long core_idx);
+int wave5_vdi_set_change_clock(unsigned long core_idx, unsigned long clock_mask);
+int wave5_vdi_done_change_clock(unsigned long core_idx);
+int wave5_vdi_buffer_sync(struct device *dev, struct vpu_buf *vb, int dir);
+
+#endif //#ifndef _VDI_H_
+
--
2.17.1


2021-11-12 10:31:02

by Dan Carpenter

[permalink] [raw]
Subject: Re: [PATCH v3 2/6] staging: media: wave5: Add the vdi layer


Why are normal kernel endianness/__swab32 routines not sufficient?

On Wed, Nov 10, 2021 at 02:09:06PM +0200, Dafna Hirschfeld wrote:
> Add the vdi directory of the wave5 codec driver.
> The vdi.h header defines common helper functions
> such as writing/reading register and handling endianness.
>
> Signed-off-by: Robert Beckett <[email protected]>
> Signed-off-by: Dafna Hirschfeld <[email protected]>
> ---
> drivers/staging/media/wave5/vdi.c | 260 ++++++++++++++++++++++++++++++
> drivers/staging/media/wave5/vdi.h | 76 +++++++++
> 2 files changed, 336 insertions(+)
> create mode 100644 drivers/staging/media/wave5/vdi.c
> create mode 100644 drivers/staging/media/wave5/vdi.h
>
> diff --git a/drivers/staging/media/wave5/vdi.c b/drivers/staging/media/wave5/vdi.c
> new file mode 100644
> index 000000000000..6049ef76c948
> --- /dev/null
> +++ b/drivers/staging/media/wave5/vdi.c
> @@ -0,0 +1,260 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> +/*
> + * Wave5 series multi-standard codec IP - low level access functions
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC
> + */
> +#include <linux/bug.h>
> +#include "vdi.h"
> +#include "vpu.h"
> +#include "wave5_regdefine.h"
> +#include <linux/delay.h>
> +#define VDI_SRAM_BASE_ADDR 0x00
> +
> +#define VDI_SYSTEM_ENDIAN VDI_LITTLE_ENDIAN
> +#define VDI_128BIT_BUS_SYSTEM_ENDIAN VDI_128BIT_LITTLE_ENDIAN
> +
> +static int wave5_vdi_allocate_common_memory(struct device *dev)
> +{
> + int ret;
> + struct vpu_device *vpu_dev = dev_get_drvdata(dev);
> +
> + if (!vpu_dev->common_mem.vaddr) {
> + vpu_dev->common_mem.size = SIZE_COMMON;
> + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vpu_dev->common_mem);
> + if (ret) {
> + dev_err(dev, "unable to allocate common buffer\n");
> + return ret;
> + }
> + }
> +
> + dev_dbg(dev, "common_mem: daddr=%pad size=%zu vaddr=0x%p\n",
> + &vpu_dev->common_mem.daddr, vpu_dev->common_mem.size,
> + vpu_dev->common_mem.vaddr);
> +
> + return 0;
> +}
> +
> +int wave5_vdi_init(struct device *dev)
> +{
> + struct vpu_device *vpu_dev = dev_get_drvdata(dev);
> + int i;
> + int ret;
> +
> + ret = wave5_vdi_allocate_common_memory(dev);
> + if (ret < 0) {
> + dev_err(dev, "[VDI] fail to get vpu common buffer from driver\n");
> + return ret;
> + }
> +
> + if (PRODUCT_CODE_W_SERIES(vpu_dev->product_code)) {
> + // if BIT processor is not running.
> + if (wave5_vdi_read_register(vpu_dev, W5_VCPU_CUR_PC) == 0) {
> + for (i = 0; i < 64; i++)
> + wave5_vdi_write_register(vpu_dev, (i * 4) + 0x100, 0x0);
> + }
> + } else {
> + WARN_ONCE(1, "unsupported product code 0x%x\n", vpu_dev->product_code);
> + }
> +
> + dev_dbg(dev, "[VDI] success to init driver\n");
> +
> + return 0;
> +
> + wave5_vdi_release(dev);
> + return ret;

Delete this dead code.

> +}
> +
> +int wave5_vdi_release(struct device *dev)
> +{
> + struct vpu_device *vpu_dev = dev_get_drvdata(dev);
> +
> + memset(&vpu_dev->vdb_register, 0x00, sizeof(vpu_dev->vdb_register));
> + wave5_vdi_free_dma_memory(vpu_dev, &vpu_dev->common_mem);
> +
> + return 0;
> +}
> +
> +void wave5_vdi_write_register(struct vpu_device *vpu_dev, unsigned int addr, unsigned int data)
> +{
> + writel(data, vpu_dev->vdb_register.vaddr + addr);
> +}
> +
> +unsigned int wave5_vdi_read_register(struct vpu_device *vpu_dev, unsigned int addr)
> +{
> + return readl(vpu_dev->vdb_register.vaddr + addr);
> +}
> +
> +int wave5_vdi_clear_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb)
> +{
> + if (!vb || !vb->vaddr) {
> + dev_err(vpu_dev->dev, "%s(): unable to clear unmapped buffer\n", __func__);
> + return -EINVAL;
> + }
> +
> + memset(vb->vaddr, 0, vb->size);
> + return vb->size;
> +}
> +
> +static int wave5_swap_endian(struct vpu_device *vpu_dev, u8 *data, int len, int endian);
> +
> +int wave5_vdi_write_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb, size_t offset,
> + u8 *data, int len, int endian)
> +{
> + if (!vb || !vb->vaddr) {
> + dev_err(vpu_dev->dev, "%s(): unable to write to unmapped buffer\n", __func__);
> + return -EINVAL;
> + }
> +
> + if ((offset + len) > vb->size) {

I wish there were an integer overflow check on this.

if (offset > vb->size || len > vb->size || offset + len > vb->size) {


> + dev_err(vpu_dev->dev, "%s(): buffer too small\n", __func__);
> + return -ENOSPC;
> + }
> +
> + wave5_swap_endian(vpu_dev, data, len, endian);
> + memcpy(vb->vaddr + offset, data, len);
> +
> + return len;
> +}
> +
> +int wave5_vdi_allocate_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb)
> +{
> + void *vaddr;
> + dma_addr_t daddr;
> +
> + if (!vb->size) {
> + dev_err(vpu_dev->dev, "%s(): requested size==0\n", __func__);
> + return -EINVAL;
> + }
> +
> + vaddr = dma_alloc_coherent(vpu_dev->dev, vb->size, &daddr, GFP_KERNEL);
> + if (!vaddr)
> + return -ENOMEM;
> + vb->vaddr = vaddr;
> + vb->daddr = daddr;
> +
> + return 0;
> +}
> +
> +void wave5_vdi_free_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb)
> +{
> + if (vb->size == 0)
> + return;
> +
> + if (!vb->vaddr)
> + dev_err(vpu_dev->dev, "%s(): requested free of unmapped buffer\n", __func__);
> + else
> + dma_free_coherent(vpu_dev->dev, vb->size, vb->vaddr, vb->daddr);
> +
> + memset(vb, 0, sizeof(*vb));
> +}
> +
> +int wave5_vdi_convert_endian(struct vpu_device *vpu_dev, unsigned int endian)
> +{
> + if (PRODUCT_CODE_W_SERIES(vpu_dev->product_code)) {
> + switch (endian) {
> + case VDI_LITTLE_ENDIAN:
> + endian = 0x00;
> + break;
> + case VDI_BIG_ENDIAN:
> + endian = 0x0f;
> + break;
> + case VDI_32BIT_LITTLE_ENDIAN:
> + endian = 0x04;
> + break;
> + case VDI_32BIT_BIG_ENDIAN:
> + endian = 0x03;
> + break;
> + }
> + }
> +
> + return (endian & 0x0f);
> +}
> +
> +static void byte_swap(unsigned char *data, int len)
> +{
> + u8 temp;
> + s32 i;

s32 is only for hardware specs. Use int.

> +
> + for (i = 0; i < len; i += 2) {
> + temp = data[i];
> + data[i] = data[i + 1];
> + data[i + 1] = temp;
> + }
> +}
> +
> +static void word_swap(unsigned char *data, int len)
> +{
> + u16 temp;
> + u16 *ptr = (uint16_t *)data;
> + s32 i, size = len / (int)sizeof(uint16_t);

No need for this cast.

> +
> + for (i = 0; i < size; i += 2) {
> + temp = ptr[i];
> + ptr[i] = ptr[i + 1];
> + ptr[i + 1] = temp;
> + }
> +}
> +
> +static void dword_swap(unsigned char *data, int len)
> +{
> + u32 temp;
> + u32 *ptr = (uint32_t *)data;

u32 * on both sides of the assign.

> + s32 i, size = len / (int)sizeof(uint32_t);
> +
> + for (i = 0; i < size; i += 2) {
> + temp = ptr[i];
> + ptr[i] = ptr[i + 1];
> + ptr[i + 1] = temp;
> + }
> +}
> +
> +static void lword_swap(unsigned char *data, int len)
> +{
> + u64 temp;
> + u64 *ptr = (uint64_t *)data;
> + s32 i, size = len / (int)sizeof(uint64_t);
> +
> + for (i = 0; i < size; i += 2) {
> + temp = ptr[i];
> + ptr[i] = ptr[i + 1];
> + ptr[i + 1] = temp;
> + }
> +}
> +
> +static int wave5_swap_endian(struct vpu_device *vpu_dev, u8 *data, int len, int endian)
> +{
> + int changes;
> + int sys_endian;
> + bool byte_change, word_change, dword_change, lword_change;
> +
> + if (PRODUCT_CODE_W_SERIES(vpu_dev->product_code)) {
> + sys_endian = VDI_128BIT_BUS_SYSTEM_ENDIAN;
> + } else {
> + dev_err(vpu_dev->dev, "unknown product id : %08x\n", vpu_dev->product_code);
> + return -1;

Use proper error codes. Except no one cares about the error codes so
just make it void.

> + }
> +
> + endian = wave5_vdi_convert_endian(vpu_dev, endian);
> + sys_endian = wave5_vdi_convert_endian(vpu_dev, sys_endian);
> + if (endian == sys_endian)
> + return 0;
> +
> + changes = endian ^ sys_endian;
> + byte_change = changes & 0x01;
> + word_change = ((changes & 0x02) == 0x02);
> + dword_change = ((changes & 0x04) == 0x04);
> + lword_change = ((changes & 0x08) == 0x08);
> +
> + if (byte_change)
> + byte_swap(data, len);
> + if (word_change)
> + word_swap(data, len);
> + if (dword_change)
> + dword_swap(data, len);
> + if (lword_change)
> + lword_swap(data, len);
> +
> + return 1;

If you decide to go this route, add a comment at the top of the function
explaining the return codes.

> +}

regards,
dan carpenter