Commit 9f599f35 authored by Ming Qian's avatar Ming Qian Committed by Hans Verkuil
Browse files

media: amphion: add vpu core driver



The vpu supports encoder and decoder.
it needs vpu core to handle it.
core will run either encoder or decoder firmware.

This driver is for support the vpu core.

Signed-off-by: default avatarMing Qian <ming.qian@nxp.com>
Signed-off-by: default avatarShijie Qin <shijie.qin@nxp.com>
Signed-off-by: default avatarZhou Peng <eagle.zhou@nxp.com>
Tested-by: default avatarNicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
parent b50a64fc
Loading
Loading
Loading
Loading
+68 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright 2020-2021 NXP
 */

#ifndef _AMPHION_VPU_CODEC_H
#define _AMPHION_VPU_CODEC_H

struct vpu_encode_params {
	u32 input_format;
	u32 codec_format;
	u32 profile;
	u32 tier;
	u32 level;
	struct v4l2_fract frame_rate;
	u32 src_stride;
	u32 src_width;
	u32 src_height;
	struct v4l2_rect crop;
	u32 out_width;
	u32 out_height;

	u32 gop_length;
	u32 bframes;

	u32 rc_enable;
	u32 rc_mode;
	u32 bitrate;
	u32 bitrate_min;
	u32 bitrate_max;

	u32 i_frame_qp;
	u32 p_frame_qp;
	u32 b_frame_qp;
	u32 qp_min;
	u32 qp_max;
	u32 qp_min_i;
	u32 qp_max_i;

	struct {
		u32 enable;
		u32 idc;
		u32 width;
		u32 height;
	} sar;

	struct {
		u32 primaries;
		u32 transfer;
		u32 matrix;
		u32 full_range;
	} color;
};

struct vpu_decode_params {
	u32 codec_format;
	u32 output_format;
	u32 b_dis_reorder;
	u32 b_non_frame;
	u32 frame_count;
	u32 end_flag;
	struct {
		u32 base;
		u32 size;
	} udata;
};

#endif
+871 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright 2020-2021 NXP
 */

#include <linux/init.h>
#include <linux/interconnect.h>
#include <linux/ioctl.h>
#include <linux/list.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
#include <linux/firmware.h>
#include <linux/vmalloc.h>
#include "vpu.h"
#include "vpu_defs.h"
#include "vpu_core.h"
#include "vpu_mbox.h"
#include "vpu_msgs.h"
#include "vpu_rpc.h"
#include "vpu_cmds.h"

void csr_writel(struct vpu_core *core, u32 reg, u32 val)
{
	writel(val, core->base + reg);
}

u32 csr_readl(struct vpu_core *core, u32 reg)
{
	return readl(core->base + reg);
}

static int vpu_core_load_firmware(struct vpu_core *core)
{
	const struct firmware *pfw = NULL;
	int ret = 0;

	if (!core->fw.virt) {
		dev_err(core->dev, "firmware buffer is not ready\n");
		return -EINVAL;
	}

	ret = request_firmware(&pfw, core->res->fwname, core->dev);
	dev_dbg(core->dev, "request_firmware %s : %d\n", core->res->fwname, ret);
	if (ret) {
		dev_err(core->dev, "request firmware %s failed, ret = %d\n",
			core->res->fwname, ret);
		return ret;
	}

	if (core->fw.length < pfw->size) {
		dev_err(core->dev, "firmware buffer size want %zu, but %d\n",
			pfw->size, core->fw.length);
		ret = -EINVAL;
		goto exit;
	}

	memset(core->fw.virt, 0, core->fw.length);
	memcpy(core->fw.virt, pfw->data, pfw->size);
	core->fw.bytesused = pfw->size;
	ret = vpu_iface_on_firmware_loaded(core);
exit:
	release_firmware(pfw);
	pfw = NULL;

	return ret;
}

static int vpu_core_boot_done(struct vpu_core *core)
{
	u32 fw_version;

	fw_version = vpu_iface_get_version(core);
	dev_info(core->dev, "%s firmware version : %d.%d.%d\n",
		 vpu_core_type_desc(core->type),
		 (fw_version >> 16) & 0xff,
		 (fw_version >> 8) & 0xff,
		 fw_version & 0xff);
	core->supported_instance_count = vpu_iface_get_max_instance_count(core);
	if (core->res->act_size) {
		u32 count = core->act.length / core->res->act_size;

		core->supported_instance_count = min(core->supported_instance_count, count);
	}
	core->fw_version = fw_version;
	core->state = VPU_CORE_ACTIVE;

	return 0;
}

static int vpu_core_wait_boot_done(struct vpu_core *core)
{
	int ret;

	ret = wait_for_completion_timeout(&core->cmp, VPU_TIMEOUT);
	if (!ret) {
		dev_err(core->dev, "boot timeout\n");
		return -EINVAL;
	}
	return vpu_core_boot_done(core);
}

static int vpu_core_boot(struct vpu_core *core, bool load)
{
	int ret;

	reinit_completion(&core->cmp);
	if (load) {
		ret = vpu_core_load_firmware(core);
		if (ret)
			return ret;
	}

	vpu_iface_boot_core(core);
	return vpu_core_wait_boot_done(core);
}

static int vpu_core_shutdown(struct vpu_core *core)
{
	return vpu_iface_shutdown_core(core);
}

static int vpu_core_restore(struct vpu_core *core)
{
	int ret;

	ret = vpu_core_sw_reset(core);
	if (ret)
		return ret;

	vpu_core_boot_done(core);
	return vpu_iface_restore_core(core);
}

static int __vpu_alloc_dma(struct device *dev, struct vpu_buffer *buf)
{
	gfp_t gfp = GFP_KERNEL | GFP_DMA32;

	if (!buf->length)
		return 0;

	buf->virt = dma_alloc_coherent(dev, buf->length, &buf->phys, gfp);
	if (!buf->virt)
		return -ENOMEM;

	buf->dev = dev;

	return 0;
}

void vpu_free_dma(struct vpu_buffer *buf)
{
	if (!buf->virt || !buf->dev)
		return;

	dma_free_coherent(buf->dev, buf->length, buf->virt, buf->phys);
	buf->virt = NULL;
	buf->phys = 0;
	buf->length = 0;
	buf->bytesused = 0;
	buf->dev = NULL;
}

int vpu_alloc_dma(struct vpu_core *core, struct vpu_buffer *buf)
{
	return __vpu_alloc_dma(core->dev, buf);
}

static void vpu_core_check_hang(struct vpu_core *core)
{
	if (core->hang_mask)
		core->state = VPU_CORE_HANG;
}

static struct vpu_core *vpu_core_find_proper_by_type(struct vpu_dev *vpu, u32 type)
{
	struct vpu_core *core = NULL;
	int request_count = INT_MAX;
	struct vpu_core *c;

	list_for_each_entry(c, &vpu->cores, list) {
		dev_dbg(c->dev, "instance_mask = 0x%lx, state = %d\n", c->instance_mask, c->state);
		if (c->type != type)
			continue;
		if (c->state == VPU_CORE_DEINIT) {
			core = c;
			break;
		}
		vpu_core_check_hang(c);
		if (c->state != VPU_CORE_ACTIVE)
			continue;
		if (c->request_count < request_count) {
			request_count = c->request_count;
			core = c;
		}
		if (!request_count)
			break;
	}

	return core;
}

static bool vpu_core_is_exist(struct vpu_dev *vpu, struct vpu_core *core)
{
	struct vpu_core *c;

	list_for_each_entry(c, &vpu->cores, list) {
		if (c == core)
			return true;
	}

	return false;
}

static void vpu_core_get_vpu(struct vpu_core *core)
{
	core->vpu->get_vpu(core->vpu);
	if (core->type == VPU_CORE_TYPE_ENC)
		core->vpu->get_enc(core->vpu);
	if (core->type == VPU_CORE_TYPE_DEC)
		core->vpu->get_dec(core->vpu);
}

static int vpu_core_register(struct device *dev, struct vpu_core *core)
{
	struct vpu_dev *vpu = dev_get_drvdata(dev);
	int ret = 0;

	dev_dbg(core->dev, "register core %s\n", vpu_core_type_desc(core->type));
	if (vpu_core_is_exist(vpu, core))
		return 0;

	core->workqueue = alloc_workqueue("vpu", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
	if (!core->workqueue) {
		dev_err(core->dev, "fail to alloc workqueue\n");
		return -ENOMEM;
	}
	INIT_WORK(&core->msg_work, vpu_msg_run_work);
	INIT_DELAYED_WORK(&core->msg_delayed_work, vpu_msg_delayed_work);
	core->msg_buffer_size = roundup_pow_of_two(VPU_MSG_BUFFER_SIZE);
	core->msg_buffer = vzalloc(core->msg_buffer_size);
	if (!core->msg_buffer) {
		dev_err(core->dev, "failed allocate buffer for fifo\n");
		ret = -ENOMEM;
		goto error;
	}
	ret = kfifo_init(&core->msg_fifo, core->msg_buffer, core->msg_buffer_size);
	if (ret) {
		dev_err(core->dev, "failed init kfifo\n");
		goto error;
	}

	list_add_tail(&core->list, &vpu->cores);

	vpu_core_get_vpu(core);

	if (vpu_iface_get_power_state(core))
		ret = vpu_core_restore(core);
	if (ret)
		goto error;

	return 0;
error:
	if (core->msg_buffer) {
		vfree(core->msg_buffer);
		core->msg_buffer = NULL;
	}
	if (core->workqueue) {
		destroy_workqueue(core->workqueue);
		core->workqueue = NULL;
	}
	return ret;
}

static void vpu_core_put_vpu(struct vpu_core *core)
{
	if (core->type == VPU_CORE_TYPE_ENC)
		core->vpu->put_enc(core->vpu);
	if (core->type == VPU_CORE_TYPE_DEC)
		core->vpu->put_dec(core->vpu);
	core->vpu->put_vpu(core->vpu);
}

static int vpu_core_unregister(struct device *dev, struct vpu_core *core)
{
	list_del_init(&core->list);

	vpu_core_put_vpu(core);
	core->vpu = NULL;
	vfree(core->msg_buffer);
	core->msg_buffer = NULL;

	if (core->workqueue) {
		cancel_work_sync(&core->msg_work);
		cancel_delayed_work_sync(&core->msg_delayed_work);
		destroy_workqueue(core->workqueue);
		core->workqueue = NULL;
	}

	return 0;
}

static int vpu_core_acquire_instance(struct vpu_core *core)
{
	int id;

	id = ffz(core->instance_mask);
	if (id >= core->supported_instance_count)
		return -EINVAL;

	set_bit(id, &core->instance_mask);

	return id;
}

static void vpu_core_release_instance(struct vpu_core *core, int id)
{
	if (id < 0 || id >= core->supported_instance_count)
		return;

	clear_bit(id, &core->instance_mask);
}

struct vpu_inst *vpu_inst_get(struct vpu_inst *inst)
{
	if (!inst)
		return NULL;

	atomic_inc(&inst->ref_count);

	return inst;
}

void vpu_inst_put(struct vpu_inst *inst)
{
	if (!inst)
		return;
	if (atomic_dec_and_test(&inst->ref_count)) {
		if (inst->release)
			inst->release(inst);
	}
}

struct vpu_core *vpu_request_core(struct vpu_dev *vpu, enum vpu_core_type type)
{
	struct vpu_core *core = NULL;
	int ret;

	mutex_lock(&vpu->lock);

	core = vpu_core_find_proper_by_type(vpu, type);
	if (!core)
		goto exit;

	mutex_lock(&core->lock);
	pm_runtime_get_sync(core->dev);

	if (core->state == VPU_CORE_DEINIT) {
		ret = vpu_core_boot(core, true);
		if (ret) {
			pm_runtime_put_sync(core->dev);
			mutex_unlock(&core->lock);
			core = NULL;
			goto exit;
		}
	}

	core->request_count++;

	mutex_unlock(&core->lock);
exit:
	mutex_unlock(&vpu->lock);

	return core;
}

void vpu_release_core(struct vpu_core *core)
{
	if (!core)
		return;

	mutex_lock(&core->lock);
	pm_runtime_put_sync(core->dev);
	if (core->request_count)
		core->request_count--;
	mutex_unlock(&core->lock);
}

int vpu_inst_register(struct vpu_inst *inst)
{
	struct vpu_dev *vpu;
	struct vpu_core *core;
	int ret = 0;

	vpu = inst->vpu;
	core = inst->core;
	if (!core) {
		core = vpu_request_core(vpu, inst->type);
		if (!core) {
			dev_err(vpu->dev, "there is no vpu core for %s\n",
				vpu_core_type_desc(inst->type));
			return -EINVAL;
		}
		inst->core = core;
		inst->dev = get_device(core->dev);
	}

	mutex_lock(&core->lock);
	if (inst->id >= 0 && inst->id < core->supported_instance_count)
		goto exit;

	ret = vpu_core_acquire_instance(core);
	if (ret < 0)
		goto exit;

	vpu_trace(inst->dev, "[%d] %p\n", ret, inst);
	inst->id = ret;
	list_add_tail(&inst->list, &core->instances);
	ret = 0;
	if (core->res->act_size) {
		inst->act.phys = core->act.phys + core->res->act_size * inst->id;
		inst->act.virt = core->act.virt + core->res->act_size * inst->id;
		inst->act.length = core->res->act_size;
	}
	vpu_inst_create_dbgfs_file(inst);
exit:
	mutex_unlock(&core->lock);

	if (ret)
		dev_err(core->dev, "register instance fail\n");
	return ret;
}

int vpu_inst_unregister(struct vpu_inst *inst)
{
	struct vpu_core *core;

	if (!inst->core)
		return 0;

	core = inst->core;
	vpu_clear_request(inst);
	mutex_lock(&core->lock);
	if (inst->id >= 0 && inst->id < core->supported_instance_count) {
		vpu_inst_remove_dbgfs_file(inst);
		list_del_init(&inst->list);
		vpu_core_release_instance(core, inst->id);
		inst->id = VPU_INST_NULL_ID;
	}
	vpu_core_check_hang(core);
	if (core->state == VPU_CORE_HANG && !core->instance_mask) {
		dev_info(core->dev, "reset hang core\n");
		if (!vpu_core_sw_reset(core)) {
			core->state = VPU_CORE_ACTIVE;
			core->hang_mask = 0;
		}
	}
	mutex_unlock(&core->lock);

	return 0;
}

struct vpu_inst *vpu_core_find_instance(struct vpu_core *core, u32 index)
{
	struct vpu_inst *inst = NULL;
	struct vpu_inst *tmp;

	mutex_lock(&core->lock);
	if (!test_bit(index, &core->instance_mask))
		goto exit;
	list_for_each_entry(tmp, &core->instances, list) {
		if (tmp->id == index) {
			inst = vpu_inst_get(tmp);
			break;
		}
	}
exit:
	mutex_unlock(&core->lock);

	return inst;
}

const struct vpu_core_resources *vpu_get_resource(struct vpu_inst *inst)
{
	struct vpu_dev *vpu;
	struct vpu_core *core = NULL;
	const struct vpu_core_resources *res = NULL;

	if (!inst || !inst->vpu)
		return NULL;

	if (inst->core && inst->core->res)
		return inst->core->res;

	vpu = inst->vpu;
	mutex_lock(&vpu->lock);
	list_for_each_entry(core, &vpu->cores, list) {
		if (core->type == inst->type) {
			res = core->res;
			break;
		}
	}
	mutex_unlock(&vpu->lock);

	return res;
}

static int vpu_core_parse_dt(struct vpu_core *core, struct device_node *np)
{
	struct device_node *node;
	struct resource res;
	int ret;

	if (of_count_phandle_with_args(np, "memory-region", NULL) < 2) {
		dev_err(core->dev, "need 2 memory-region for boot and rpc\n");
		return -ENODEV;
	}

	node = of_parse_phandle(np, "memory-region", 0);
	if (!node) {
		dev_err(core->dev, "boot-region of_parse_phandle error\n");
		return -ENODEV;
	}
	if (of_address_to_resource(node, 0, &res)) {
		dev_err(core->dev, "boot-region of_address_to_resource error\n");
		return -EINVAL;
	}
	core->fw.phys = res.start;
	core->fw.length = resource_size(&res);

	node = of_parse_phandle(np, "memory-region", 1);
	if (!node) {
		dev_err(core->dev, "rpc-region of_parse_phandle error\n");
		return -ENODEV;
	}
	if (of_address_to_resource(node, 0, &res)) {
		dev_err(core->dev, "rpc-region of_address_to_resource error\n");
		return -EINVAL;
	}
	core->rpc.phys = res.start;
	core->rpc.length = resource_size(&res);

	if (core->rpc.length < core->res->rpc_size + core->res->fwlog_size) {
		dev_err(core->dev, "the rpc-region <%pad, 0x%x> is not enough\n",
			&core->rpc.phys, core->rpc.length);
		return -EINVAL;
	}

	core->fw.virt = memremap(core->fw.phys, core->fw.length, MEMREMAP_WC);
	core->rpc.virt = memremap(core->rpc.phys, core->rpc.length, MEMREMAP_WC);
	memset(core->rpc.virt, 0, core->rpc.length);

	ret = vpu_iface_check_memory_region(core, core->rpc.phys, core->rpc.length);
	if (ret != VPU_CORE_MEMORY_UNCACHED) {
		dev_err(core->dev, "rpc region<%pad, 0x%x> isn't uncached\n",
			&core->rpc.phys, core->rpc.length);
		return -EINVAL;
	}

	core->log.phys = core->rpc.phys + core->res->rpc_size;
	core->log.virt = core->rpc.virt + core->res->rpc_size;
	core->log.length = core->res->fwlog_size;
	core->act.phys = core->log.phys + core->log.length;
	core->act.virt = core->log.virt + core->log.length;
	core->act.length = core->rpc.length - core->res->rpc_size - core->log.length;
	core->rpc.length = core->res->rpc_size;

	return 0;
}

static int vpu_core_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct vpu_core *core;
	struct vpu_dev *vpu = dev_get_drvdata(dev->parent);
	struct vpu_shared_addr *iface;
	u32 iface_data_size;
	int ret;

	dev_dbg(dev, "probe\n");
	if (!vpu)
		return -EINVAL;
	core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL);
	if (!core)
		return -ENOMEM;

	core->pdev = pdev;
	core->dev = dev;
	platform_set_drvdata(pdev, core);
	core->vpu = vpu;
	INIT_LIST_HEAD(&core->instances);
	mutex_init(&core->lock);
	mutex_init(&core->cmd_lock);
	init_completion(&core->cmp);
	init_waitqueue_head(&core->ack_wq);
	core->state = VPU_CORE_DEINIT;

	core->res = of_device_get_match_data(dev);
	if (!core->res)
		return -ENODEV;

	core->type = core->res->type;
	core->id = of_alias_get_id(dev->of_node, "vpu_core");
	if (core->id < 0) {
		dev_err(dev, "can't get vpu core id\n");
		return core->id;
	}
	dev_info(core->dev, "[%d] = %s\n", core->id, vpu_core_type_desc(core->type));
	ret = vpu_core_parse_dt(core, dev->of_node);
	if (ret)
		return ret;

	core->base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(core->base))
		return PTR_ERR(core->base);

	if (!vpu_iface_check_codec(core)) {
		dev_err(core->dev, "is not supported\n");
		return -EINVAL;
	}

	ret = vpu_mbox_init(core);
	if (ret)
		return ret;

	iface = devm_kzalloc(dev, sizeof(*iface), GFP_KERNEL);
	if (!iface)
		return -ENOMEM;

	iface_data_size = vpu_iface_get_data_size(core);
	if (iface_data_size) {
		iface->priv = devm_kzalloc(dev, iface_data_size, GFP_KERNEL);
		if (!iface->priv)
			return -ENOMEM;
	}

	ret = vpu_iface_init(core, iface, &core->rpc, core->fw.phys);
	if (ret) {
		dev_err(core->dev, "init iface fail, ret = %d\n", ret);
		return ret;
	}

	vpu_iface_config_system(core, vpu->res->mreg_base, vpu->base);
	vpu_iface_set_log_buf(core, &core->log);

	pm_runtime_enable(dev);
	ret = pm_runtime_get_sync(dev);
	if (ret) {
		pm_runtime_put_noidle(dev);
		pm_runtime_set_suspended(dev);
		goto err_runtime_disable;
	}

	ret = vpu_core_register(dev->parent, core);
	if (ret)
		goto err_core_register;
	core->parent = dev->parent;

	pm_runtime_put_sync(dev);
	vpu_core_create_dbgfs_file(core);

	return 0;

err_core_register:
	pm_runtime_put_sync(dev);
err_runtime_disable:
	pm_runtime_disable(dev);

	return ret;
}

static int vpu_core_remove(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct vpu_core *core = platform_get_drvdata(pdev);
	int ret;

	vpu_core_remove_dbgfs_file(core);
	ret = pm_runtime_get_sync(dev);
	WARN_ON(ret < 0);

	vpu_core_shutdown(core);
	pm_runtime_put_sync(dev);
	pm_runtime_disable(dev);

	vpu_core_unregister(core->parent, core);
	memunmap(core->fw.virt);
	memunmap(core->rpc.virt);
	mutex_destroy(&core->lock);
	mutex_destroy(&core->cmd_lock);

	return 0;
}

static int __maybe_unused vpu_core_runtime_resume(struct device *dev)
{
	struct vpu_core *core = dev_get_drvdata(dev);

	return vpu_mbox_request(core);
}

static int __maybe_unused vpu_core_runtime_suspend(struct device *dev)
{
	struct vpu_core *core = dev_get_drvdata(dev);

	vpu_mbox_free(core);
	return 0;
}

static void vpu_core_cancel_work(struct vpu_core *core)
{
	struct vpu_inst *inst = NULL;

	cancel_work_sync(&core->msg_work);
	cancel_delayed_work_sync(&core->msg_delayed_work);

	mutex_lock(&core->lock);
	list_for_each_entry(inst, &core->instances, list)
		cancel_work_sync(&inst->msg_work);
	mutex_unlock(&core->lock);
}

static void vpu_core_resume_work(struct vpu_core *core)
{
	struct vpu_inst *inst = NULL;
	unsigned long delay = msecs_to_jiffies(10);

	queue_work(core->workqueue, &core->msg_work);
	queue_delayed_work(core->workqueue, &core->msg_delayed_work, delay);

	mutex_lock(&core->lock);
	list_for_each_entry(inst, &core->instances, list)
		queue_work(inst->workqueue, &inst->msg_work);
	mutex_unlock(&core->lock);
}

static int __maybe_unused vpu_core_resume(struct device *dev)
{
	struct vpu_core *core = dev_get_drvdata(dev);
	int ret = 0;

	mutex_lock(&core->lock);
	pm_runtime_get_sync(dev);
	vpu_core_get_vpu(core);
	if (core->state != VPU_CORE_SNAPSHOT)
		goto exit;

	if (!vpu_iface_get_power_state(core)) {
		if (!list_empty(&core->instances)) {
			ret = vpu_core_boot(core, false);
			if (ret) {
				dev_err(core->dev, "%s boot fail\n", __func__);
				core->state = VPU_CORE_DEINIT;
				goto exit;
			}
		} else {
			core->state = VPU_CORE_DEINIT;
		}
	} else {
		if (!list_empty(&core->instances)) {
			ret = vpu_core_sw_reset(core);
			if (ret) {
				dev_err(core->dev, "%s sw_reset fail\n", __func__);
				core->state = VPU_CORE_HANG;
				goto exit;
			}
		}
		core->state = VPU_CORE_ACTIVE;
	}

exit:
	pm_runtime_put_sync(dev);
	mutex_unlock(&core->lock);

	vpu_core_resume_work(core);
	return ret;
}

static int __maybe_unused vpu_core_suspend(struct device *dev)
{
	struct vpu_core *core = dev_get_drvdata(dev);
	int ret = 0;

	mutex_lock(&core->lock);
	if (core->state == VPU_CORE_ACTIVE) {
		if (!list_empty(&core->instances)) {
			ret = vpu_core_snapshot(core);
			if (ret) {
				mutex_unlock(&core->lock);
				return ret;
			}
		}

		core->state = VPU_CORE_SNAPSHOT;
	}
	mutex_unlock(&core->lock);

	vpu_core_cancel_work(core);

	mutex_lock(&core->lock);
	vpu_core_put_vpu(core);
	mutex_unlock(&core->lock);
	return ret;
}

static const struct dev_pm_ops vpu_core_pm_ops = {
	SET_RUNTIME_PM_OPS(vpu_core_runtime_suspend, vpu_core_runtime_resume, NULL)
	SET_SYSTEM_SLEEP_PM_OPS(vpu_core_suspend, vpu_core_resume)
};

static struct vpu_core_resources imx8q_enc = {
	.type = VPU_CORE_TYPE_ENC,
	.fwname = "vpu/vpu_fw_imx8_enc.bin",
	.stride = 16,
	.max_width = 1920,
	.max_height = 1920,
	.min_width = 64,
	.min_height = 48,
	.step_width = 2,
	.step_height = 2,
	.rpc_size = 0x80000,
	.fwlog_size = 0x80000,
	.act_size = 0xc0000,
};

static struct vpu_core_resources imx8q_dec = {
	.type = VPU_CORE_TYPE_DEC,
	.fwname = "vpu/vpu_fw_imx8_dec.bin",
	.stride = 256,
	.max_width = 8188,
	.max_height = 8188,
	.min_width = 16,
	.min_height = 16,
	.step_width = 1,
	.step_height = 1,
	.rpc_size = 0x80000,
	.fwlog_size = 0x80000,
};

static const struct of_device_id vpu_core_dt_match[] = {
	{ .compatible = "nxp,imx8q-vpu-encoder", .data = &imx8q_enc },
	{ .compatible = "nxp,imx8q-vpu-decoder", .data = &imx8q_dec },
	{}
};
MODULE_DEVICE_TABLE(of, vpu_core_dt_match);

static struct platform_driver amphion_vpu_core_driver = {
	.probe = vpu_core_probe,
	.remove = vpu_core_remove,
	.driver = {
		.name = "amphion-vpu-core",
		.of_match_table = vpu_core_dt_match,
		.pm = &vpu_core_pm_ops,
	},
};

int __init vpu_core_driver_init(void)
{
	return platform_driver_register(&amphion_vpu_core_driver);
}

void __exit vpu_core_driver_exit(void)
{
	platform_driver_unregister(&amphion_vpu_core_driver);
}
+15 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright 2020-2021 NXP
 */

#ifndef _AMPHION_VPU_CORE_H
#define _AMPHION_VPU_CORE_H

void csr_writel(struct vpu_core *core, u32 reg, u32 val);
u32 csr_readl(struct vpu_core *core, u32 reg);
int vpu_alloc_dma(struct vpu_core *core, struct vpu_buffer *buf);
void vpu_free_dma(struct vpu_buffer *buf);
struct vpu_inst *vpu_core_find_instance(struct vpu_core *core, u32 index);

#endif
+494 −0

File added.

Preview size limit exceeded, changes collapsed.

+257 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading