6aa0829104
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>
211 lines
4.7 KiB
C
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));
|
|
}
|