media: rockchip: vpss: init driver for rk3576

Change-Id: I8fd1af91d055d0ebd30bd9898db6305a51fd7dda
Signed-off-by: Cai YiWei <cyw@rock-chips.com>
This commit is contained in:
Cai YiWei 2023-12-21 16:33:36 +08:00 committed by Tao Huang
commit aae52d34ed
21 changed files with 6726 additions and 0 deletions

View file

@ -9,3 +9,4 @@ source "drivers/media/platform/rockchip/ispp/Kconfig"
source "drivers/media/platform/rockchip/hdmirx/Kconfig"
source "drivers/media/platform/rockchip/rga/Kconfig"
source "drivers/media/platform/rockchip/rkisp1/Kconfig"
source "drivers/media/platform/rockchip/vpss/Kconfig"

View file

@ -6,3 +6,4 @@ obj-y += ispp/
obj-y += hdmirx/
obj-y += rga/
obj-y += rkisp1/
obj-y += vpss/

View file

@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0
config VIDEO_ROCKCHIP_VPSS
tristate "Rockchip Video Processing Sub System driver"
depends on V4L_PLATFORM_DRIVERS
depends on VIDEO_DEV
depends on ARCH_ROCKCHIP || COMPILE_TEST
depends on CPU_RK3576
select VIDEO_V4L2_SUBDEV_API
select VIDEOBUF2_CMA_SG
default n
help
Support for VPSS on the rockchip SoC.

View file

@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_VIDEO_ROCKCHIP_VPSS) += video_rkvpss.o
video_rkvpss-objs += hw.o \
dev.o \
common.o \
vpss.o \
stream.o \
procfs.o \
vpss_offline.o

View file

@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2023 Rockchip Electronics Co., Ltd */
#include <linux/delay.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include "common.h"
#include "dev.h"
#include "regs.h"
void rkvpss_write(struct rkvpss_device *dev, u32 reg, u32 val)
{
u32 *mem = dev->sw_base_addr + reg;
u32 *flag = dev->sw_base_addr + reg + RKVPSS_SW_REG_SIZE;
*mem = val;
*flag = RKVPSS_REG_CACHE;
if (dev->hw_dev->is_single)
rkvpss_hw_write(dev->hw_dev, reg, val);
}
u32 rkvpss_read(struct rkvpss_device *dev, u32 reg)
{
u32 val;
if (dev->hw_dev->is_single)
val = rkvpss_hw_read(dev->hw_dev, reg);
else
val = *(u32 *)(dev->sw_base_addr + reg);
return val;
}
void rkvpss_set_bits(struct rkvpss_device *dev, u32 reg, u32 mask, u32 val)
{
u32 *mem = dev->sw_base_addr + reg;
u32 *flag = dev->sw_base_addr + reg + RKVPSS_SW_REG_SIZE;
*mem &= ~mask;
*mem |= val;
*flag = RKVPSS_REG_CACHE;
if (dev->hw_dev->is_single)
rkvpss_hw_set_bits(dev->hw_dev, reg, mask, val);
}
void rkvpss_clear_bits(struct rkvpss_device *dev, u32 reg, u32 mask)
{
u32 *mem = dev->sw_base_addr + reg;
u32 *flag = dev->sw_base_addr + reg + RKVPSS_SW_REG_SIZE;
*mem &= ~mask;
*flag = RKVPSS_REG_CACHE;
if (dev->hw_dev->is_single)
rkvpss_hw_clear_bits(dev->hw_dev, reg, mask);
}
void rkvpss_update_regs(struct rkvpss_device *dev, u32 start, u32 end)
{
struct rkvpss_hw_dev *hw = dev->hw_dev;
void __iomem *base = hw->base_addr;
unsigned long lock_flags = 0;
u32 i, j;
if (end > RKVPSS_SW_REG_SIZE - 4) {
dev_err(dev->dev, "%s out of range\n", __func__);
return;
}
for (i = start; i <= end; i += 4) {
u32 *val = dev->sw_base_addr + i;
u32 *flag = dev->sw_base_addr + i + RKVPSS_SW_REG_SIZE;
if (*flag != RKVPSS_REG_CACHE)
continue;
if (IS_SYNC_REG(i)) {
spin_lock_irqsave(&hw->reg_lock, lock_flags);
if (i == RKVPSS_VPSS_ONLINE) {
u32 mask = 0;
for (j = 0; j < RKVPSS_OUTPUT_MAX; j++) {
if (!hw->is_ofl_ch[j])
continue;
mask |= (RKVPSS_ISP2VPSS_CHN0_SEL(3) << j * 2);
}
*val |= (readl(base + i) & mask);
}
}
writel(*val, base + i);
if (IS_SYNC_REG(i))
spin_unlock_irqrestore(&hw->reg_lock, lock_flags);
}
}
int rkvpss_attach_hw(struct rkvpss_device *vpss)
{
struct device_node *np;
struct platform_device *pdev;
struct rkvpss_hw_dev *hw;
np = of_parse_phandle(vpss->dev->of_node, "rockchip,hw", 0);
if (!np || !of_device_is_available(np)) {
dev_err(vpss->dev, "failed to get vpss hw node\n");
return -ENODEV;
}
pdev = of_find_device_by_node(np);
of_node_put(np);
if (!pdev) {
dev_err(vpss->dev, "failed to get vpss hw from node\n");
return -ENODEV;
}
hw = platform_get_drvdata(pdev);
if (!hw) {
dev_err(vpss->dev, "failed attach vpss hw\n");
return -EINVAL;
}
if (hw->dev_num >= VPSS_MAX_DEV) {
dev_err(vpss->dev, "failed attach vpss hw, max dev:%d\n", VPSS_MAX_DEV);
return -EINVAL;
}
if (hw->dev_num)
hw->is_single = false;
vpss->dev_id = hw->dev_num;
hw->vpss[hw->dev_num] = vpss;
hw->dev_num++;
vpss->hw_dev = hw;
vpss->vpss_ver = hw->vpss_ver;
return 0;
}

View file

@ -0,0 +1,93 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */
#ifndef _RKVPSS_COMMON_H
#define _RKVPSS_COMMON_H
#include <linux/clk.h>
#include <linux/kfifo.h>
#include <linux/mutex.h>
#include <media/media-device.h>
#include <media/media-entity.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/videobuf2-v4l2.h>
#include "../isp/isp_vpss.h"
#define RKVPSS_PLANE_Y 0
#define RKVPSS_PLANE_UV 1
#define RKVPSS_DEFAULT_WIDTH 1920
#define RKVPSS_DEFAULT_HEIGHT 1080
#define RKVPSS_MAX_WIDTH 4672
#define RKVPSS_MAX_HEIGHT 3504
#define RKVPSS_MIN_WIDTH 32
#define RKVPSS_MIN_HEIGHT 32
#define RKVPSS_VIDEO_NAME_LEN 16
#define RKVPSS_REG_CACHE_SYNC 0xeeeeeeee
#define RKVPSS_REG_CACHE 0xffffffff
#define RKVPSS_SW_REG_SIZE 0x35c0
#define RKVPSS_SW_REG_SIZE_MAX (RKVPSS_SW_REG_SIZE * 2)
struct rkvpss_device;
enum rkvpss_ver {
VPSS_V10 = 0x00,
};
enum rkvpss_fmt_pix_type {
FMT_YUV,
FMT_RGB,
};
/* One structure per video node */
struct rkvpss_vdev_node {
struct vb2_queue buf_queue;
struct video_device vdev;
struct media_pad pad;
};
struct rkvpss_buffer {
struct vb2_v4l2_buffer vb;
struct list_head queue;
u32 dma[VIDEO_MAX_PLANES];
void *vaddr[VIDEO_MAX_PLANES];
};
static inline struct rkvpss_vdev_node *vdev_to_node(struct video_device *vdev)
{
return container_of(vdev, struct rkvpss_vdev_node, vdev);
}
static inline struct rkvpss_vdev_node *queue_to_node(struct vb2_queue *q)
{
return container_of(q, struct rkvpss_vdev_node, buf_queue);
}
static inline struct rkvpss_buffer *to_rkvpss_buffer(struct vb2_v4l2_buffer *vb)
{
return container_of(vb, struct rkvpss_buffer, vb);
}
static inline struct vb2_queue *to_vb2_queue(struct file *file)
{
struct rkvpss_vdev_node *vnode = video_drvdata(file);
return &vnode->buf_queue;
}
extern int rkvpss_debug;
extern struct platform_driver rkvpss_plat_drv;
void rkvpss_write(struct rkvpss_device *dev, u32 reg, u32 val);
void rkvpss_set_bits(struct rkvpss_device *dev, u32 reg, u32 mask, u32 val);
u32 rkvpss_read(struct rkvpss_device *dev, u32 reg);
void rkvpss_clear_bits(struct rkvpss_device *dev, u32 reg, u32 mask);
void rkvpss_update_regs(struct rkvpss_device *dev, u32 start, u32 end);
int rkvpss_attach_hw(struct rkvpss_device *vpss);
void rkvpss_set_clk_rate(struct clk *clk, unsigned long rate);
#endif

View file

@ -0,0 +1,386 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2023 Rockchip Electronics Co., Ltd */
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/of_platform.h>
#include <linux/of_reserved_mem.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/regmap.h>
#include <media/v4l2-fwnode.h>
#include "dev.h"
#include "regs.h"
#include "version.h"
#define RKVPSS_VERNO_LEN 10
int rkvpss_debug;
module_param_named(debug, rkvpss_debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-3)");
static bool rkvpss_clk_dbg;
module_param_named(clk_dbg, rkvpss_clk_dbg, bool, 0644);
MODULE_PARM_DESC(clk_dbg, "rkvpss clk set by user");
static char rkvpss_version[RKVPSS_VERNO_LEN];
module_param_string(version, rkvpss_version, RKVPSS_VERNO_LEN, 0444);
MODULE_PARM_DESC(version, "version number");
void rkvpss_set_clk_rate(struct clk *clk, unsigned long rate)
{
if (rkvpss_clk_dbg)
return;
clk_set_rate(clk, rate);
}
void rkvpss_pipeline_default_fmt(struct rkvpss_device *dev)
{
u32 i, w, h;
w = dev->vpss_sdev.out_fmt.width;
h = dev->vpss_sdev.out_fmt.height;
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++)
rkvpss_stream_default_fmt(dev, i, w, h, V4L2_PIX_FMT_NV12);
}
int rkvpss_pipeline_open(struct rkvpss_device *dev)
{
if (atomic_inc_return(&dev->pipe_power_cnt) > 1)
return 0;
return 0;
}
int rkvpss_pipeline_close(struct rkvpss_device *dev)
{
if (atomic_dec_return(&dev->pipe_power_cnt))
return 0;
return 0;
}
int rkvpss_pipeline_stream(struct rkvpss_device *dev, bool on)
{
int ret;
if ((on && atomic_inc_return(&dev->pipe_stream_cnt) > 1) ||
(!on && atomic_dec_return(&dev->pipe_stream_cnt) > 0))
return 0;
if (on) {
ret = v4l2_subdev_call(&dev->vpss_sdev.sd, video, s_stream, true);
if (ret < 0)
goto err;
ret = v4l2_subdev_call(dev->remote_sd, video, s_stream, true);
if (ret < 0) {
v4l2_subdev_call(&dev->vpss_sdev.sd, video, s_stream, false);
goto err;
}
} else {
v4l2_subdev_call(dev->remote_sd, video, s_stream, false);
v4l2_subdev_call(&dev->vpss_sdev.sd, video, s_stream, false);
}
return 0;
err:
atomic_dec(&dev->pipe_stream_cnt);
v4l2_err(&dev->v4l2_dev, "%s on:%d failed:%d\n", __func__, on, ret);
return ret;
}
static int rkvpss_create_links(struct rkvpss_device *dev)
{
struct rkvpss_stream_vdev *stream_vdev;
struct media_entity *source, *sink;
struct rkvpss_stream *stream;
unsigned int flags = 0;
int ret;
if (!dev->remote_sd)
return -EINVAL;
/* input links */
sink = &dev->vpss_sdev.sd.entity;
ret = media_create_pad_link(&dev->remote_sd->entity, 0,
sink, RKVPSS_PAD_SINK, MEDIA_LNK_FL_ENABLED);
if (ret < 0)
goto end;
dev->inp = INP_ISP;
stream_vdev = &dev->stream_vdev;
/* output stream links */
flags = MEDIA_LNK_FL_ENABLED;
source = &dev->vpss_sdev.sd.entity;
stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH0];
stream->linked = flags;
sink = &stream->vnode.vdev.entity;
ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags);
if (ret < 0)
goto end;
stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH1];
stream->linked = flags;
sink = &stream->vnode.vdev.entity;
ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags);
if (ret < 0)
goto end;
stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH2];
stream->linked = flags;
sink = &stream->vnode.vdev.entity;
ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags);
if (ret < 0)
goto end;
stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH3];
stream->linked = flags;
sink = &stream->vnode.vdev.entity;
ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags);
if (ret < 0)
goto end;
end:
return ret;
}
static int
rkvpss_subdev_notifier_complete(struct v4l2_async_notifier *notifier)
{
struct rkvpss_device *dev;
int ret;
dev = container_of(notifier, struct rkvpss_device, notifier);
mutex_lock(&dev->media_dev.graph_mutex);
ret = rkvpss_create_links(dev);
if (ret < 0)
goto unlock;
ret = v4l2_device_register_subdev_nodes(&dev->v4l2_dev);
if (ret < 0)
goto unlock;
rkvpss_pipeline_default_fmt(dev);
v4l2_info(&dev->v4l2_dev, "Async subdev notifier completed\n");
unlock:
mutex_unlock(&dev->media_dev.graph_mutex);
return ret;
}
static int rkvpss_subdev_notifier_bound(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd)
{
struct rkvpss_device *dev;
dev = container_of(notifier, struct rkvpss_device, notifier);
dev->remote_sd = subdev;
v4l2_set_subdev_hostdata(subdev, &dev->vpss_sdev.sd);
return 0;
}
static void rkvpss_subdev_notifier_unbind(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd)
{
struct rkvpss_device *dev;
dev = container_of(notifier, struct rkvpss_device, notifier);
if (dev->remote_sd) {
v4l2_set_subdev_hostdata(dev->remote_sd, NULL);
dev->remote_sd = NULL;
}
}
static const struct
v4l2_async_notifier_operations rkvpss_subdev_notifier_ops = {
.bound = rkvpss_subdev_notifier_bound,
.unbind = rkvpss_subdev_notifier_unbind,
.complete = rkvpss_subdev_notifier_complete,
};
static int rkvpss_subdev_notifier(struct rkvpss_device *dev)
{
struct v4l2_async_notifier *ntf = &dev->notifier;
int ret;
v4l2_async_nf_init(ntf);
ret = v4l2_async_nf_parse_fwnode_endpoints(dev->dev,
ntf, sizeof(struct v4l2_async_subdev), NULL);
if (ret < 0)
return ret;
ntf->ops = &rkvpss_subdev_notifier_ops;
return v4l2_async_nf_register(&dev->v4l2_dev, ntf);
}
static int rkvpss_register_platform_subdevs(struct rkvpss_device *dev)
{
int ret;
ret = rkvpss_register_subdev(dev, &dev->v4l2_dev);
if (ret < 0)
return ret;
ret = rkvpss_register_stream_vdevs(dev);
if (ret < 0)
goto err_unreg_vpss_subdev;
ret = rkvpss_subdev_notifier(dev);
if (ret < 0)
goto err_unreg_stream_vdevs;
return ret;
err_unreg_stream_vdevs:
rkvpss_unregister_stream_vdevs(dev);
err_unreg_vpss_subdev:
rkvpss_unregister_subdev(dev);
return ret;
}
static const struct of_device_id rkvpss_plat_of_match[] = {
{
.compatible = "rockchip,rkvpss-vir",
},
{},
};
static int rkvpss_plat_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct v4l2_device *v4l2_dev;
struct rkvpss_device *vpss_dev;
int ret;
sprintf(rkvpss_version, "v%02x.%02x.%02x",
RKVPSS_DRIVER_VERSION >> 16,
(RKVPSS_DRIVER_VERSION & 0xff00) >> 8,
RKVPSS_DRIVER_VERSION & 0x00ff);
dev_info(dev, "rkvpss driver version: %s\n", rkvpss_version);
vpss_dev = devm_kzalloc(dev, sizeof(*vpss_dev), GFP_KERNEL);
if (!vpss_dev)
return -ENOMEM;
vpss_dev->sw_base_addr = devm_kzalloc(dev, RKVPSS_SW_REG_SIZE_MAX, GFP_KERNEL);
if (!vpss_dev->sw_base_addr)
return -ENOMEM;
dev_set_drvdata(dev, vpss_dev);
vpss_dev->dev = dev;
ret = rkvpss_attach_hw(vpss_dev);
if (ret)
return ret;
mutex_init(&vpss_dev->apilock);
strscpy(vpss_dev->name, dev_name(dev), sizeof(vpss_dev->name));
snprintf(vpss_dev->media_dev.model, sizeof(vpss_dev->media_dev.model),
"%s%d", DRIVER_NAME, vpss_dev->dev_id);
strscpy(vpss_dev->media_dev.driver_name, vpss_dev->name,
sizeof(vpss_dev->media_dev.driver_name));
vpss_dev->media_dev.dev = &pdev->dev;
v4l2_dev = &vpss_dev->v4l2_dev;
v4l2_dev->mdev = &vpss_dev->media_dev;
strscpy(v4l2_dev->name, vpss_dev->name, sizeof(v4l2_dev->name));
v4l2_ctrl_handler_init(&vpss_dev->ctrl_handler, 5);
v4l2_dev->ctrl_handler = &vpss_dev->ctrl_handler;
ret = v4l2_device_register(vpss_dev->dev, v4l2_dev);
if (ret < 0) {
v4l2_err(v4l2_dev, "register v4l2 device failed:%d\n", ret);
return ret;
}
media_device_init(&vpss_dev->media_dev);
ret = media_device_register(&vpss_dev->media_dev);
if (ret < 0) {
v4l2_err(v4l2_dev, "register media device failed:%d\n", ret);
goto err_unreg_v4l2_dev;
}
ret = rkvpss_register_platform_subdevs(vpss_dev);
if (ret < 0)
goto err_unreg_media_dev;
atomic_set(&vpss_dev->pipe_power_cnt, 0);
atomic_set(&vpss_dev->pipe_stream_cnt, 0);
rkvpss_proc_init(vpss_dev);
pm_runtime_enable(&pdev->dev);
vpss_dev->is_probe_end = true;
return 0;
err_unreg_media_dev:
media_device_unregister(&vpss_dev->media_dev);
err_unreg_v4l2_dev:
v4l2_device_unregister(&vpss_dev->v4l2_dev);
return ret;
}
static int rkvpss_plat_remove(struct platform_device *pdev)
{
struct rkvpss_device *vpss_dev = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
rkvpss_proc_cleanup(vpss_dev);
rkvpss_unregister_subdev(vpss_dev);
rkvpss_unregister_stream_vdevs(vpss_dev);
v4l2_ctrl_handler_free(&vpss_dev->ctrl_handler);
v4l2_async_nf_unregister(&vpss_dev->notifier);
v4l2_async_nf_cleanup(&vpss_dev->notifier);
media_device_unregister(&vpss_dev->media_dev);
v4l2_device_unregister(&vpss_dev->v4l2_dev);
mutex_destroy(&vpss_dev->apilock);
return 0;
}
static int __maybe_unused rkvpss_runtime_suspend(struct device *dev)
{
struct rkvpss_device *vpss_dev = dev_get_drvdata(dev);
int ret;
mutex_lock(&vpss_dev->hw_dev->dev_lock);
ret = pm_runtime_put_sync(vpss_dev->hw_dev->dev);
mutex_unlock(&vpss_dev->hw_dev->dev_lock);
return (ret > 0) ? 0 : ret;
}
static int __maybe_unused rkvpss_runtime_resume(struct device *dev)
{
struct rkvpss_device *vpss_dev = dev_get_drvdata(dev);
int ret;
mutex_lock(&vpss_dev->hw_dev->dev_lock);
ret = pm_runtime_get_sync(vpss_dev->hw_dev->dev);
mutex_unlock(&vpss_dev->hw_dev->dev_lock);
return (ret > 0) ? 0 : ret;
}
static const struct dev_pm_ops rkvpss_plat_pm_ops = {
SET_RUNTIME_PM_OPS(rkvpss_runtime_suspend,
rkvpss_runtime_resume, NULL)
};
struct platform_driver rkvpss_plat_drv = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(rkvpss_plat_of_match),
.pm = &rkvpss_plat_pm_ops,
},
.probe = rkvpss_plat_probe,
.remove = rkvpss_plat_remove,
};
EXPORT_SYMBOL(rkvpss_plat_drv);
MODULE_AUTHOR("Rockchip Camera/ISP team");
MODULE_DESCRIPTION("Rockchip VPSS platform driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(DMA_BUF);

View file

@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2023 Fuzhou Rockchip Electronics Co., Ltd. */
#ifndef _RKVPSS_DEV_H
#define _RKVPSS_DEV_H
#include <linux/rk-vpss-config.h>
#include "hw.h"
#include "procfs.h"
#include "stream.h"
#include "vpss.h"
#define DRIVER_NAME "rkvpss"
#define S0_VDEV_NAME DRIVER_NAME "_scale0"
#define S1_VDEV_NAME DRIVER_NAME "_scale1"
#define S2_VDEV_NAME DRIVER_NAME "_scale2"
#define S3_VDEV_NAME DRIVER_NAME "_scale3"
enum rkvpss_input {
INP_INVAL = 0,
INP_ISP,
};
enum {
T_CMD_QUEUE,
T_CMD_DEQUEUE,
T_CMD_LEN,
T_CMD_END,
};
struct rkvpss_rdbk_info {
u64 timestamp;
u64 seq;
};
struct rkvpss_device {
char name[128];
struct device *dev;
void *sw_base_addr;
struct v4l2_device v4l2_dev;
struct media_device media_dev;
struct v4l2_async_notifier notifier;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_subdev *remote_sd;
struct rkvpss_hw_dev *hw_dev;
struct rkvpss_subdev vpss_sdev;
struct rkvpss_stream_vdev stream_vdev;
struct proc_dir_entry *procfs;
atomic_t pipe_power_cnt;
atomic_t pipe_stream_cnt;
spinlock_t cmsc_lock;
struct rkvpss_cmsc_cfg cmsc_cfg;
enum rkvpss_ver vpss_ver;
/* mutex to serialize the calls from user */
struct mutex apilock;
enum rkvpss_input inp;
u32 dev_id;
u32 isr_cnt;
u32 isr_err_cnt;
bool mir_en;
bool cmsc_upd;
bool is_probe_end;
};
void rkvpss_pipeline_default_fmt(struct rkvpss_device *dev);
int rkvpss_pipeline_open(struct rkvpss_device *dev);
int rkvpss_pipeline_close(struct rkvpss_device *dev);
int rkvpss_pipeline_stream(struct rkvpss_device *dev, bool on);
#endif

View file

@ -0,0 +1,898 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2023 Rockchip Electronics Co., Ltd */
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iommu.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/of_platform.h>
#include <linux/of_reserved_mem.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <media/videobuf2-cma-sg.h>
#include <media/videobuf2-dma-sg.h>
#include <soc/rockchip/rockchip_iommu.h>
#include "common.h"
#include "dev.h"
#include "hw.h"
#include "regs.h"
struct irqs_data {
const char *name;
irqreturn_t (*irq_hdl)(int irq, void *ctx);
};
const s16 rkvpss_zme_tap8_coe[11][17][8] = {
{//>=2.667
{4, -12, 20, 488, 20, -12, 4, 0},
{4, -8, 8, 484, 36, -16, 4, 0},
{4, -4, -4, 476, 52, -20, 8, 0},
{0, 0, -16, 480, 68, -28, 8, 0},
{0, 4, -24, 472, 84, -32, 8, 0},
{0, 4, -36, 468, 100, -36, 12, 0},
{0, 8, -44, 456, 120, -40, 12, 0},
{0, 12, -52, 448, 136, -44, 12, 0},
{0, 12, -56, 436, 156, -48, 16, -4},
{-4, 16, -60, 424, 176, -52, 16, -4},
{-4, 16, -64, 412, 196, -56, 16, -4},
{-4, 16, -68, 400, 216, -60, 16, -4},
{-4, 20, -72, 380, 236, -64, 20, -4},
{-4, 20, -72, 364, 256, -68, 20, -4},
{-4, 20, -72, 348, 272, -68, 20, -4},
{-4, 20, -72, 332, 292, -72, 20, -4},
{-4, 20, -72, 312, 312, -72, 20, -4},
},
{//>=2
{8, -24, 44, 456, 44, -24, 8, 0},
{8, -20, 28, 460, 56, -28, 8, 0},
{8, -16, 16, 452, 72, -32, 12, 0},
{4, -12, 8, 448, 88, -36, 12, 0},
{4, -8, -4, 444, 104, -40, 12, 0},
{4, -8, -16, 444, 120, -44, 12, 0},
{4, -4, -24, 432, 136, -48, 16, 0},
{4, 0, -32, 428, 152, -52, 16, -4},
{0, 4, -40, 424, 168, -56, 16, -4},
{0, 4, -44, 412, 188, -60, 16, -4},
{0, 8, -52, 400, 204, -60, 16, -4},
{0, 8, -56, 388, 224, -64, 16, -4},
{0, 12, -60, 372, 240, -64, 16, -4},
{0, 12, -64, 356, 264, -68, 16, -4},
{0, 12, -64, 340, 280, -68, 16, -4},
{0, 16, -68, 324, 296, -68, 16, -4},
{0, 16, -68, 308, 308, -68, 16, 0},
},
{//>=1.5
{12, -32, 64, 424, 64, -32, 12, 0},
{8, -32, 52, 432, 76, -36, 12, 0},
{8, -28, 40, 432, 88, -40, 12, 0},
{8, -24, 28, 428, 104, -44, 12, 0},
{8, -20, 16, 424, 120, -48, 12, 0},
{8, -16, 8, 416, 132, -48, 12, 0},
{4, -16, -4, 420, 148, -52, 12, 0},
{4, -12, -12, 412, 164, -56, 12, 0},
{4, -8, -20, 400, 180, -56, 12, 0},
{4, -4, -28, 388, 196, -56, 12, 0},
{4, -4, -32, 380, 212, -60, 12, 0},
{4, 0, -40, 368, 228, -60, 12, 0},
{4, 0, -44, 356, 244, -60, 12, 0},
{0, 4, -48, 344, 260, -60, 12, 0},
{0, 4, -52, 332, 276, -60, 12, 0},
{0, 8, -56, 320, 292, -60, 8, 0},
{0, 8, -56, 304, 304, -56, 8, 0},
},
{//>1
{12, -40, 84, 400, 84, -40, 12, 0},
{12, -40, 72, 404, 96, -44, 12, 0},
{12, -36, 60, 404, 108, -48, 12, 0},
{8, -32, 48, 404, 120, -48, 12, 0},
{8, -32, 36, 404, 136, -52, 12, 0},
{8, -28, 28, 396, 148, -52, 12, 0},
{8, -24, 16, 392, 160, -52, 12, 0},
{8, -20, 8, 384, 176, -56, 12, 0},
{8, -20, 0, 384, 188, -56, 8, 0},
{8, -16, -8, 372, 204, -56, 8, 0},
{8, -12, -16, 364, 216, -56, 8, 0},
{4, -12, -20, 356, 232, -56, 8, 0},
{4, -8, -28, 348, 244, -56, 8, 0},
{4, -8, -32, 332, 264, -52, 4, 0},
{4, -4, -36, 324, 272, -52, 4, 0},
{4, 0, -40, 312, 280, -48, 0, 4},
{4, 0, -44, 296, 296, -44, 0, 4},
},
{//==1
{0, 0, 0, 511, 0, 0, 0, 0},
{-1, 3, -12, 511, 14, -4, 1, 0},
{-2, 6, -23, 509, 28, -8, 2, 0},
{-2, 9, -33, 503, 44, -12, 3, 0},
{-3, 11, -41, 496, 61, -16, 4, 0},
{-3, 13, -48, 488, 79, -21, 5, -1},
{-3, 14, -54, 477, 98, -25, 7, -2},
{-4, 16, -59, 465, 118, -30, 8, -2},
{-4, 17, -63, 451, 138, -35, 9, -1},
{-4, 18, -66, 437, 158, -39, 10, -2},
{-4, 18, -68, 421, 180, -44, 11, -2},
{-4, 18, -69, 404, 201, -48, 13, -3},
{-4, 18, -70, 386, 222, -52, 14, -2},
{-4, 18, -70, 368, 244, -56, 15, -3},
{-4, 18, -69, 348, 265, -59, 16, -3},
{-4, 18, -67, 329, 286, -63, 16, -3},
{-3, 17, -65, 307, 307, -65, 17, -3},
},
{//>=0.833
{-16, 0, 145, 254, 145, 0, -16, 0},
{-16, -2, 140, 253, 151, 3, -17, 0},
{-15, -5, 135, 253, 157, 5, -18, 0},
{-14, -7, 129, 252, 162, 8, -18, 0},
{-13, -9, 123, 252, 167, 11, -19, 0},
{-13, -11, 118, 250, 172, 15, -19, 0},
{-12, -12, 112, 250, 177, 18, -20, -1},
{-11, -14, 107, 247, 183, 21, -20, -1},
{-10, -15, 101, 245, 188, 25, -21, -1},
{-9, -16, 96, 243, 192, 29, -21, -2},
{-8, -18, 90, 242, 197, 33, -22, -2},
{-8, -19, 85, 239, 202, 37, -22, -2},
{-7, -19, 80, 236, 206, 41, -22, -3},
{-7, -20, 75, 233, 210, 46, -22, -3},
{-6, -21, 69, 230, 215, 50, -22, -3},
{-5, -21, 65, 226, 219, 55, -22, -5},
{-5, -21, 60, 222, 222, 60, -21, -5},
},
{//>=0.7
{-16, 0, 145, 254, 145, 0, -16, 0},
{-16, -2, 140, 253, 151, 3, -17, 0},
{-15, -5, 135, 253, 157, 5, -18, 0},
{-14, -7, 129, 252, 162, 8, -18, 0},
{-13, -9, 123, 252, 167, 11, -19, 0},
{-13, -11, 118, 250, 172, 15, -19, 0},
{-12, -12, 112, 250, 177, 18, -20, -1},
{-11, -14, 107, 247, 183, 21, -20, -1},
{-10, -15, 101, 245, 188, 25, -21, -1},
{-9, -16, 96, 243, 192, 29, -21, -2},
{-8, -18, 90, 242, 197, 33, -22, -2},
{-8, -19, 85, 239, 202, 37, -22, -2},
{-7, -19, 80, 236, 206, 41, -22, -3},
{-7, -20, 75, 233, 210, 46, -22, -3},
{-6, -21, 69, 230, 215, 50, -22, -3},
{-5, -21, 65, 226, 219, 55, -22, -5},
{-5, -21, 60, 222, 222, 60, -21, -5},
},
{//>=0.5
{-16, 0, 145, 254, 145, 0, -16, 0},
{-16, -2, 140, 253, 151, 3, -17, 0},
{-15, -5, 135, 253, 157, 5, -18, 0},
{-14, -7, 129, 252, 162, 8, -18, 0},
{-13, -9, 123, 252, 167, 11, -19, 0},
{-13, -11, 118, 250, 172, 15, -19, 0},
{-12, -12, 112, 250, 177, 18, -20, -1},
{-11, -14, 107, 247, 183, 21, -20, -1},
{-10, -15, 101, 245, 188, 25, -21, -1},
{-9, -16, 96, 243, 192, 29, -21, -2},
{-8, -18, 90, 242, 197, 33, -22, -2},
{-8, -19, 85, 239, 202, 37, -22, -2},
{-7, -19, 80, 236, 206, 41, -22, -3},
{-7, -20, 75, 233, 210, 46, -22, -3},
{-6, -21, 69, 230, 215, 50, -22, -3},
{-5, -21, 65, 226, 219, 55, -22, -5},
{-5, -21, 60, 222, 222, 60, -21, -5},
},
{//>=0.33
{-18, 18, 144, 226, 144, 19, -17, -4},
{-17, 16, 139, 226, 148, 21, -17, -4},
{-17, 13, 135, 227, 153, 24, -18, -5},
{-17, 11, 131, 226, 157, 27, -18, -5},
{-17, 9, 126, 225, 161, 30, -17, -5},
{-16, 6, 122, 225, 165, 33, -17, -6},
{-16, 4, 118, 224, 169, 37, -17, -7},
{-16, 2, 113, 224, 173, 40, -17, -7},
{-15, 0, 109, 222, 177, 43, -17, -7},
{-15, -1, 104, 220, 181, 47, -16, -8},
{-14, -3, 100, 218, 185, 51, -16, -9},
{-14, -5, 96, 217, 188, 54, -15, -9},
{-14, -6, 91, 214, 192, 58, -14, -9},
{-13, -7, 87, 212, 195, 62, -14, -10},
{-13, -9, 83, 210, 198, 66, -13, -10},
{-12, -10, 79, 207, 201, 70, -12, -11},
{-12, -11, 74, 205, 205, 74, -11, -12},
},
{//>=0.25
{14, 66, 113, 133, 113, 66, 14, -7},
{12, 65, 112, 133, 114, 68, 15, -7},
{11, 63, 111, 132, 115, 70, 17, -7},
{10, 62, 110, 132, 116, 71, 18, -7},
{8, 60, 108, 132, 118, 73, 20, -7},
{7, 58, 107, 132, 119, 75, 21, -7},
{6, 56, 106, 132, 120, 76, 23, -7},
{5, 55, 105, 131, 121, 78, 24, -7},
{4, 53, 103, 131, 122, 80, 26, -7},
{3, 51, 102, 131, 122, 81, 28, -6},
{2, 50, 101, 130, 123, 83, 29, -6},
{1, 48, 99, 131, 124, 84, 31, -6},
{0, 46, 98, 129, 125, 86, 33, -5},
{-1, 45, 97, 128, 126, 88, 34, -5},
{-2, 43, 95, 130, 126, 89, 36, -5},
{-3, 41, 94, 128, 127, 91, 38, -4},
{-3, 39, 92, 128, 128, 92, 39, -3},
},
{//others
{39, 69, 93, 102, 93, 69, 39, 8},
{38, 68, 92, 102, 93, 70, 40, 9},
{37, 67, 91, 102, 93, 71, 41, 10},
{36, 66, 91, 101, 94, 71, 42, 11},
{35, 65, 90, 102, 94, 72, 43, 11},
{34, 64, 89, 102, 94, 73, 44, 12},
{33, 63, 88, 101, 95, 74, 45, 13},
{32, 62, 88, 100, 95, 75, 46, 14},
{31, 62, 87, 100, 95, 75, 47, 15},
{30, 61, 86, 99, 96, 76, 48, 16},
{29, 60, 86, 98, 96, 77, 49, 17},
{28, 59, 85, 98, 96, 78, 50, 18},
{27, 58, 84, 99, 97, 78, 50, 19},
{26, 57, 83, 99, 97, 79, 51, 20},
{25, 56, 83, 98, 97, 80, 52, 21},
{24, 55, 82, 97, 98, 81, 53, 22},
{23, 54, 81, 98, 98, 81, 54, 23},
}
};
const s16 rkvpss_zme_tap6_coe[11][17][8] = {
{//>=2.667
{-12, 20, 492, 20, -12, 4, 0, 0},
{-8, 8, 488, 36, -16, 4, 0, 0},
{-4, -4, 488, 48, -20, 4, 0, 0},
{0, -16, 484, 64, -24, 4, 0, 0},
{0, -24, 476, 80, -28, 8, 0, 0},
{4, -32, 464, 100, -32, 8, 0, 0},
{8, -40, 456, 116, -36, 8, 0, 0},
{8, -48, 448, 136, -40, 8, 0, 0},
{12, -52, 436, 152, -44, 8, 0, 0},
{12, -60, 424, 172, -48, 12, 0, 0},
{12, -64, 412, 192, -52, 12, 0, 0},
{16, -64, 392, 212, -56, 12, 0, 0},
{16, -68, 380, 232, -60, 12, 0, 0},
{16, -68, 360, 248, -60, 16, 0, 0},
{16, -68, 344, 268, -64, 16, 0, 0},
{16, -68, 328, 288, -68, 16, 0, 0},
{16, -68, 308, 308, -68, 16, 0, 0},
},
{//>=2
{-20, 40, 468, 40, -20, 4, 0, 0},
{-16, 28, 464, 56, -24, 4, 0, 0},
{-16, 16, 464, 68, -28, 8, 0, 0},
{-12, 4, 460, 84, -32, 8, 0, 0},
{-8, -4, 452, 100, -36, 8, 0, 0},
{-4, -12, 444, 116, -40, 8, 0, 0},
{-4, -24, 440, 136, -44, 8, 0, 0},
{0, -32, 432, 152, -48, 8, 0, 0},
{0, -36, 416, 168, -48, 12, 0, 0},
{4, -44, 408, 184, -52, 12, 0, 0},
{4, -48, 400, 200, -56, 12, 0, 0},
{8, -52, 380, 220, -56, 12, 0, 0},
{8, -56, 372, 236, -60, 12, 0, 0},
{8, -60, 356, 256, -60, 12, 0, 0},
{12, -60, 340, 268, -60, 12, 0, 0},
{12, -60, 324, 288, -64, 12, 0, 0},
{12, -64, 308, 308, -64, 12, 0, 0},
},
{//>=1.5
{-28, 60, 440, 60, -28, 8, 0, 0},
{-28, 48, 440, 76, -32, 8, 0, 0},
{-24, 36, 440, 88, -36, 8, 0, 0},
{-20, 28, 432, 104, -40, 8, 0, 0},
{-16, 16, 428, 116, -40, 8, 0, 0},
{-16, 4, 428, 132, -44, 8, 0, 0},
{-12, -4, 420, 148, -48, 8, 0, 0},
{-8, -12, 408, 164, -48, 8, 0, 0},
{-8, -20, 404, 180, -52, 8, 0, 0},
{-4, -24, 388, 196, -52, 8, 0, 0},
{-4, -32, 384, 212, -56, 8, 0, 0},
{0, -36, 372, 224, -56, 8, 0, 0},
{0, -40, 360, 240, -56, 8, 0, 0},
{4, -44, 344, 256, -56, 8, 0, 0},
{4, -48, 332, 272, -56, 8, 0, 0},
{4, -52, 316, 292, -56, 8, 0, 0},
{8, -52, 300, 300, -52, 8, 0, 0},
},
{//>1
{-36, 80, 420, 80, -36, 4, 0, 0},
{-32, 68, 412, 92, -36, 8, 0, 0},
{-28, 56, 412, 104, -40, 8, 0, 0},
{-28, 44, 412, 116, -40, 8, 0, 0},
{-24, 36, 404, 132, -44, 8, 0, 0},
{-24, 24, 404, 144, -44, 8, 0, 0},
{-20, 16, 396, 160, -48, 8, 0, 0},
{-16, 8, 388, 172, -48, 8, 0, 0},
{-16, 0, 380, 188, -48, 8, 0, 0},
{-12, -8, 376, 200, -48, 4, 0, 0},
{-12, -12, 364, 216, -48, 4, 0, 0},
{-8, -20, 356, 228, -48, 4, 0, 0},
{-8, -24, 344, 244, -48, 4, 0, 0},
{-4, -32, 332, 260, -48, 4, 0, 0},
{-4, -36, 320, 272, -44, 4, 0, 0},
{0, -40, 308, 288, -44, 0, 0, 0},
{0, -40, 296, 296, -40, 0, 0, 0},
},
{//==1
{0, 0, 511, 0, 0, 0, 0, 0},
{3, -12, 511, 13, -3, 0, 0, 0},
{6, -22, 507, 28, -7, 0, 0, 0},
{8, -32, 502, 44, -11, 1, 0, 0},
{10, -40, 495, 61, -15, 1, 0, 0},
{11, -47, 486, 79, -19, 2, 0, 0},
{12, -53, 476, 98, -24, 3, 0, 0},
{13, -58, 464, 117, -28, 4, 0, 0},
{14, -62, 451, 137, -33, 5, 0, 0},
{15, -65, 437, 157, -38, 6, 0, 0},
{15, -67, 420, 179, -42, 7, 0, 0},
{15, -68, 404, 200, -46, 7, 0, 0},
{14, -68, 386, 221, -50, 9, 0, 0},
{14, -68, 367, 243, -54, 10, 0, 0},
{14, -67, 348, 264, -58, 11, 0, 0},
{13, -66, 328, 286, -61, 12, 0, 0},
{13, -63, 306, 306, -63, 13, 0, 0},
},
{//>=0.833
{-31, 104, 362, 104, -31, 4, 0, 0},
{-30, 94, 362, 114, -32, 4, 0, 0},
{-29, 84, 361, 125, -32, 3, 0, 0},
{-28, 75, 359, 136, -33, 3, 0, 0},
{-27, 66, 356, 147, -33, 3, 0, 0},
{-25, 57, 353, 158, -33, 2, 0, 0},
{-24, 49, 349, 169, -33, 2, 0, 0},
{-22, 41, 344, 180, -32, 1, 0, 0},
{-20, 33, 339, 191, -31, 0, 0, 0},
{-19, 26, 333, 203, -30, -1, 0, 0},
{-17, 19, 327, 214, -29, -2, 0, 0},
{-16, 13, 320, 225, -27, -3, 0, 0},
{-14, 7, 312, 236, -25, -4, 0, 0},
{-13, 1, 305, 246, -22, -5, 0, 0},
{-11, -4, 295, 257, -19, -6, 0, 0},
{-10, -8, 286, 267, -16, -7, 0, 0},
{-9, -12, 277, 277, -12, -9, 0, 0},
},
{//>=0.7
{-31, 104, 362, 104, -31, 4, 0, 0},
{-30, 94, 362, 114, -32, 4, 0, 0},
{-29, 84, 361, 125, -32, 3, 0, 0},
{-28, 75, 359, 136, -33, 3, 0, 0},
{-27, 66, 356, 147, -33, 3, 0, 0},
{-25, 57, 353, 158, -33, 2, 0, 0},
{-24, 49, 349, 169, -33, 2, 0, 0},
{-22, 41, 344, 180, -32, 1, 0, 0},
{-20, 33, 339, 191, -31, 0, 0, 0},
{-19, 26, 333, 203, -30, -1, 0, 0},
{-17, 19, 327, 214, -29, -2, 0, 0},
{-16, 13, 320, 225, -27, -3, 0, 0},
{-14, 7, 312, 236, -25, -4, 0, 0},
{-13, 1, 305, 246, -22, -5, 0, 0},
{-11, -4, 295, 257, -19, -6, 0, 0},
{-10, -8, 286, 267, -16, -7, 0, 0},
{-9, -12, 277, 277, -12, -9, 0, 0},
},
{//>=0.5
{ -20, 130, 297, 130, -20, -5, 0, 0},
{ -21, 122, 298, 138, -19, -6, 0, 0},
{ -22, 115, 297, 146, -17, -7, 0, 0},
{ -22, 108, 296, 153, -16, -7, 0, 0},
{ -23, 101, 295, 161, -14, -8, 0, 0},
{ -23, 93, 294, 169, -12, -9, 0, 0},
{ -24, 87, 292, 177, -10, -10, 0, 0},
{ -24, 80, 289, 185, -7, -11, 0, 0},
{ -24, 73, 286, 193, -4, -12, 0, 0},
{ -23, 66, 283, 200, -1, -13, 0, 0},
{ -23, 60, 279, 208, 2, -14, 0, 0},
{ -23, 54, 276, 215, 5, -15, 0, 0},
{ -22, 48, 271, 222, 9, -16, 0, 0},
{ -21, 42, 266, 229, 13, -17, 0, 0},
{ -21, 37, 261, 236, 17, -18, 0, 0},
{ -21, 32, 255, 242, 22, -18, 0, 0},
{ -20, 27, 249, 249, 27, -20, 0, 0},
},
{//>=0.33
{16, 136, 217, 136, 16, -9, 0, 0},
{13, 132, 217, 141, 18, -9, 0, 0},
{11, 128, 217, 145, 21, -10, 0, 0},
{9, 124, 216, 149, 24, -10, 0, 0},
{7, 119, 216, 153, 27, -10, 0, 0},
{5, 115, 216, 157, 30, -11, 0, 0},
{3, 111, 215, 161, 33, -11, 0, 0},
{1, 107, 214, 165, 36, -11, 0, 0},
{0, 102, 213, 169, 39, -11, 0, 0},
{-2, 98, 211, 173, 43, -11, 0, 0},
{-3, 94, 209, 177, 46, -11, 0, 0},
{-4, 90, 207, 180, 50, -11, 0, 0},
{-5, 85, 206, 184, 53, -11, 0, 0},
{-6, 81, 203, 187, 57, -10, 0, 0},
{-7, 77, 201, 190, 61, -10, 0, 0},
{-8, 73, 198, 193, 65, -9, 0, 0},
{-9, 69, 196, 196, 69, -9, 0, 0},
},
{//>=0.25
{66, 115, 138, 115, 66, 12, 0, 0},
{64, 114, 136, 116, 68, 14, 0, 0},
{63, 113, 134, 117, 70, 15, 0, 0},
{61, 111, 135, 118, 71, 16, 0, 0},
{59, 110, 133, 119, 73, 18, 0, 0},
{57, 108, 134, 120, 74, 19, 0, 0},
{55, 107, 133, 121, 76, 20, 0, 0},
{53, 105, 133, 121, 78, 22, 0, 0},
{51, 104, 133, 122, 79, 23, 0, 0},
{49, 102, 132, 123, 81, 25, 0, 0},
{47, 101, 132, 124, 82, 26, 0, 0},
{45, 99, 131, 125, 84, 28, 0, 0},
{44, 98, 130, 125, 85, 30, 0, 0},
{42, 96, 130, 126, 87, 31, 0, 0},
{40, 95, 128, 127, 89, 33, 0, 0},
{38, 93, 129, 127, 90, 35, 0, 0},
{36, 92, 128, 128, 92, 36, 0, 0},
},
{//others
{80, 105, 116, 105, 80, 26, 0, 0},
{79, 104, 115, 105, 81, 28, 0, 0},
{77, 103, 116, 106, 81, 29, 0, 0},
{76, 102, 115, 106, 82, 31, 0, 0},
{74, 101, 115, 106, 83, 33, 0, 0},
{73, 100, 114, 106, 84, 35, 0, 0},
{71, 99, 114, 107, 84, 37, 0, 0},
{70, 98, 113, 107, 85, 39, 0, 0},
{68, 98, 113, 107, 86, 40, 0, 0},
{67, 97, 112, 108, 86, 42, 0, 0},
{65, 96, 112, 108, 87, 44, 0, 0},
{63, 95, 112, 108, 88, 46, 0, 0},
{62, 94, 112, 108, 88, 48, 0, 0},
{60, 93, 111, 109, 89, 50, 0, 0},
{58, 93, 111, 109, 90, 51, 0, 0},
{57, 92, 110, 110, 90, 53, 0, 0},
{55, 91, 110, 110, 91, 55, 0, 0},
}
};
void rkvpss_hw_write(struct rkvpss_hw_dev *hw, u32 reg, u32 val)
{
unsigned long lock_flags = 0;
if (IS_SYNC_REG(reg))
spin_lock_irqsave(&hw->reg_lock, lock_flags);
writel(val, hw->base_addr + reg);
if (IS_SYNC_REG(reg))
spin_unlock_irqrestore(&hw->reg_lock, lock_flags);
}
u32 rkvpss_hw_read(struct rkvpss_hw_dev *hw, u32 reg)
{
unsigned long lock_flags = 0;
u32 val;
if (IS_SYNC_REG(reg))
spin_lock_irqsave(&hw->reg_lock, lock_flags);
val = readl(hw->base_addr + reg);
if (IS_SYNC_REG(reg))
spin_unlock_irqrestore(&hw->reg_lock, lock_flags);
return val;
}
void rkvpss_hw_set_bits(struct rkvpss_hw_dev *hw, u32 reg, u32 mask, u32 val)
{
unsigned long lock_flags = 0;
u32 tmp;
if (IS_SYNC_REG(reg))
spin_lock_irqsave(&hw->reg_lock, lock_flags);
tmp = readl(hw->base_addr + reg) & ~mask;
writel(val | tmp, hw->base_addr + reg);
if (IS_SYNC_REG(reg))
spin_unlock_irqrestore(&hw->reg_lock, lock_flags);
}
void rkvpss_hw_clear_bits(struct rkvpss_hw_dev *hw, u32 reg, u32 mask)
{
unsigned long lock_flags = 0;
u32 val;
if (IS_SYNC_REG(reg))
spin_lock_irqsave(&hw->reg_lock, lock_flags);
val = readl(hw->base_addr + reg) & ~mask;
writel(val, hw->base_addr + reg);
if (IS_SYNC_REG(reg))
spin_unlock_irqrestore(&hw->reg_lock, lock_flags);
}
void rkvpss_soft_reset(struct rkvpss_hw_dev *hw)
{
writel(RKVPSS_SOFT_RST, hw->base_addr + RKVPSS_VPSS_RESET);
if (hw->reset) {
reset_control_assert(hw->reset);
udelay(20);
reset_control_deassert(hw->reset);
udelay(20);
}
/* refresh iommu after reset */
if (hw->is_mmu) {
rockchip_iommu_disable(hw->dev);
rockchip_iommu_enable(hw->dev);
}
rkvpss_hw_write(hw, RKVPSS_VPSS_CTRL, RKVPSS_ACK_FRM_PRO_DIS);
rkvpss_hw_write(hw, RKVPSS_VPSS_IRQ_CFG, 0x3fff);
rkvpss_hw_write(hw, RKVPSS_MI_IMSC, 0xd0000000);
}
int rkvpss_get_zme_tap_coe_index(int ratio)
{
int idx;
if (ratio >= 26670)
idx = 0;
else if (ratio >= 20000)
idx = 1;
else if (ratio >= 15000)
idx = 2;
else if (ratio > 10000)
idx = 3;
else if (ratio == 10000)
idx = 4;
else if (ratio >= 8333)
idx = 5;
else if (ratio >= 7000)
idx = 6;
else if (ratio >= 5000)
idx = 7;
else if (ratio >= 3300)
idx = 8;
else if (ratio >= 2500)
idx = 9;
else
idx = 10;
return idx;
}
void rkvpss_cmsc_slop(struct rkvpss_cmsc_point *p0,
struct rkvpss_cmsc_point *p1,
int *k, int *hor)
{
int x0 = p0->x, y0 = p0->y;
int x1 = p1->x, y1 = p1->y;
int i, slp, sign_flag = 0, slp_bit = 13;
int highest1 = 0, k_fix, pow;
if (y0 == y1) {
/* horizontal line */
*hor = 1;
slp = 0;
} else {
*hor = 0;
slp = (int)((x1 - x0) * (1 << slp_bit) / (y1 - y0));
if (slp < 0) {
sign_flag = 1;
slp = -slp;
}
slp = min_t(int, slp, (1 << slp_bit * 2) - 1);
}
for (i = slp_bit * 2 - 1; i >= 0; i--) {
if (slp >> i) {
highest1 = i;
break;
}
}
if (highest1 >= slp_bit) {
pow = highest1 - slp_bit + 1;
k_fix = slp >> pow;
} else {
pow = 0;
k_fix = slp;
}
*k = (sign_flag << (slp_bit + 4)) + (pow << slp_bit) + k_fix;
}
static inline bool is_iommu_enable(struct device *dev)
{
struct device_node *iommu;
iommu = of_parse_phandle(dev->of_node, "iommus", 0);
if (!iommu) {
dev_info(dev, "no iommu attached, using non-iommu buffers\n");
return false;
} else if (!of_device_is_available(iommu)) {
dev_info(dev, "iommu is disabled, using non-iommu buffers\n");
of_node_put(iommu);
return false;
}
of_node_put(iommu);
return true;
}
static void disable_sys_clk(struct rkvpss_hw_dev *dev)
{
int i;
for (i = 0; i < dev->clks_num; i++)
clk_disable_unprepare(dev->clks[i]);
}
static int enable_sys_clk(struct rkvpss_hw_dev *dev)
{
int i, ret = -EINVAL;
for (i = 0; i < dev->clks_num; i++) {
ret = clk_prepare_enable(dev->clks[i]);
if (ret < 0)
goto err;
}
return 0;
err:
for (--i; i >= 0; --i)
clk_disable_unprepare(dev->clks[i]);
return ret;
}
static irqreturn_t vpss_irq_hdl(int irq, void *ctx)
{
struct device *dev = ctx;
struct rkvpss_hw_dev *hw_dev = dev_get_drvdata(dev);
struct rkvpss_device *vpss = hw_dev->vpss[hw_dev->cur_dev_id];
unsigned int mis_val;
mis_val = rkvpss_hw_read(hw_dev, RKVPSS_VPSS_MIS);
if (mis_val) {
rkvpss_hw_write(hw_dev, RKVPSS_VPSS_ICR, mis_val);
if (mis_val & RKVPSS_ISP_ALL_FRM_END)
rkvpss_isr(vpss, mis_val);
if (mis_val & RKVPSS_ALL_FRM_END)
rkvpss_offline_irq(hw_dev, mis_val);
}
return IRQ_HANDLED;
}
static irqreturn_t mi_irq_hdl(int irq, void *ctx)
{
struct device *dev = ctx;
struct rkvpss_hw_dev *hw_dev = dev_get_drvdata(dev);
struct rkvpss_device *vpss = hw_dev->vpss[hw_dev->cur_dev_id];
unsigned int mis_val;
mis_val = rkvpss_hw_read(hw_dev, RKVPSS_MI_MIS);
if (mis_val) {
rkvpss_hw_write(hw_dev, RKVPSS_MI_ICR, mis_val);
rkvpss_mi_isr(vpss, mis_val);
}
return IRQ_HANDLED;
}
static struct irqs_data vpss_irqs[] = {
{"vpss_irq", vpss_irq_hdl},
{"mi_irq", mi_irq_hdl},
};
static const char * const vpss_clks[] = {
"clk_vpss",
"aclk_vpss",
"hclk_vpss",
};
static const struct vpss_match_data rk3576_vpss_match_data = {
.irqs = vpss_irqs,
.num_irqs = ARRAY_SIZE(vpss_irqs),
.clks = vpss_clks,
.clks_num = ARRAY_SIZE(vpss_clks),
.vpss_ver = VPSS_V10,
};
static const struct of_device_id rkvpss_hw_of_match[] = {
{
.compatible = "rockchip,rk3576-rkvpss",
.data = &rk3576_vpss_match_data,
},
{},
};
static int rkvpss_hw_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
const struct vpss_match_data *match_data;
struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct rkvpss_hw_dev *hw_dev;
struct resource *res;
int i, ret, irq;
bool is_mem_reserved = true;
match = of_match_node(rkvpss_hw_of_match, node);
if (IS_ERR(match))
return PTR_ERR(match);
hw_dev = devm_kzalloc(dev, sizeof(*hw_dev), GFP_KERNEL);
if (!hw_dev)
return -ENOMEM;
dev_set_drvdata(dev, hw_dev);
hw_dev->dev = dev;
match_data = match->data;
hw_dev->match_data = match->data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "get resource failed\n");
ret = -EINVAL;
goto err;
}
hw_dev->base_addr = devm_ioremap_resource(dev, res);
if (PTR_ERR(hw_dev->base_addr) == -EBUSY) {
resource_size_t offset = res->start;
resource_size_t size = resource_size(res);
hw_dev->base_addr = devm_ioremap(dev, offset, size);
}
if (IS_ERR(hw_dev->base_addr)) {
dev_err(dev, "ioremap failed\n");
ret = PTR_ERR(hw_dev->base_addr);
goto err;
}
for (i = 0; i < match_data->num_irqs; i++) {
irq = platform_get_irq_byname(pdev, match_data->irqs[i].name);
if (irq < 0) {
dev_err(dev, "no irq %s in dts\n", match_data->irqs[i].name);
ret = irq;
goto err;
}
ret = devm_request_irq(dev, irq,
match_data->irqs[i].irq_hdl,
IRQF_SHARED,
dev_driver_string(dev),
dev);
if (ret < 0) {
dev_err(dev, "request %s failed: %d\n",
match_data->irqs[i].name, ret);
goto err;
}
}
for (i = 0; i < match_data->clks_num; i++) {
struct clk *clk = devm_clk_get(dev, match_data->clks[i]);
if (IS_ERR(clk)) {
dev_err(dev, "failed to get %s\n",
match_data->clks[i]);
ret = PTR_ERR(clk);
goto err;
}
hw_dev->clks[i] = clk;
}
hw_dev->clks_num = match_data->clks_num;
hw_dev->reset = devm_reset_control_array_get(dev, false, false);
if (IS_ERR(hw_dev->reset)) {
dev_info(dev, "failed to get cru reset\n");
hw_dev->reset = NULL;
}
hw_dev->dev_num = 0;
hw_dev->cur_dev_id = 0;
hw_dev->pre_dev_id = -1;
hw_dev->vpss_ver = match_data->vpss_ver;
mutex_init(&hw_dev->dev_lock);
spin_lock_init(&hw_dev->reg_lock);
atomic_set(&hw_dev->refcnt, 0);
INIT_LIST_HEAD(&hw_dev->list);
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++)
hw_dev->is_ofl_ch[i] = false;
hw_dev->is_ofl_cmsc = false;
hw_dev->is_single = true;
hw_dev->is_dma_contig = true;
hw_dev->is_shutdown = false;
hw_dev->is_mmu = is_iommu_enable(dev);
ret = of_reserved_mem_device_init(dev);
if (ret) {
is_mem_reserved = false;
if (!hw_dev->is_mmu)
dev_info(dev, "No reserved memory region. default cma area!\n");
}
if (hw_dev->is_mmu && !is_mem_reserved)
hw_dev->is_dma_contig = false;
hw_dev->mem_ops = &vb2_cma_sg_memops;
rkvpss_register_offline(hw_dev);
pm_runtime_enable(&pdev->dev);
return 0;
err:
return ret;
}
static int rkvpss_hw_remove(struct platform_device *pdev)
{
struct rkvpss_hw_dev *hw_dev = platform_get_drvdata(pdev);
rkvpss_unregister_offline(hw_dev);
pm_runtime_disable(&pdev->dev);
mutex_destroy(&hw_dev->dev_lock);
return 0;
}
static void rkvpss_hw_shutdown(struct platform_device *pdev)
{
struct rkvpss_hw_dev *hw_dev = platform_get_drvdata(pdev);
hw_dev->is_shutdown = true;
if (pm_runtime_active(&pdev->dev)) {
rkvpss_hw_write(hw_dev, RKVPSS_VPSS_IMSC, 0);
rkvpss_hw_write(hw_dev, RKVPSS_MI_IMSC, 0);
rkvpss_hw_write(hw_dev, RKVPSS_VPSS_RESET, RKVPSS_SOFT_RST);
}
}
static int __maybe_unused rkvpss_runtime_suspend(struct device *dev)
{
struct rkvpss_hw_dev *hw_dev = dev_get_drvdata(dev);
rkvpss_hw_write(hw_dev, RKVPSS_MI_IMSC, 0);
rkvpss_hw_write(hw_dev, RKVPSS_VPSS_IMSC, 0);
disable_sys_clk(hw_dev);
return 0;
}
static int __maybe_unused rkvpss_runtime_resume(struct device *dev)
{
struct rkvpss_hw_dev *hw_dev = dev_get_drvdata(dev);
void __iomem *base = hw_dev->base_addr;
int i;
enable_sys_clk(hw_dev);
rkvpss_soft_reset(hw_dev);
for (i = 0; i < hw_dev->dev_num; i++) {
void *buf = hw_dev->vpss[i]->sw_base_addr;
memset(buf, 0, RKVPSS_SW_REG_SIZE_MAX);
memcpy_fromio(buf, base, RKVPSS_SW_REG_SIZE);
}
return 0;
}
static const struct dev_pm_ops rkvpss_hw_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(rkvpss_runtime_suspend,
rkvpss_runtime_resume, NULL)
};
static struct platform_driver rkvpss_hw_drv = {
.driver = {
.name = "rkvpss_hw",
.of_match_table = of_match_ptr(rkvpss_hw_of_match),
.pm = &rkvpss_hw_pm_ops,
},
.probe = rkvpss_hw_probe,
.remove = rkvpss_hw_remove,
.shutdown = rkvpss_hw_shutdown,
};
static int __init rkvpss_hw_drv_init(void)
{
int ret;
ret = platform_driver_register(&rkvpss_hw_drv);
if (!ret)
ret = platform_driver_register(&rkvpss_plat_drv);
return ret;
}
static void __exit rkvpss_hw_drv_exit(void)
{
platform_driver_unregister(&rkvpss_plat_drv);
platform_driver_unregister(&rkvpss_hw_drv);
}
module_init(rkvpss_hw_drv_init);
module_exit(rkvpss_hw_drv_exit);

View file

@ -0,0 +1,70 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2023 Rockchip Electronics Co., Ltd. */
#ifndef _RKVPSS_HW_H
#define _RKVPSS_HW_H
#include "common.h"
#include "vpss_offline.h"
#define VPSS_MAX_BUS_CLK 4
#define VPSS_MAX_DEV 8
struct vpss_clk_info {
u32 clk_rate;
u32 refer_data;
};
struct vpss_match_data {
int clks_num;
const char * const *clks;
enum rkvpss_ver vpss_ver;
struct irqs_data *irqs;
int num_irqs;
};
struct rkvpss_hw_dev {
struct device *dev;
void __iomem *base_addr;
const struct vpss_match_data *match_data;
const struct vpss_clk_info *clk_rate_tbl;
struct reset_control *reset;
struct clk *clks[VPSS_MAX_BUS_CLK];
struct rkvpss_device *vpss[VPSS_MAX_DEV];
struct rkvpss_offline_dev ofl_dev;
struct list_head list;
int clk_rate_tbl_num;
int clks_num;
int dev_num;
int cur_dev_id;
int pre_dev_id;
unsigned long core_clk_min;
unsigned long core_clk_max;
enum rkvpss_ver vpss_ver;
/* lock for multi dev */
struct mutex dev_lock;
spinlock_t reg_lock;
atomic_t refcnt;
const struct vb2_mem_ops *mem_ops;
bool is_ofl_ch[RKVPSS_OUTPUT_MAX];
bool is_ofl_cmsc;
bool is_mmu;
bool is_single;
bool is_dma_contig;
bool is_shutdown;
};
#define RKVPSS_ZME_TAP_COE(x, y) (((x) & 0x3ff) | (((y) & 0x3ff) << 16))
extern const s16 rkvpss_zme_tap8_coe[11][17][8];
extern const s16 rkvpss_zme_tap6_coe[11][17][8];
int rkvpss_get_zme_tap_coe_index(int ratio);
void rkvpss_cmsc_slop(struct rkvpss_cmsc_point *p0,
struct rkvpss_cmsc_point *p1,
int *k, int *hor);
void rkvpss_soft_reset(struct rkvpss_hw_dev *hw_dev);
void rkvpss_hw_write(struct rkvpss_hw_dev *hw_dev, u32 reg, u32 val);
u32 rkvpss_hw_read(struct rkvpss_hw_dev *hw_dev, u32 reg);
void rkvpss_hw_set_bits(struct rkvpss_hw_dev *hw, u32 reg, u32 mask, u32 val);
void rkvpss_hw_clear_bits(struct rkvpss_hw_dev *hw, u32 reg, u32 mask);
#endif

View file

@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) Rockchip Electronics Co., Ltd. */
#include <linux/clk.h>
#include <linux/proc_fs.h>
#include <linux/sem.h>
#include <linux/seq_file.h>
#include <media/v4l2-common.h>
#include "dev.h"
#include "procfs.h"
#include "regs.h"
#include "version.h"
#ifdef CONFIG_PROC_FS
static int vpss_show(struct seq_file *p, void *v)
{
struct rkvpss_device *dev = p->private;
enum rkvpss_state state = dev->vpss_sdev.state;
u32 val;
seq_printf(p, "%-10s Version:v%02x.%02x.%02x\n",
dev->name,
RKVPSS_DRIVER_VERSION >> 16,
(RKVPSS_DRIVER_VERSION & 0xff00) >> 8,
RKVPSS_DRIVER_VERSION & 0x00ff);
for (val = 0; val < dev->hw_dev->clks_num; val++) {
seq_printf(p, "%-10s %ld\n",
dev->hw_dev->match_data->clks[val],
clk_get_rate(dev->hw_dev->clks[val]));
}
if (state != VPSS_START)
return 0;
seq_printf(p, "%-10s Cnt:%d ErrCnt:%d\n",
"Interrupt",
dev->isr_cnt,
dev->isr_err_cnt);
return 0;
}
static int vpss_open(struct inode *inode, struct file *file)
{
struct rkvpss_device *data = pde_data(inode);
return single_open(file, vpss_show, data);
}
static const struct proc_ops ops = {
.proc_open = vpss_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
int rkvpss_proc_init(struct rkvpss_device *dev)
{
dev->procfs = proc_create_data(dev->name, 0, NULL, &ops, dev);
if (!dev->procfs)
return -EINVAL;
return 0;
}
void rkvpss_proc_cleanup(struct rkvpss_device *dev)
{
if (dev->procfs)
remove_proc_entry(dev->name, NULL);
dev->procfs = NULL;
}
#endif /* CONFIG_PROC_FS */

View file

@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */
#ifndef _RKVPSS_PROCFS_H
#define _RKVPSS_PROCFS_H
#ifdef CONFIG_PROC_FS
int rkvpss_proc_init(struct rkvpss_device *dev);
void rkvpss_proc_cleanup(struct rkvpss_device *dev);
#else
static inline int rkvpss_proc_init(struct rkvpss_device *dev) { return 0; }
static inline void rkvpss_proc_cleanup(struct rkvpss_device *dev) {}
#endif
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,172 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */
#ifndef _RKVPSS_STREAM_H
#define _RKVPSS_STREAM_H
#include <linux/interrupt.h>
#include "common.h"
struct rkvpss_stream;
/*
* fourcc: pixel format
* fmt_type: helper filed for pixel format
* cplanes: number of colour planes
* mplanes: number of stored memory planes
* wr_fmt: defines format for reg
* bpp: bits per pixel
*/
struct capture_fmt {
u32 fourcc;
u8 fmt_type;
u8 cplanes;
u8 mplanes;
u8 swap;
u32 wr_fmt;
u32 output_fmt;
u8 bpp[VIDEO_MAX_PLANES];
};
/* Different config for stream */
struct stream_config {
const struct capture_fmt *fmts;
unsigned int fmt_size;
u32 frame_end_id;
/* registers */
struct {
u32 ctrl;
u32 update;
u32 src_size;
u32 dst_size;
u32 hy_fac;
u32 hc_fac;
u32 vy_fac;
u32 vc_fac;
u32 hy_offs;
u32 hc_offs;
u32 vy_offs;
u32 vc_offs;
u32 hy_size;
u32 hc_size;
u32 hy_offs_mi;
u32 hc_offs_mi;
u32 in_crop_offs;
u32 ctrl_shd;
u32 src_size_shd;
u32 dst_size_shd;
u32 hy_fac_shd;
u32 hc_fac_shd;
u32 vy_fac_shd;
u32 vc_fac_shd;
u32 hy_offs_shd;
u32 hc_offs_shd;
u32 vy_offs_shd;
u32 vc_offs_shd;
u32 hy_size_shd;
u32 hc_size_shd;
u32 hy_offs_mi_shd;
u32 hc_offs_mi_shd;
u32 in_crop_offs_shd;
} scale;
struct {
u32 ctrl;
u32 update;
u32 h_offs;
u32 v_offs;
u32 h_size;
u32 v_size;
u32 ctrl_shd;
u32 h_offs_shd;
u32 v_offs_shd;
u32 h_size_shd;
u32 v_size_shd;
} crop;
struct {
u32 ctrl;
u32 stride;
u32 y_base;
u32 uv_base;
u32 y_size;
u32 uv_size;
u32 y_offs_cnt;
u32 uv_offs_cnt;
u32 y_pic_width;
u32 y_pic_size;
u32 ctrl_shd;
u32 y_shd;
u32 uv_shd;
} mi;
};
/* Different reg ops for stream */
struct streams_ops {
void (*config_mi)(struct rkvpss_stream *stream);
void (*enable_mi)(struct rkvpss_stream *stream);
void (*disable_mi)(struct rkvpss_stream *stream);
void (*update_mi)(struct rkvpss_stream *stream);
void (*frame_start)(struct rkvpss_stream *stream, u32 irq);
int (*is_stopped)(struct rkvpss_stream *stream);
int (*limit_check)(struct rkvpss_stream *stream,
struct v4l2_pix_format_mplane *try_fmt);
};
/* struct rkvpss_stream - VPSS stream video device
* id: stream video identify
* buf_queue: queued buffer list
* curr_buf: the buffer used for current frame
* next_buf: the buffer used for next frame
* done: wait frame end event queue
* vbq_lock: lock to protect buf_queue
* out_cap_fmt: the output of vpss
* out_fmt: the output of v4l2 pix format
* last_module: last function module
* streaming: stream start flag
* stopping: stream stop flag
* linked: link enable flag
*/
struct rkvpss_stream {
struct rkvpss_device *dev;
struct rkvpss_vdev_node vnode;
struct list_head buf_queue;
struct list_head buf_done_list;
struct tasklet_struct buf_done_tasklet;
struct rkvpss_buffer *curr_buf;
struct rkvpss_buffer *next_buf;
wait_queue_head_t done;
spinlock_t vbq_lock;
struct streams_ops *ops;
struct stream_config *config;
struct capture_fmt out_cap_fmt;
struct v4l2_rect crop;
struct v4l2_pix_format_mplane out_fmt;
int id;
bool streaming;
bool stopping;
bool linked;
bool flip_en;
bool is_crop_upd;
bool is_mf_upd;
bool is_pause;
};
/* rkvpss stream device */
struct rkvpss_stream_vdev {
struct rkvpss_stream stream[RKVPSS_OUTPUT_MAX];
atomic_t refcnt;
};
void rkvpss_cmsc_config(struct rkvpss_device *dev, bool sync);
void rkvpss_stream_default_fmt(struct rkvpss_device *dev, u32 id,
u32 width, u32 height, u32 pixelformat);
void rkvpss_isr(struct rkvpss_device *dev, u32 mis_val);
void rkvpss_mi_isr(struct rkvpss_device *dev, u32 mis_val);
void rkvpss_unregister_stream_vdevs(struct rkvpss_device *dev);
int rkvpss_register_stream_vdevs(struct rkvpss_device *dev);
#endif

View file

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */
#ifndef _RKVPSS_VERSION_H
#define _RKVPSS_VERSION_H
#include <linux/version.h>
#include <linux/rk-vpss-config.h>
/*
* RKVPSS DRIVER VERSION NOTE
*
* v0.1.0:
* 1. First version;
*
*/
#define RKVPSS_DRIVER_VERSION VPSS_API_VERSION
#endif

View file

@ -0,0 +1,387 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
#include <linux/pm_runtime.h>
#include <linux/videodev2.h>
#include <media/media-entity.h>
#include <media/v4l2-event.h>
#include "dev.h"
#include "regs.h"
static const struct vpsssd_fmt rkvpss_formats[] = {
{
.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.fourcc = V4L2_PIX_FMT_NV16,
},
};
static int rkvpss_subdev_link_setup(struct media_entity *entity,
const struct media_pad *local,
const struct media_pad *remote,
u32 flags)
{
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct rkvpss_subdev *vpss_sdev;
struct rkvpss_device *dev;
struct rkvpss_stream_vdev *vdev;
struct rkvpss_stream *stream = NULL;
if (local->index != RKVPSS_PAD_SINK &&
local->index != RKVPSS_PAD_SOURCE)
return 0;
if (!sd)
return -ENODEV;
vpss_sdev = v4l2_get_subdevdata(sd);
if (!vpss_sdev)
return -ENODEV;
dev = vpss_sdev->dev;
if (!dev)
return -ENODEV;
vdev = &dev->stream_vdev;
if (vpss_sdev->state & VPSS_START)
return -EBUSY;
if (!strcmp(remote->entity->name, S0_VDEV_NAME)) {
stream = &vdev->stream[RKVPSS_OUTPUT_CH0];
} else if (!strcmp(remote->entity->name, S1_VDEV_NAME)) {
stream = &vdev->stream[RKVPSS_OUTPUT_CH1];
} else if (!strcmp(remote->entity->name, S2_VDEV_NAME)) {
stream = &vdev->stream[RKVPSS_OUTPUT_CH2];
} else if (!strcmp(remote->entity->name, S3_VDEV_NAME)) {
stream = &vdev->stream[RKVPSS_OUTPUT_CH3];
} else if (strstr(remote->entity->name, "rkisp")) {
if (flags & MEDIA_LNK_FL_ENABLED)
dev->inp = INP_ISP;
else
dev->inp = INP_INVAL;
}
if (stream)
stream->linked = flags & MEDIA_LNK_FL_ENABLED;
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, "input:%d\n", dev->inp);
return 0;
}
static int rkvpss_sd_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
struct rkvpss_subdev *sdev = v4l2_get_subdevdata(sd);
struct rkvpss_device *dev = sdev->dev;
struct v4l2_mbus_framefmt *mf;
int ret = 0;
if (!fmt)
return -EINVAL;
if (fmt->pad != RKVPSS_PAD_SINK &&
fmt->pad != RKVPSS_PAD_SOURCE)
return -EINVAL;
mf = &fmt->format;
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
if (!sd_state)
return -EINVAL;
mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
}
*mf = sdev->in_fmt;
if (fmt->pad == RKVPSS_PAD_SINK && sdev->dev->inp == INP_ISP) {
ret = v4l2_subdev_call(dev->remote_sd, pad, get_fmt, sd_state, fmt);
if (!ret) {
if (sdev->in_fmt.width != mf->width ||
sdev->in_fmt.height != mf->height) {
sdev->out_fmt.width = mf->width;
sdev->out_fmt.height = mf->height;
rkvpss_pipeline_default_fmt(dev);
}
sdev->in_fmt = *mf;
}
} else {
*mf = sdev->in_fmt;
mf->width = sdev->out_fmt.width;
mf->height = sdev->out_fmt.height;
}
return ret;
}
static int rkvpss_sd_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
struct rkvpss_subdev *sdev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf;
if (!fmt)
return -EINVAL;
/* format from isp output */
if (fmt->pad == RKVPSS_PAD_SINK && sdev->dev->inp == INP_ISP)
return rkvpss_sd_get_fmt(sd, sd_state, fmt);
mf = &fmt->format;
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
if (!sd_state)
return -EINVAL;
mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
}
if (fmt->pad == RKVPSS_PAD_SINK) {
sdev->in_fmt = *mf;
} else {
sdev->out_fmt.width = mf->width;
sdev->out_fmt.height = mf->height;
}
return 0;
}
static int rkvpss_sd_s_stream(struct v4l2_subdev *sd, int on)
{
struct rkvpss_subdev *sdev = v4l2_get_subdevdata(sd);
struct rkvpss_device *dev = sdev->dev;
u32 val, w = sdev->in_fmt.width, h = sdev->in_fmt.height;
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev,
"s_stream on:%d %dx%d\n", on, w, h);
if (!on) {
rkvpss_clear_bits(dev, RKVPSS_VPSS_ONLINE, RKVPSS_ONLINE_MODE_MASK);
rkvpss_clear_bits(dev, RKVPSS_VPSS_IMSC, RKVPSS_ISP_ALL_FRM_END);
sdev->state = VPSS_STOP;
atomic_dec(&dev->hw_dev->refcnt);
return 0;
}
sdev->frame_seq = -1;
sdev->frame_timestamp = 0;
atomic_inc(&dev->hw_dev->refcnt);
dev->cmsc_upd = true;
rkvpss_cmsc_config(dev, true);
rkvpss_write(dev, RKVPSS_VPSS_ONLINE2_SIZE, h << 16 | w);
val = RKVPSS_CFG_FORCE_UPD | RKVPSS_CFG_GEN_UPD | RKVPSS_MIR_GEN_UPD;
if (!dev->hw_dev->is_ofl_cmsc)
val |= RKVPSS_MIR_FORCE_UPD;
rkvpss_write(dev, RKVPSS_VPSS_UPDATE, val);
rkvpss_set_bits(dev, RKVPSS_VPSS_IMSC, 0, RKVPSS_ISP_ALL_FRM_END);
sdev->state |= VPSS_START;
return 0;
}
static int rkvpss_sd_s_rx_buffer(struct v4l2_subdev *sd,
void *buf, unsigned int *size)
{
return 0;
}
static int rkvpss_sd_s_power(struct v4l2_subdev *sd, int on)
{
struct rkvpss_subdev *sdev = v4l2_get_subdevdata(sd);
struct rkvpss_device *dev = sdev->dev;
int ret;
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev,
"power on:%d\n", on);
if (on) {
if (dev->inp == INP_ISP) {
struct v4l2_subdev_format fmt = {
.pad = RKVPSS_PAD_SINK,
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
if (ret) {
v4l2_err(&dev->v4l2_dev,
"%s get isp format fail:%d\n", __func__, ret);
return ret;
}
ret = v4l2_subdev_call(dev->remote_sd, core, s_power, 1);
if (ret < 0) {
v4l2_err(&dev->v4l2_dev,
"%s set isp power on fail:%d\n", __func__, ret);
return ret;
}
}
ret = pm_runtime_get_sync(dev->dev);
if (ret < 0) {
v4l2_err(&dev->v4l2_dev,
"%s runtime get fail:%d\n", __func__, ret);
if (dev->inp == INP_ISP)
v4l2_subdev_call(dev->remote_sd, core, s_power, 0);
}
} else {
if (dev->inp == INP_ISP)
v4l2_subdev_call(dev->remote_sd, core, s_power, 0);
ret = pm_runtime_put_sync(dev->dev);
}
if (ret < 0)
v4l2_err(sd, "%s on:%d failed:%d\n", __func__, on, ret);
return ret;
}
static int rkvpss_sof(struct rkvpss_subdev *sdev, struct rkisp_vpss_sof *info)
{
struct rkvpss_device *dev = sdev->dev;
struct rkvpss_hw_dev *hw = dev->hw_dev;
struct rkvpss_stream *stream;
int i;
if (!info)
return -EINVAL;
sdev->frame_seq = info->seq;
sdev->frame_timestamp = info->timestamp;
rkvpss_cmsc_config(dev, !info->irq);
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) {
stream = &dev->stream_vdev.stream[i];
if (!stream->streaming)
continue;
if (stream->ops->frame_start)
stream->ops->frame_start(stream, info->irq);
}
if (!info->irq && !hw->is_single) {
hw->cur_dev_id = dev->dev_id;
rkvpss_update_regs(dev, RKVPSS_VPSS_CTRL, RKVPSS_VPSS_ONLINE2_SIZE);
rkvpss_update_regs(dev, RKVPSS_VPSS_IRQ_CFG, RKVPSS_VPSS_IMSC);
rkvpss_update_regs(dev, RKVPSS_VPSS_Y2R_COE00, RKVPSS_VPSS_Y2R_OFF2);
rkvpss_update_regs(dev, RKVPSS_CMSC_INTSCT_CORR, RKVPSS_CMSC_WIN7_L3_SLP);
rkvpss_update_regs(dev, RKVPSS_CROP1_0_H_OFFS, RKVPSS_CROP1_3_V_SIZE);
rkvpss_update_regs(dev, RKVPSS_ZME_Y_HOR_COE0_10, RKVPSS_ZME_UV_VER_COE16_76);
rkvpss_update_regs(dev, RKVPSS_ZME_H_SIZE, RKVPSS_ZME_UV_YSCL_FACTOR);
rkvpss_update_regs(dev, RKVPSS_SCALE1_SRC_SIZE, RKVPSS_SCALE1_IN_CROP_OFFSET);
rkvpss_update_regs(dev, RKVPSS_SCALE2_SRC_SIZE, RKVPSS_SCALE2_IN_CROP_OFFSET);
rkvpss_update_regs(dev, RKVPSS_SCALE3_SRC_SIZE, RKVPSS_SCALE3_IN_CROP_OFFSET);
rkvpss_update_regs(dev, RKVPSS_MI_WR_WRAP_CTRL, RKVPSS_MI_IMSC);
rkvpss_update_regs(dev, RKVPSS_MI_CHN0_WR_CTRL, RKVPSS_MI_CHN3_WR_LINE_CNT);
rkvpss_update_regs(dev, RKVPSS_MI_WR_CTRL, RKVPSS_MI_WR_INIT);
rkvpss_update_regs(dev, RKVPSS_SCALE3_CTRL, RKVPSS_SCALE3_UPDATE);
rkvpss_update_regs(dev, RKVPSS_SCALE2_CTRL, RKVPSS_SCALE2_UPDATE);
rkvpss_update_regs(dev, RKVPSS_SCALE1_CTRL, RKVPSS_SCALE1_UPDATE);
rkvpss_update_regs(dev, RKVPSS_ZME_CTRL, RKVPSS_ZME_UPDATE);
rkvpss_update_regs(dev, RKVPSS_CROP1_CTRL, RKVPSS_CROP1_UPDATE);
rkvpss_update_regs(dev, RKVPSS_CMSC_CTRL, RKVPSS_CMSC_UPDATE);
rkvpss_update_regs(dev, RKVPSS_VPSS_UPDATE, RKVPSS_VPSS_UPDATE);
}
return 0;
}
static long rkvpss_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
struct rkvpss_subdev *sdev = v4l2_get_subdevdata(sd);
long ret = 0;
switch (cmd) {
case RKISP_VPSS_CMD_SOF:
ret = rkvpss_sof(sdev, arg);
break;
default:
ret = -ENOIOCTLCMD;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long rkvpss_compat_ioctl32(struct v4l2_subdev *sd,
unsigned int cmd, unsigned long arg)
{
long ret = 0;
return ret;
}
#endif
static const struct media_entity_operations rkvpss_sd_media_ops = {
.link_setup = rkvpss_subdev_link_setup,
.link_validate = v4l2_subdev_link_validate,
};
static const struct v4l2_subdev_pad_ops rkvpss_sd_pad_ops = {
.get_fmt = rkvpss_sd_get_fmt,
.set_fmt = rkvpss_sd_set_fmt,
};
static const struct v4l2_subdev_video_ops rkvpss_sd_video_ops = {
.s_stream = rkvpss_sd_s_stream,
.s_rx_buffer = rkvpss_sd_s_rx_buffer,
};
static const struct v4l2_subdev_core_ops rkvpss_sd_core_ops = {
.s_power = rkvpss_sd_s_power,
.ioctl = rkvpss_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = rkvpss_compat_ioctl32,
#endif
};
static const struct v4l2_subdev_ops rkvpss_sd_ops = {
.core = &rkvpss_sd_core_ops,
.video = &rkvpss_sd_video_ops,
.pad = &rkvpss_sd_pad_ops,
};
static void rkvpss_subdev_default_fmt(struct rkvpss_subdev *sdev)
{
struct v4l2_mbus_framefmt *in_fmt = &sdev->in_fmt;
struct vpsssd_fmt *out_fmt = &sdev->out_fmt;
in_fmt->width = RKVPSS_DEFAULT_WIDTH;
in_fmt->height = RKVPSS_DEFAULT_HEIGHT;
in_fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
*out_fmt = rkvpss_formats[0];
out_fmt->width = RKVPSS_DEFAULT_WIDTH;
out_fmt->height = RKVPSS_DEFAULT_HEIGHT;
}
int rkvpss_register_subdev(struct rkvpss_device *dev,
struct v4l2_device *v4l2_dev)
{
struct rkvpss_subdev *vpss_sdev = &dev->vpss_sdev;
struct v4l2_subdev *sd;
int ret;
spin_lock_init(&dev->cmsc_lock);
memset(vpss_sdev, 0, sizeof(*vpss_sdev));
vpss_sdev->dev = dev;
sd = &vpss_sdev->sd;
vpss_sdev->state = VPSS_STOP;
v4l2_subdev_init(sd, &rkvpss_sd_ops);
//sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
sd->entity.ops = &rkvpss_sd_media_ops;
snprintf(sd->name, sizeof(sd->name), "rkvpss-subdev");
sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_COMPOSER;
vpss_sdev->pads[RKVPSS_PAD_SINK].flags =
MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
vpss_sdev->pads[RKVPSS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&sd->entity, RKVPSS_PAD_MAX, vpss_sdev->pads);
if (ret < 0)
return ret;
sd->owner = THIS_MODULE;
v4l2_set_subdevdata(sd, vpss_sdev);
sd->grp_id = GRP_ID_VPSS;
ret = v4l2_device_register_subdev(v4l2_dev, sd);
if (ret < 0)
goto free_media;
rkvpss_subdev_default_fmt(vpss_sdev);
return ret;
free_media:
media_entity_cleanup(&sd->entity);
v4l2_err(sd, "Failed to register subdev, ret:%d\n", ret);
return ret;
}
void rkvpss_unregister_subdev(struct rkvpss_device *dev)
{
struct v4l2_subdev *sd = &dev->vpss_sdev.sd;
v4l2_device_unregister_subdev(sd);
media_entity_cleanup(&sd->entity);
}

View file

@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2023 Fuzhou Rockchip Electronics Co., Ltd. */
#ifndef _RKVPSS_VPSS_H
#define _RKVPSS_VPSS_H
#include "common.h"
#define GRP_ID_VPSS BIT(0)
enum rkvpss_pad {
RKVPSS_PAD_SINK,
RKVPSS_PAD_SOURCE,
RKVPSS_PAD_MAX
};
enum rkvpss_state {
VPSS_STOP = 0,
VPSS_FS = BIT(0),
VPSS_FE = BIT(1),
VPSS_START = BIT(8),
VPSS_RX_START = BIT(9),
};
struct vpsssd_fmt {
u32 mbus_code;
u32 fourcc;
u32 width;
u32 height;
u8 wr_fmt;
};
struct rkvpss_subdev {
struct rkvpss_device *dev;
struct v4l2_subdev sd;
struct media_pad pads[RKVPSS_PAD_MAX];
struct v4l2_mbus_framefmt in_fmt;
struct vpsssd_fmt out_fmt;
u32 frame_seq;
/* timestamp in ns */
u64 frame_timestamp;
enum rkvpss_state state;
};
int rkvpss_register_subdev(struct rkvpss_device *dev,
struct v4l2_device *v4l2_dev);
void rkvpss_unregister_subdev(struct rkvpss_device *dev);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */
#ifndef _RKVPSS_OFFLINE_H
#define _RKVPSS_OFFLINE_H
#include "hw.h"
struct rkvpss_offline_dev {
struct rkvpss_hw_dev *hw;
struct v4l2_device v4l2_dev;
struct video_device vfd;
struct mutex apilock;
struct completion cmpl;
struct list_head list;
};
int rkvpss_register_offline(struct rkvpss_hw_dev *hw);
void rkvpss_unregister_offline(struct rkvpss_hw_dev *hw);
void rkvpss_offline_irq(struct rkvpss_hw_dev *hw, u32 irq);
#endif

View file

@ -0,0 +1,269 @@
/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR MIT)
*
* Copyright (C) 2023 Rockchip Electronics Co., Ltd.
*/
#ifndef _UAPI_RK_VPSS_CONFIG_H
#define _UAPI_RK_VPSS_CONFIG_H
#include <linux/types.h>
#include <linux/v4l2-controls.h>
#define VPSS_API_VERSION KERNEL_VERSION(0, 1, 0)
/* |-------------------------------------------------------------------------------------------|
* | mirror_cmsc_en |
* | |1------------------------>| |
* |ISP->| |->|crop1->scl->ddr channelX| isp->vpss online mode |
* | |0---->| |->| media v4l2 driver |
* |------------|->mirror->cmsc->|-------------------------------------------------------------|
* | |1---->| |->| |
* |DDR->| |->|crop0->scl->aspt->ddr channelY| ddr->vpss offline mode |
* | |0------------------------>| independent driver |
* | mirror_cmsc_en |
* |-------------------------------------------------------------------------------------------|
* mirror/cover mux to ISP or DDR
* channelX or channelY = 0,1,2,3 but X != Y
* ioctl RKVPSS_CMD_MODULE_SEL to select function using
*/
/******vpss(online mode) v4l2 ioctl***************************/
/* set before VIDIOC_S_FMT if dynamically changing output resolution */
#define RKVPSS_CMD_SET_STREAM_MAX_SIZE \
_IOW('V', BASE_VIDIOC_PRIVATE + 0, struct rkvpss_stream_size)
/* for dynamically changing output resolution:
* SET_STREAM_SUSPEND->VIDIOC_S_FMT/VIDIOC_S_SELECTION->SET_STREAM_RESUME
*/
#define RKVPSS_CMD_SET_STREAM_SUSPEND \
_IO('V', BASE_VIDIOC_PRIVATE + 1)
#define RKVPSS_CMD_SET_STREAM_RESUME \
_IO('V', BASE_VIDIOC_PRIVATE + 2)
#define RKVPSS_CMD_GET_MIRROR_FLIP \
_IOR('V', BASE_VIDIOC_PRIVATE + 3, struct rkvpss_mirror_flip)
#define RKVPSS_CMD_SET_MIRROR_FLIP \
_IOW('V', BASE_VIDIOC_PRIVATE + 4, struct rkvpss_mirror_flip)
#define RKVPSS_CMD_GET_CMSC \
_IOR('V', BASE_VIDIOC_PRIVATE + 5, struct rkvpss_cmsc_cfg)
#define RKVPSS_CMD_SET_CMSC \
_IOW('V', BASE_VIDIOC_PRIVATE + 6, struct rkvpss_cmsc_cfg)
/******vpss(offline mode) independent video ioctl****************/
#define RKVPSS_CMD_MODULE_SEL \
_IOW('V', BASE_VIDIOC_PRIVATE + 50, struct rkvpss_module_sel)
#define RKVPSS_CMD_FRAME_HANDLE \
_IOW('V', BASE_VIDIOC_PRIVATE + 51, struct rkvpss_frame_cfg)
/* request vpss to alloc or external dma buf add to vpss */
#define RKVPSS_CMD_BUF_ADD \
_IOWR('V', BASE_VIDIOC_PRIVATE + 52, struct rkvpss_buf_info)
#define RKVPSS_CMD_BUF_DEL \
_IOW('V', BASE_VIDIOC_PRIVATE + 53, struct rkvpss_buf_info)
/********************************************************************/
/* struct rkvpss_mirror_flip
* mirror: global for all output stream
* flip: independent for all output stream
*/
struct rkvpss_mirror_flip {
unsigned char mirror;
unsigned char flip;
} __attribute__ ((packed));
/* struct rkvpss_stream_size
* set max resolution before VIDIOC_S_FMT for init buffer
*/
struct rkvpss_stream_size {
unsigned int max_width;
unsigned int max_height;
} __attribute__ ((packed));
#define RKVPSS_CMSC_WIN_MAX 8
#define RKVPSS_CMSC_POINT_MAX 4
#define RKVPSS_CMSC_COVER_MODE 0
#define RKVPSS_CMSC_MOSAIC_MODE 1
struct rkvpss_cmsc_point {
unsigned int x;
unsigned int y;
} __attribute__ ((packed));
/* struct rkvpss_cmsc_win
* Priacy Mask Window configture, support windows
*
* win_index: window index 0~8. windows overlap, priority win8 > win0.
* mode: RKVPSS_CMSC_MOSAIC_MODE:mosaic mode, RKVPSS_CMSC_COVER_MODE:cover mode
* cover_color_y: cover mode Y value[0, 255].
* cover_color_u: cover mode U value[0, 255].
* cover_color_v: cover mode V value[0, 255].
* cover_color_a: cover mode alpha value[0, 15], 0 is transparent.
* point: four coordinates of any quadrilateral, the top left of the input image is the origin.
* point0 must be the vertex, point0~ponit3 clockwise, and four coordinates should different.
*/
struct rkvpss_cmsc_win {
unsigned short win_en;
/* following share for all channel when same win index */
unsigned short mode;
unsigned char cover_color_y;
unsigned char cover_color_u;
unsigned char cover_color_v;
unsigned char cover_color_a;
struct rkvpss_cmsc_point point[RKVPSS_CMSC_POINT_MAX];
} __attribute__ ((packed));
/* struct rkvpss_cmsc_cfg
* cover and mosaic configure
* win: priacy mask window
* mosaic_block: Mosaic block size, 0:8x8 1:16x16 2:32x32 3:64x64, share for all windows
* width_ro: vpss full resolution.
* height_ro: vpss full resolution.
*/
struct rkvpss_cmsc_cfg {
struct rkvpss_cmsc_win win[RKVPSS_CMSC_WIN_MAX];
unsigned int mosaic_block;
unsigned int width_ro;
unsigned int height_ro;
} __attribute__ ((packed));
/* struct rkisp_aspt_cfg _____background____
* aspective ratio for image background color filling |offs __image___ c |
* width: width of background. 2 align | |scl_width | o |
* height: height of background. 2 align | |scl_height| l |
* h_offs: horizontal offset of image in the background. 2 align | |__________| o |
* v_offs: vertical offset of image in the background. 2 align | color r |
* color_y: background y color. 0~255 |___________________|
* color_u: background u color. 0~255
* color_v: background v color. 0~255
* enable: function enable
*/
struct rkvpss_aspt_cfg {
unsigned int width;
unsigned int height;
unsigned int h_offs;
unsigned int v_offs;
unsigned char color_y;
unsigned char color_u;
unsigned char color_v;
unsigned char enable;
} __attribute__ ((packed));
/********************************************************************/
enum {
RKVPSS_OUTPUT_CH0 = 0,
RKVPSS_OUTPUT_CH1,
RKVPSS_OUTPUT_CH2,
RKVPSS_OUTPUT_CH3,
RKVPSS_OUTPUT_MAX,
};
/* struct rkvpss_module_sel
* selection module for vpss offline mode, default select to online mode.
* mirror_cmsc_en 1:miiror_cmsc sel to offline mode, 0:sel to online mode
* ch_en 1:channel sel to offline mode, 0:sel to online mode
*/
struct rkvpss_module_sel {
unsigned char mirror_cmsc_en;
unsigned char ch_en[RKVPSS_OUTPUT_MAX];
} __attribute__ ((packed));
/* struct rkvpss_input_cfg
* input configuration of image
*
* width: width of input image, range: 32~4672
* height: height of input image, range: 32~3504
* stride: virtual width of input image, 16 align. auto calculate according to width and format if 0.
* format: V4L2_PIX_FMT_NV12/V4L2_PIX_FMT_NV16/V4L2_PIX_FMT_RGB565/V4L2_PIX_FMT_RGB24/V4L2_PIX_FMT_XBGR32
* buf_fd: dmabuf fd of input image buf
*/
struct rkvpss_input_cfg {
int width;
int height;
int stride;
int format;
int buf_fd;
} __attribute__ ((packed));
/* struct rkvpss_output_cfg __________________
* output channel configuration of image |offs __________ |
* | | ______ | |
* enable: channel enable | | | | | |
* crop_h_offs: horizontal offset of crop, 2 align | | | | | |
* crop_v_offs: vertical offset of crop, 2align | | |scl___| | |
* crop_width: crop output width, 2align | |crop______| |
* crop_height: crop output height, 2align |input_____________|
* scl_width: scale width. CH0 1~8 scale range. CH1/CH2/CH3 1~32 scale range. CH2/CH3 max 1080p with scale.
* scl_height: scale height. CH0 1~6 scale range. CH1/CH2/CH3 1~32 scale range. CH2/CH3 max 1080p with scale.
* stride: virtual width of output image, 16 align. auto calculate according to width and format if 0.
* format: V4L2_PIX_FMT_NV12/V4L2_PIX_FMT_NV16/V4L2_PIX_FMT_GREY/V4L2_PIX_FMT_UYVY for all channel.
* V4L2_PIX_FMT_RGB565/V4L2_PIX_FMT_RGB24/V4L2_PIX_FMT_XBGR32 only for RKVPSS_OUTPUT_CH1.
* flip: flip enable
* buf_fd: dmabuf fd of output image buf
* cmsc: cover and mosaic configure
* aspt: aspective ratio for image background color filling
*/
struct rkvpss_output_cfg {
int enable;
int crop_h_offs;
int crop_v_offs;
int crop_width;
int crop_height;
int scl_width;
int scl_height;
int stride;
int format;
int flip;
int buf_fd;
struct rkvpss_cmsc_cfg cmsc;
struct rkvpss_aspt_cfg aspt;
} __attribute__ ((packed));
#define RKVPSS_DEV_ID_MAX 128
/* struct rkvpss_frame_cfg
* frame handle configure
*
* dev_id: device id, range 0~127.
* sequence: frame sequence
* mirror: mirror enable
* input: input configuration of image
* output: output channel configuration of image
*/
struct rkvpss_frame_cfg {
int dev_id;
int sequence;
int mirror;
struct rkvpss_input_cfg input;
struct rkvpss_output_cfg output[RKVPSS_OUTPUT_MAX];
} __attribute__ ((packed));
#define RKVPSS_BUF_MAX 32
/* struct rkvpss_buf_info
* request vpss to alloc or external dma buf add to vpss.
* dev_id: device id, range 0~127.
* buf_alloc: request vpss alloc buf or no. 0: no alloc using external buf
* buf_cnt: buffer count.
* buf_size: buffer size.
* buf_fd: dma buffer fd. return if buf_alloc=1, other user set for driver.
*/
struct rkvpss_buf_info {
int dev_id;
int buf_alloc;
int buf_cnt;
int buf_size[RKVPSS_BUF_MAX];
int buf_fd[RKVPSS_BUF_MAX];
} __attribute__ ((packed));
#endif