| // 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)); |
| } |