1
linux/drivers/media/platform/chips-media/wave5/wave5-vdi.c
Ivan Bornyakov 6aa0829104 media: chips-media: wave5: support Wave515 decoder
Add initial support for the Wave515 multi-decoder IP. For now it is only
able to decode HEVC Main/Main10 profile videos into YUV420.

This was tested on FPGA prototype, so wave5_dt_ids[] was not expanded.
Users of the real hardware with Wave515 IP will have to
 * provide firmware specific to their SoC
 * add struct wave5_match_data like this:

	static const struct wave5_match_data platform_name_wave515_data = {
		.flags = WAVE5_IS_DEC,
		.fw_name = "cnm/wave515_platform_name_fw.bin",
		.sram_size = (71 * 1024),
	};

 * add item to wave5_dt_ids[] like this:

	{
		.compatible = "vendor,soc-wave515",
		.data = &platform_name_wave515_data,
	},

 * describe new compatible in
   Documentation/devicetree/bindings/media/cnm,wave521c.yaml

Signed-off-by: Ivan Bornyakov <brnkv.i1@gmail.com>
Signed-off-by: Sebastian Fricke <sebastian.fricke@collabora.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
2024-06-21 10:15:21 +02:00

211 lines
4.7 KiB
C

// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
* Wave5 series multi-standard codec IP - low level access functions
*
* Copyright (C) 2021-2023 CHIPS&MEDIA INC
*/
#include <linux/bug.h>
#include "wave5-vdi.h"
#include "wave5-vpu.h"
#include "wave5-regdefine.h"
#include <linux/delay.h>
static int wave5_vdi_allocate_common_memory(struct device *dev)
{
struct vpu_device *vpu_dev = dev_get_drvdata(dev);
if (!vpu_dev->common_mem.vaddr) {
int ret;
if (vpu_dev->product_code == WAVE515_CODE)
vpu_dev->common_mem.size = WAVE515_SIZE_COMMON;
else
vpu_dev->common_mem.size = WAVE521_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, "[VDI] 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 ret;
ret = wave5_vdi_allocate_common_memory(dev);
if (ret < 0) {
dev_err(dev, "[VDI] failed to get vpu common buffer from driver\n");
return ret;
}
if (!PRODUCT_CODE_W_SERIES(vpu_dev->product_code)) {
WARN_ONCE(1, "unsupported product code: 0x%x\n", vpu_dev->product_code);
return -EOPNOTSUPP;
}
/* if BIT processor is not running. */
if (wave5_vdi_read_register(vpu_dev, W5_VCPU_CUR_PC) == 0) {
int i;
for (i = 0; i < 64; i++)
wave5_vdi_write_register(vpu_dev, (i * 4) + 0x100, 0x0);
}
dev_dbg(dev, "[VDI] driver initialized successfully\n");
return 0;
}
int wave5_vdi_release(struct device *dev)
{
struct vpu_device *vpu_dev = dev_get_drvdata(dev);
vpu_dev->vdb_register = NULL;
wave5_vdi_free_dma_memory(vpu_dev, &vpu_dev->common_mem);
return 0;
}
void wave5_vdi_write_register(struct vpu_device *vpu_dev, u32 addr, u32 data)
{
writel(data, vpu_dev->vdb_register + addr);
}
unsigned int wave5_vdi_read_register(struct vpu_device *vpu_dev, u32 addr)
{
return readl(vpu_dev->vdb_register + 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;
}
int wave5_vdi_write_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb, size_t offset,
u8 *data, size_t len)
{
if (!vb || !vb->vaddr) {
dev_err(vpu_dev->dev, "%s: unable to write to unmapped buffer\n", __func__);
return -EINVAL;
}
if (offset > vb->size || len > vb->size || offset + len > vb->size) {
dev_err(vpu_dev->dev, "%s: buffer too small\n", __func__);
return -ENOSPC;
}
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;
}
int wave5_vdi_free_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb)
{
if (vb->size == 0)
return -EINVAL;
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));
return 0;
}
int wave5_vdi_allocate_array(struct vpu_device *vpu_dev, struct vpu_buf *array, unsigned int count,
size_t size)
{
struct vpu_buf vb_buf;
int i, ret = 0;
vb_buf.size = size;
for (i = 0; i < count; i++) {
if (array[i].size == size)
continue;
if (array[i].size != 0)
wave5_vdi_free_dma_memory(vpu_dev, &array[i]);
ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_buf);
if (ret)
return -ENOMEM;
array[i] = vb_buf;
}
for (i = count; i < MAX_REG_FRAME; i++)
wave5_vdi_free_dma_memory(vpu_dev, &array[i]);
return 0;
}
void wave5_vdi_allocate_sram(struct vpu_device *vpu_dev)
{
struct vpu_buf *vb = &vpu_dev->sram_buf;
dma_addr_t daddr;
void *vaddr;
size_t size;
if (!vpu_dev->sram_pool || vb->vaddr)
return;
size = min_t(size_t, vpu_dev->sram_size, gen_pool_avail(vpu_dev->sram_pool));
vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, size, &daddr);
if (vaddr) {
vb->vaddr = vaddr;
vb->daddr = daddr;
vb->size = size;
}
dev_dbg(vpu_dev->dev, "%s: sram daddr: %pad, size: %zu, vaddr: 0x%p\n",
__func__, &vb->daddr, vb->size, vb->vaddr);
}
void wave5_vdi_free_sram(struct vpu_device *vpu_dev)
{
struct vpu_buf *vb = &vpu_dev->sram_buf;
if (!vb->size || !vb->vaddr)
return;
gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr, vb->size);
memset(vb, 0, sizeof(*vb));
}