android13/kernel-5.10/drivers/video/rockchip/mpp/mpp_service.c

500 lines
13 KiB
C

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
*
* author:
* Alpha Lin, alpha.lin@rock-chips.com
* Randy Li, randy.li@rock-chips.com
* Ding Wei, leo.ding@rock-chips.com
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/nospec.h>
#include <linux/mfd/syscon.h>
#include "mpp_debug.h"
#include "mpp_common.h"
#include "mpp_iommu.h"
#define MPP_CLASS_NAME "mpp_class"
#define MPP_SERVICE_NAME "mpp_service"
#define HAS_RKVDEC IS_ENABLED(CONFIG_ROCKCHIP_MPP_RKVDEC)
#define HAS_RKVENC IS_ENABLED(CONFIG_ROCKCHIP_MPP_RKVENC)
#define HAS_VDPU1 IS_ENABLED(CONFIG_ROCKCHIP_MPP_VDPU1)
#define HAS_VEPU1 IS_ENABLED(CONFIG_ROCKCHIP_MPP_VEPU1)
#define HAS_VDPU2 IS_ENABLED(CONFIG_ROCKCHIP_MPP_VDPU2)
#define HAS_VEPU2 IS_ENABLED(CONFIG_ROCKCHIP_MPP_VEPU2)
#define HAS_VEPU22 IS_ENABLED(CONFIG_ROCKCHIP_MPP_VEPU22)
#define HAS_IEP2 IS_ENABLED(CONFIG_ROCKCHIP_MPP_IEP2)
#define HAS_JPGDEC IS_ENABLED(CONFIG_ROCKCHIP_MPP_JPGDEC)
#define HAS_RKVDEC2 IS_ENABLED(CONFIG_ROCKCHIP_MPP_RKVDEC2)
#define HAS_RKVENC2 IS_ENABLED(CONFIG_ROCKCHIP_MPP_RKVENC2)
#define HAS_AV1DEC IS_ENABLED(CONFIG_ROCKCHIP_MPP_AV1DEC)
#define HAS_VDPP IS_ENABLED(CONFIG_ROCKCHIP_MPP_VDPP)
#define MPP_REGISTER_DRIVER(srv, flag, X, x) {\
if (flag)\
mpp_add_driver(srv, MPP_DRIVER_##X, &rockchip_##x##_driver, "grf_"#x);\
}
unsigned int mpp_dev_debug;
module_param(mpp_dev_debug, uint, 0644);
MODULE_PARM_DESC(mpp_dev_debug, "bit switch for mpp debug information");
static const char mpp_version[] = MPP_VERSION;
static int mpp_init_grf(struct device_node *np,
struct mpp_grf_info *grf_info,
const char *grf_name)
{
int ret;
int index;
u32 grf_offset = 0;
u32 grf_value = 0;
struct regmap *grf;
grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
if (IS_ERR_OR_NULL(grf))
return -EINVAL;
ret = of_property_read_u32(np, "rockchip,grf-offset", &grf_offset);
if (ret)
return -ENODATA;
index = of_property_match_string(np, "rockchip,grf-names", grf_name);
if (index < 0)
return -ENODATA;
ret = of_property_read_u32_index(np, "rockchip,grf-values",
index, &grf_value);
if (ret)
return -ENODATA;
grf_info->grf = grf;
grf_info->offset = grf_offset;
grf_info->val = grf_value;
mpp_set_grf(grf_info);
return 0;
}
static int mpp_add_driver(struct mpp_service *srv,
enum MPP_DRIVER_TYPE type,
struct platform_driver *driver,
const char *grf_name)
{
int ret;
mpp_init_grf(srv->dev->of_node,
&srv->grf_infos[type],
grf_name);
if (IS_ENABLED(CONFIG_ROCKCHIP_MPP_AV1DEC) && type == MPP_DRIVER_AV1DEC)
ret = av1dec_driver_register(driver);
else
ret = platform_driver_register(driver);
if (ret)
return ret;
srv->sub_drivers[type] = driver;
return 0;
}
static int mpp_remove_driver(struct mpp_service *srv, int i)
{
if (srv && srv->sub_drivers[i]) {
if (i != MPP_DRIVER_AV1DEC) {
mpp_set_grf(&srv->grf_infos[i]);
platform_driver_unregister(srv->sub_drivers[i]);
} else if (IS_ENABLED(CONFIG_ROCKCHIP_MPP_AV1DEC)) {
av1dec_driver_unregister(srv->sub_drivers[i]);
}
srv->sub_drivers[i] = NULL;
}
return 0;
}
static int mpp_register_service(struct mpp_service *srv,
const char *service_name)
{
int ret;
struct device *dev = srv->dev;
/* create a device */
ret = alloc_chrdev_region(&srv->dev_id, 0, 1, service_name);
if (ret) {
dev_err(dev, "alloc dev_t failed\n");
return ret;
}
cdev_init(&srv->mpp_cdev, &rockchip_mpp_fops);
srv->mpp_cdev.owner = THIS_MODULE;
srv->mpp_cdev.ops = &rockchip_mpp_fops;
ret = cdev_add(&srv->mpp_cdev, srv->dev_id, 1);
if (ret) {
unregister_chrdev_region(srv->dev_id, 1);
dev_err(dev, "add device failed\n");
return ret;
}
srv->child_dev = device_create(srv->cls, dev, srv->dev_id,
NULL, "%s", service_name);
return 0;
}
static int mpp_remove_service(struct mpp_service *srv)
{
device_destroy(srv->cls, srv->dev_id);
cdev_del(&srv->mpp_cdev);
unregister_chrdev_region(srv->dev_id, 1);
return 0;
}
#ifdef CONFIG_ROCKCHIP_MPP_PROC_FS
static int mpp_procfs_remove(struct mpp_service *srv)
{
if (srv->procfs) {
proc_remove(srv->procfs);
srv->procfs = NULL;
}
return 0;
}
static int mpp_show_version(struct seq_file *seq, void *offset)
{
seq_printf(seq, "%s\n", mpp_version);
return 0;
}
static int mpp_dump_session(struct mpp_session *session, struct seq_file *s)
{
struct mpp_dma_session *dma = session->dma;
struct mpp_dma_buffer *n;
struct mpp_dma_buffer *buffer;
phys_addr_t end;
unsigned long z = 0, t = 0;
int i = 0;
#define K(size) ((unsigned long)((size) >> 10))
if (!dma)
return 0;
seq_puts(s, "session iova range dump:\n");
mutex_lock(&dma->list_mutex);
list_for_each_entry_safe(buffer, n, &dma->used_list, link) {
end = buffer->iova + buffer->size - 1;
z = (unsigned long)buffer->size;
t += z;
seq_printf(s, "%4d: ", i++);
seq_printf(s, "%pa..%pa (%10lu %s)\n", &buffer->iova, &end,
(z >= 1024) ? (K(z)) : z,
(z >= 1024) ? "KiB" : "Bytes");
}
i = 0;
list_for_each_entry_safe(buffer, n, &dma->unused_list, link) {
if (!buffer->dmabuf)
continue;
end = buffer->iova + buffer->size - 1;
z = (unsigned long)buffer->size;
t += z;
seq_printf(s, "%4d: ", i++);
seq_printf(s, "%pa..%pa (%10lu %s)\n", &buffer->iova, &end,
(z >= 1024) ? (K(z)) : z,
(z >= 1024) ? "KiB" : "Bytes");
}
mutex_unlock(&dma->list_mutex);
seq_printf(s, "session: pid=%d index=%d\n", session->pid, session->index);
seq_printf(s, " device: %s\n", dev_name(session->mpp->dev));
seq_printf(s, " memory: %lu MiB\n", K(K(t)));
return 0;
}
static int mpp_show_session_summary(struct seq_file *seq, void *offset)
{
struct mpp_session *session = NULL, *n;
struct mpp_service *srv = seq->private;
mutex_lock(&srv->session_lock);
list_for_each_entry_safe(session, n,
&srv->session_list,
service_link) {
struct mpp_dev *mpp;
if (!session->priv)
continue;
if (!session->mpp)
continue;
mpp = session->mpp;
mpp_dump_session(session, seq);
if (mpp->dev_ops->dump_session)
mpp->dev_ops->dump_session(session, seq);
}
mutex_unlock(&srv->session_lock);
return 0;
}
static int mpp_show_support_cmd(struct seq_file *file, void *v)
{
seq_puts(file, "------------- SUPPORT CMD -------------\n");
seq_printf(file, "QUERY_HW_SUPPORT: 0x%08x\n", MPP_CMD_QUERY_HW_SUPPORT);
seq_printf(file, "QUERY_HW_ID: 0x%08x\n", MPP_CMD_QUERY_HW_ID);
seq_printf(file, "QUERY_CMD_SUPPORT: 0x%08x\n", MPP_CMD_QUERY_CMD_SUPPORT);
seq_printf(file, "QUERY_BUTT: 0x%08x\n", MPP_CMD_QUERY_BUTT);
seq_puts(file, "----\n");
seq_printf(file, "INIT_CLIENT_TYPE: 0x%08x\n", MPP_CMD_INIT_CLIENT_TYPE);
seq_printf(file, "INIT_TRANS_TABLE: 0x%08x\n", MPP_CMD_INIT_TRANS_TABLE);
seq_printf(file, "INIT_BUTT: 0x%08x\n", MPP_CMD_INIT_BUTT);
seq_puts(file, "----\n");
seq_printf(file, "SET_REG_WRITE: 0x%08x\n", MPP_CMD_SET_REG_WRITE);
seq_printf(file, "SET_REG_READ: 0x%08x\n", MPP_CMD_SET_REG_READ);
seq_printf(file, "SET_REG_ADDR_OFFSET: 0x%08x\n", MPP_CMD_SET_REG_ADDR_OFFSET);
seq_printf(file, "SEND_BUTT: 0x%08x\n", MPP_CMD_SEND_BUTT);
seq_puts(file, "----\n");
seq_printf(file, "POLL_HW_FINISH: 0x%08x\n", MPP_CMD_POLL_HW_FINISH);
seq_printf(file, "POLL_BUTT: 0x%08x\n", MPP_CMD_POLL_BUTT);
seq_puts(file, "----\n");
seq_printf(file, "RESET_SESSION: 0x%08x\n", MPP_CMD_RESET_SESSION);
seq_printf(file, "TRANS_FD_TO_IOVA: 0x%08x\n", MPP_CMD_TRANS_FD_TO_IOVA);
seq_printf(file, "RELEASE_FD: 0x%08x\n", MPP_CMD_RELEASE_FD);
seq_printf(file, "SEND_CODEC_INFO: 0x%08x\n", MPP_CMD_SEND_CODEC_INFO);
seq_printf(file, "CONTROL_BUTT: 0x%08x\n", MPP_CMD_CONTROL_BUTT);
return 0;
}
static int mpp_show_support_device(struct seq_file *file, void *v)
{
u32 i;
struct mpp_service *srv = file->private;
seq_puts(file, "---- SUPPORT DEVICES ----\n");
for (i = 0; i < MPP_DEVICE_BUTT; i++) {
struct mpp_dev *mpp;
struct mpp_hw_info *hw_info;
if (test_bit(i, &srv->hw_support)) {
mpp = srv->sub_devices[array_index_nospec(i, MPP_DEVICE_BUTT)];
if (!mpp)
continue;
seq_printf(file, "DEVICE[%2d]:%-10s", i, mpp_device_name[i]);
hw_info = mpp->var->hw_info;
if (hw_info->hw_id)
seq_printf(file, "HW_ID:0x%08x", hw_info->hw_id);
seq_puts(file, "\n");
}
}
return 0;
}
static int mpp_procfs_init(struct mpp_service *srv)
{
srv->procfs = proc_mkdir(MPP_SERVICE_NAME, NULL);
if (IS_ERR_OR_NULL(srv->procfs)) {
mpp_err("failed on mkdir /proc/%s\n", MPP_SERVICE_NAME);
srv->procfs = NULL;
return -EIO;
}
/* show version */
proc_create_single("version", 0444, srv->procfs, mpp_show_version);
/* for show session info */
proc_create_single_data("sessions-summary", 0444,
srv->procfs, mpp_show_session_summary, srv);
/* show support dev cmd */
proc_create_single("supports-cmd", 0444, srv->procfs, mpp_show_support_cmd);
/* show support devices */
proc_create_single_data("supports-device", 0444,
srv->procfs, mpp_show_support_device, srv);
mpp_procfs_create_u32("timing_en", 0644, srv->procfs, &srv->timing_en);
return 0;
}
#else
static inline int mpp_procfs_remove(struct mpp_service *srv)
{
return 0;
}
static inline int mpp_procfs_init(struct mpp_service *srv)
{
return 0;
}
#endif
static int mpp_service_probe(struct platform_device *pdev)
{
int ret, i;
struct mpp_service *srv = NULL;
struct mpp_taskqueue *queue;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
dev_info(dev, "%s\n", mpp_version);
dev_info(dev, "probe start\n");
srv = devm_kzalloc(dev, sizeof(*srv), GFP_KERNEL);
if (!srv)
return -ENOMEM;
srv->dev = dev;
atomic_set(&srv->shutdown_request, 0);
platform_set_drvdata(pdev, srv);
srv->cls = class_create(THIS_MODULE, MPP_CLASS_NAME);
if (PTR_ERR_OR_ZERO(srv->cls))
return PTR_ERR(srv->cls);
of_property_read_u32(np, "rockchip,taskqueue-count",
&srv->taskqueue_cnt);
if (srv->taskqueue_cnt > MPP_DEVICE_BUTT) {
dev_err(dev, "rockchip,taskqueue-count %d must less than %d\n",
srv->taskqueue_cnt, MPP_DEVICE_BUTT);
return -EINVAL;
}
for (i = 0; i < srv->taskqueue_cnt; i++) {
queue = mpp_taskqueue_init(dev);
if (!queue)
continue;
kthread_init_worker(&queue->worker);
queue->kworker_task = kthread_run(kthread_worker_fn, &queue->worker,
"queue_work%d", i);
srv->task_queues[i] = queue;
}
of_property_read_u32(np, "rockchip,resetgroup-count",
&srv->reset_group_cnt);
if (srv->reset_group_cnt > MPP_DEVICE_BUTT) {
dev_err(dev, "rockchip,resetgroup-count %d must less than %d\n",
srv->reset_group_cnt, MPP_DEVICE_BUTT);
return -EINVAL;
}
if (srv->reset_group_cnt) {
u32 i = 0;
struct mpp_reset_group *group;
for (i = 0; i < srv->reset_group_cnt; i++) {
group = devm_kzalloc(dev, sizeof(*group), GFP_KERNEL);
if (!group)
continue;
init_rwsem(&group->rw_sem);
srv->reset_groups[i] = group;
}
}
ret = mpp_register_service(srv, MPP_SERVICE_NAME);
if (ret) {
dev_err(dev, "register %s device\n", MPP_SERVICE_NAME);
goto fail_register;
}
mutex_init(&srv->session_lock);
INIT_LIST_HEAD(&srv->session_list);
mpp_procfs_init(srv);
/* register sub drivers */
MPP_REGISTER_DRIVER(srv, HAS_RKVDEC, RKVDEC, rkvdec);
MPP_REGISTER_DRIVER(srv, HAS_RKVENC, RKVENC, rkvenc);
MPP_REGISTER_DRIVER(srv, HAS_VDPU1, VDPU1, vdpu1);
MPP_REGISTER_DRIVER(srv, HAS_VEPU1, VEPU1, vepu1);
MPP_REGISTER_DRIVER(srv, HAS_VDPU2, VDPU2, vdpu2);
MPP_REGISTER_DRIVER(srv, HAS_VEPU2, VEPU2, vepu2);
MPP_REGISTER_DRIVER(srv, HAS_VEPU22, VEPU22, vepu22);
MPP_REGISTER_DRIVER(srv, HAS_IEP2, IEP2, iep2);
MPP_REGISTER_DRIVER(srv, HAS_JPGDEC, JPGDEC, jpgdec);
MPP_REGISTER_DRIVER(srv, HAS_RKVDEC2, RKVDEC2, rkvdec2);
MPP_REGISTER_DRIVER(srv, HAS_RKVENC2, RKVENC2, rkvenc2);
MPP_REGISTER_DRIVER(srv, HAS_AV1DEC, AV1DEC, av1dec);
MPP_REGISTER_DRIVER(srv, HAS_VDPP, VDPP, vdpp);
dev_info(dev, "probe success\n");
return 0;
fail_register:
class_destroy(srv->cls);
return ret;
}
static int mpp_service_remove(struct platform_device *pdev)
{
struct mpp_taskqueue *queue;
struct device *dev = &pdev->dev;
struct mpp_service *srv = platform_get_drvdata(pdev);
int i;
dev_info(dev, "remove device\n");
for (i = 0; i < srv->taskqueue_cnt; i++) {
queue = srv->task_queues[i];
if (queue && queue->kworker_task) {
kthread_flush_worker(&queue->worker);
kthread_stop(queue->kworker_task);
queue->kworker_task = NULL;
}
}
/* remove sub drivers */
for (i = 0; i < MPP_DRIVER_BUTT; i++)
mpp_remove_driver(srv, i);
mpp_remove_service(srv);
class_destroy(srv->cls);
mpp_procfs_remove(srv);
return 0;
}
static const struct of_device_id mpp_dt_ids[] = {
{
.compatible = "rockchip,mpp-service",
},
{ },
};
static struct platform_driver mpp_service_driver = {
.probe = mpp_service_probe,
.remove = mpp_service_remove,
.driver = {
.name = "mpp_service",
.of_match_table = of_match_ptr(mpp_dt_ids),
},
};
module_platform_driver(mpp_service_driver);
MODULE_LICENSE("Dual MIT/GPL");
MODULE_VERSION(MPP_VERSION);
MODULE_AUTHOR("Ding Wei leo.ding@rock-chips.com");
MODULE_DESCRIPTION("Rockchip mpp service driver");