From aae52d34ed73a7474572a31e7fcfcaba80a4ed74 Mon Sep 17 00:00:00 2001 From: Cai YiWei Date: Thu, 21 Dec 2023 16:33:36 +0800 Subject: [PATCH] media: rockchip: vpss: init driver for rk3576 Change-Id: I8fd1af91d055d0ebd30bd9898db6305a51fd7dda Signed-off-by: Cai YiWei --- drivers/media/platform/rockchip/Kconfig | 1 + drivers/media/platform/rockchip/Makefile | 1 + drivers/media/platform/rockchip/vpss/Kconfig | 12 + drivers/media/platform/rockchip/vpss/Makefile | 10 + drivers/media/platform/rockchip/vpss/common.c | 133 ++ drivers/media/platform/rockchip/vpss/common.h | 93 + drivers/media/platform/rockchip/vpss/dev.c | 386 ++++ drivers/media/platform/rockchip/vpss/dev.h | 76 + drivers/media/platform/rockchip/vpss/hw.c | 898 +++++++++ drivers/media/platform/rockchip/vpss/hw.h | 70 + drivers/media/platform/rockchip/vpss/procfs.c | 69 + drivers/media/platform/rockchip/vpss/procfs.h | 15 + drivers/media/platform/rockchip/vpss/regs.h | 1184 +++++++++++ drivers/media/platform/rockchip/vpss/stream.c | 1785 +++++++++++++++++ drivers/media/platform/rockchip/vpss/stream.h | 172 ++ .../media/platform/rockchip/vpss/version.h | 19 + drivers/media/platform/rockchip/vpss/vpss.c | 387 ++++ drivers/media/platform/rockchip/vpss/vpss.h | 49 + .../platform/rockchip/vpss/vpss_offline.c | 1075 ++++++++++ .../platform/rockchip/vpss/vpss_offline.h | 22 + include/uapi/linux/rk-vpss-config.h | 269 +++ 21 files changed, 6726 insertions(+) create mode 100644 drivers/media/platform/rockchip/vpss/Kconfig create mode 100644 drivers/media/platform/rockchip/vpss/Makefile create mode 100644 drivers/media/platform/rockchip/vpss/common.c create mode 100644 drivers/media/platform/rockchip/vpss/common.h create mode 100644 drivers/media/platform/rockchip/vpss/dev.c create mode 100644 drivers/media/platform/rockchip/vpss/dev.h create mode 100644 drivers/media/platform/rockchip/vpss/hw.c create mode 100644 drivers/media/platform/rockchip/vpss/hw.h create mode 100644 drivers/media/platform/rockchip/vpss/procfs.c create mode 100644 drivers/media/platform/rockchip/vpss/procfs.h create mode 100644 drivers/media/platform/rockchip/vpss/regs.h create mode 100644 drivers/media/platform/rockchip/vpss/stream.c create mode 100644 drivers/media/platform/rockchip/vpss/stream.h create mode 100644 drivers/media/platform/rockchip/vpss/version.h create mode 100644 drivers/media/platform/rockchip/vpss/vpss.c create mode 100644 drivers/media/platform/rockchip/vpss/vpss.h create mode 100644 drivers/media/platform/rockchip/vpss/vpss_offline.c create mode 100644 drivers/media/platform/rockchip/vpss/vpss_offline.h create mode 100644 include/uapi/linux/rk-vpss-config.h diff --git a/drivers/media/platform/rockchip/Kconfig b/drivers/media/platform/rockchip/Kconfig index 817b4cb2dc9e..89bb3fa75c1d 100644 --- a/drivers/media/platform/rockchip/Kconfig +++ b/drivers/media/platform/rockchip/Kconfig @@ -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" diff --git a/drivers/media/platform/rockchip/Makefile b/drivers/media/platform/rockchip/Makefile index fe41a2e2fde0..c46a8959ce8a 100644 --- a/drivers/media/platform/rockchip/Makefile +++ b/drivers/media/platform/rockchip/Makefile @@ -6,3 +6,4 @@ obj-y += ispp/ obj-y += hdmirx/ obj-y += rga/ obj-y += rkisp1/ +obj-y += vpss/ diff --git a/drivers/media/platform/rockchip/vpss/Kconfig b/drivers/media/platform/rockchip/vpss/Kconfig new file mode 100644 index 000000000000..282b627eb689 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/Kconfig @@ -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. diff --git a/drivers/media/platform/rockchip/vpss/Makefile b/drivers/media/platform/rockchip/vpss/Makefile new file mode 100644 index 000000000000..db2cba72f536 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/Makefile @@ -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 diff --git a/drivers/media/platform/rockchip/vpss/common.c b/drivers/media/platform/rockchip/vpss/common.c new file mode 100644 index 000000000000..e76cd2f5f561 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/common.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023 Rockchip Electronics Co., Ltd */ + +#include +#include +#include + +#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; +} diff --git a/drivers/media/platform/rockchip/vpss/common.h b/drivers/media/platform/rockchip/vpss/common.h new file mode 100644 index 000000000000..c0acec04ed67 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/common.h @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/drivers/media/platform/rockchip/vpss/dev.c b/drivers/media/platform/rockchip/vpss/dev.c new file mode 100644 index 000000000000..5f60c24696a1 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/dev.c @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023 Rockchip Electronics Co., Ltd */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); diff --git a/drivers/media/platform/rockchip/vpss/dev.h b/drivers/media/platform/rockchip/vpss/dev.h new file mode 100644 index 000000000000..6c6309a8d626 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/dev.h @@ -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 + +#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 diff --git a/drivers/media/platform/rockchip/vpss/hw.c b/drivers/media/platform/rockchip/vpss/hw.c new file mode 100644 index 000000000000..77888a4f0128 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/hw.c @@ -0,0 +1,898 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023 Rockchip Electronics Co., Ltd */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); diff --git a/drivers/media/platform/rockchip/vpss/hw.h b/drivers/media/platform/rockchip/vpss/hw.h new file mode 100644 index 000000000000..662f1763b17b --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/hw.h @@ -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 diff --git a/drivers/media/platform/rockchip/vpss/procfs.c b/drivers/media/platform/rockchip/vpss/procfs.c new file mode 100644 index 000000000000..a103a88e6e12 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/procfs.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) Rockchip Electronics Co., Ltd. */ +#include +#include +#include +#include +#include + +#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 */ diff --git a/drivers/media/platform/rockchip/vpss/procfs.h b/drivers/media/platform/rockchip/vpss/procfs.h new file mode 100644 index 000000000000..739eb9cabcbc --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/procfs.h @@ -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 diff --git a/drivers/media/platform/rockchip/vpss/regs.h b/drivers/media/platform/rockchip/vpss/regs.h new file mode 100644 index 000000000000..8f850bb11b73 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/regs.h @@ -0,0 +1,1184 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKVPSS_REGS_H +#define _RKVPSS_REGS_H + +#define RKVPSS_VPSS_BASE 0x0000 +#define RKVPSS_VPSS_CTRL (RKVPSS_VPSS_BASE + 0x0000) +#define RKVPSS_VPSS_ONLINE (RKVPSS_VPSS_BASE + 0x0004) +#define RKVPSS_VPSS_ONLINE2_SIZE (RKVPSS_VPSS_BASE + 0x0008) +#define RKVPSS_VPSS_VERSION (RKVPSS_VPSS_BASE + 0x000c) +#define RKVPSS_VPSS_UPDATE (RKVPSS_VPSS_BASE + 0x0010) +#define RKVPSS_VPSS_CLK_EN (RKVPSS_VPSS_BASE + 0x0014) +#define RKVPSS_VPSS_CLK_GATE (RKVPSS_VPSS_BASE + 0x0018) +#define RKVPSS_VPSS_RESET (RKVPSS_VPSS_BASE + 0x001c) +#define RKVPSS_VPSS_SLICE_CTRL (RKVPSS_VPSS_BASE + 0x0020) +#define RKVPSS_VPSS_IRQ_CFG (RKVPSS_VPSS_BASE + 0x0050) +#define RKVPSS_VPSS_IMSC (RKVPSS_VPSS_BASE + 0x0070) +#define RKVPSS_VPSS_RIS (RKVPSS_VPSS_BASE + 0x0074) +#define RKVPSS_VPSS_MIS (RKVPSS_VPSS_BASE + 0x0078) +#define RKVPSS_VPSS_ICR (RKVPSS_VPSS_BASE + 0x007c) +#define RKVPSS_VPSS_ISR (RKVPSS_VPSS_BASE + 0x0080) +#define RKVPSS_VPSS_CTRL_SHD (RKVPSS_VPSS_BASE + 0x0090) +#define RKVPSS_VPSS_ONLINE_SHD (RKVPSS_VPSS_BASE + 0x0094) +#define RKVPSS_VPSS_ONLINE2_SIZE_SHD (RKVPSS_VPSS_BASE + 0x0098) +#define RKVPSS_VPSS_WORK (RKVPSS_VPSS_BASE + 0x00a0) +#define RKVPSS_VPSS_PIPE_ACK0 (RKVPSS_VPSS_BASE + 0x00b0) +#define RKVPSS_VPSS_PIPE_ACK1 (RKVPSS_VPSS_BASE + 0x00b4) +#define RKVPSS_VPSS_LINE_CNT0 (RKVPSS_VPSS_BASE + 0x00c0) +#define RKVPSS_VPSS_LINE_CNT1 (RKVPSS_VPSS_BASE + 0x00c4) +#define RKVPSS_VPSS_Y2R_COE00 (RKVPSS_VPSS_BASE + 0x00d0) +#define RKVPSS_VPSS_Y2R_COE01 (RKVPSS_VPSS_BASE + 0x00d4) +#define RKVPSS_VPSS_Y2R_COE02 (RKVPSS_VPSS_BASE + 0x00d8) +#define RKVPSS_VPSS_Y2R_OFF0 (RKVPSS_VPSS_BASE + 0x00dc) +#define RKVPSS_VPSS_Y2R_COE10 (RKVPSS_VPSS_BASE + 0x00e0) +#define RKVPSS_VPSS_Y2R_COE11 (RKVPSS_VPSS_BASE + 0x00e4) +#define RKVPSS_VPSS_Y2R_COE12 (RKVPSS_VPSS_BASE + 0x00e8) +#define RKVPSS_VPSS_Y2R_OFF1 (RKVPSS_VPSS_BASE + 0x00ec) +#define RKVPSS_VPSS_Y2R_COE20 (RKVPSS_VPSS_BASE + 0x00f0) +#define RKVPSS_VPSS_Y2R_COE21 (RKVPSS_VPSS_BASE + 0x00f4) +#define RKVPSS_VPSS_Y2R_COE22 (RKVPSS_VPSS_BASE + 0x00f8) +#define RKVPSS_VPSS_Y2R_OFF2 (RKVPSS_VPSS_BASE + 0x00fc) + +#define RKVPSS_CMSC_BASE 0x0500 +#define RKVPSS_CMSC_CTRL (RKVPSS_CMSC_BASE + 0x0000) +#define RKVPSS_CMSC_UPDATE (RKVPSS_CMSC_BASE + 0x0004) +#define RKVPSS_CMSC_INTSCT_CORR (RKVPSS_CMSC_BASE + 0x0008) +#define RKVPSS_CMSC_CHN0_WIN (RKVPSS_CMSC_BASE + 0x0010) +#define RKVPSS_CMSC_CHN1_WIN (RKVPSS_CMSC_BASE + 0x0014) +#define RKVPSS_CMSC_CHN2_WIN (RKVPSS_CMSC_BASE + 0x0018) +#define RKVPSS_CMSC_CHN3_WIN (RKVPSS_CMSC_BASE + 0x001c) +#define RKVPSS_CMSC_CHN0_MODE (RKVPSS_CMSC_BASE + 0x0020) +#define RKVPSS_CMSC_CHN1_MODE (RKVPSS_CMSC_BASE + 0x0024) +#define RKVPSS_CMSC_CHN2_MODE (RKVPSS_CMSC_BASE + 0x0028) +#define RKVPSS_CMSC_CHN3_MODE (RKVPSS_CMSC_BASE + 0x002c) +#define RKVPSS_CMSC_WIN0_PARA (RKVPSS_CMSC_BASE + 0x0030) +#define RKVPSS_CMSC_WIN1_PARA (RKVPSS_CMSC_BASE + 0x0034) +#define RKVPSS_CMSC_WIN2_PARA (RKVPSS_CMSC_BASE + 0x0038) +#define RKVPSS_CMSC_WIN3_PARA (RKVPSS_CMSC_BASE + 0x003c) +#define RKVPSS_CMSC_WIN4_PARA (RKVPSS_CMSC_BASE + 0x0040) +#define RKVPSS_CMSC_WIN5_PARA (RKVPSS_CMSC_BASE + 0x0044) +#define RKVPSS_CMSC_WIN6_PARA (RKVPSS_CMSC_BASE + 0x0048) +#define RKVPSS_CMSC_WIN7_PARA (RKVPSS_CMSC_BASE + 0x004c) +#define RKVPSS_CMSC_WIN0_L0_VTX (RKVPSS_CMSC_BASE + 0x0050) +#define RKVPSS_CMSC_WIN0_L0_SLP (RKVPSS_CMSC_BASE + 0x0054) +#define RKVPSS_CMSC_WIN0_L1_VTX (RKVPSS_CMSC_BASE + 0x0058) +#define RKVPSS_CMSC_WIN0_L1_SLP (RKVPSS_CMSC_BASE + 0x005c) +#define RKVPSS_CMSC_WIN0_L2_VTX (RKVPSS_CMSC_BASE + 0x0060) +#define RKVPSS_CMSC_WIN0_L2_SLP (RKVPSS_CMSC_BASE + 0x0064) +#define RKVPSS_CMSC_WIN0_L3_VTX (RKVPSS_CMSC_BASE + 0x0068) +#define RKVPSS_CMSC_WIN0_L3_SLP (RKVPSS_CMSC_BASE + 0x006c) +#define RKVPSS_CMSC_WIN1_L0_VTX (RKVPSS_CMSC_BASE + 0x0070) +#define RKVPSS_CMSC_WIN1_L0_SLP (RKVPSS_CMSC_BASE + 0x0074) +#define RKVPSS_CMSC_WIN1_L1_VTX (RKVPSS_CMSC_BASE + 0x0078) +#define RKVPSS_CMSC_WIN1_L1_SLP (RKVPSS_CMSC_BASE + 0x007c) +#define RKVPSS_CMSC_WIN1_L2_VTX (RKVPSS_CMSC_BASE + 0x0080) +#define RKVPSS_CMSC_WIN1_L2_SLP (RKVPSS_CMSC_BASE + 0x0084) +#define RKVPSS_CMSC_WIN1_L3_VTX (RKVPSS_CMSC_BASE + 0x0088) +#define RKVPSS_CMSC_WIN1_L3_SLP (RKVPSS_CMSC_BASE + 0x008c) +#define RKVPSS_CMSC_WIN2_L0_VTX (RKVPSS_CMSC_BASE + 0x0090) +#define RKVPSS_CMSC_WIN2_L0_SLP (RKVPSS_CMSC_BASE + 0x0094) +#define RKVPSS_CMSC_WIN2_L1_VTX (RKVPSS_CMSC_BASE + 0x0098) +#define RKVPSS_CMSC_WIN2_L1_SLP (RKVPSS_CMSC_BASE + 0x009c) +#define RKVPSS_CMSC_WIN2_L2_VTX (RKVPSS_CMSC_BASE + 0x00a0) +#define RKVPSS_CMSC_WIN2_L2_SLP (RKVPSS_CMSC_BASE + 0x00a4) +#define RKVPSS_CMSC_WIN2_L3_VTX (RKVPSS_CMSC_BASE + 0x00a8) +#define RKVPSS_CMSC_WIN2_L3_SLP (RKVPSS_CMSC_BASE + 0x00ac) +#define RKVPSS_CMSC_WIN3_L0_VTX (RKVPSS_CMSC_BASE + 0x00b0) +#define RKVPSS_CMSC_WIN3_L0_SLP (RKVPSS_CMSC_BASE + 0x00b4) +#define RKVPSS_CMSC_WIN3_L1_VTX (RKVPSS_CMSC_BASE + 0x00b8) +#define RKVPSS_CMSC_WIN3_L1_SLP (RKVPSS_CMSC_BASE + 0x00bc) +#define RKVPSS_CMSC_WIN3_L2_VTX (RKVPSS_CMSC_BASE + 0x00c0) +#define RKVPSS_CMSC_WIN3_L2_SLP (RKVPSS_CMSC_BASE + 0x00c4) +#define RKVPSS_CMSC_WIN3_L3_VTX (RKVPSS_CMSC_BASE + 0x00c8) +#define RKVPSS_CMSC_WIN3_L3_SLP (RKVPSS_CMSC_BASE + 0x00cc) +#define RKVPSS_CMSC_WIN4_L0_VTX (RKVPSS_CMSC_BASE + 0x00d0) +#define RKVPSS_CMSC_WIN4_L0_SLP (RKVPSS_CMSC_BASE + 0x00d4) +#define RKVPSS_CMSC_WIN4_L1_VTX (RKVPSS_CMSC_BASE + 0x00d8) +#define RKVPSS_CMSC_WIN4_L1_SLP (RKVPSS_CMSC_BASE + 0x00dc) +#define RKVPSS_CMSC_WIN4_L2_VTX (RKVPSS_CMSC_BASE + 0x00e0) +#define RKVPSS_CMSC_WIN4_L2_SLP (RKVPSS_CMSC_BASE + 0x00e4) +#define RKVPSS_CMSC_WIN4_L3_VTX (RKVPSS_CMSC_BASE + 0x00e8) +#define RKVPSS_CMSC_WIN4_L3_SLP (RKVPSS_CMSC_BASE + 0x00ec) +#define RKVPSS_CMSC_WIN5_L0_VTX (RKVPSS_CMSC_BASE + 0x00f0) +#define RKVPSS_CMSC_WIN5_L0_SLP (RKVPSS_CMSC_BASE + 0x00f4) +#define RKVPSS_CMSC_WIN5_L1_VTX (RKVPSS_CMSC_BASE + 0x00f8) +#define RKVPSS_CMSC_WIN5_L1_SLP (RKVPSS_CMSC_BASE + 0x00fc) +#define RKVPSS_CMSC_WIN5_L2_VTX (RKVPSS_CMSC_BASE + 0x0100) +#define RKVPSS_CMSC_WIN5_L2_SLP (RKVPSS_CMSC_BASE + 0x0104) +#define RKVPSS_CMSC_WIN5_L3_VTX (RKVPSS_CMSC_BASE + 0x0108) +#define RKVPSS_CMSC_WIN5_L3_SLP (RKVPSS_CMSC_BASE + 0x010c) +#define RKVPSS_CMSC_WIN6_L0_VTX (RKVPSS_CMSC_BASE + 0x0110) +#define RKVPSS_CMSC_WIN6_L0_SLP (RKVPSS_CMSC_BASE + 0x0114) +#define RKVPSS_CMSC_WIN6_L1_VTX (RKVPSS_CMSC_BASE + 0x0118) +#define RKVPSS_CMSC_WIN6_L1_SLP (RKVPSS_CMSC_BASE + 0x011c) +#define RKVPSS_CMSC_WIN6_L2_VTX (RKVPSS_CMSC_BASE + 0x0120) +#define RKVPSS_CMSC_WIN6_L2_SLP (RKVPSS_CMSC_BASE + 0x0124) +#define RKVPSS_CMSC_WIN6_L3_VTX (RKVPSS_CMSC_BASE + 0x0128) +#define RKVPSS_CMSC_WIN6_L3_SLP (RKVPSS_CMSC_BASE + 0x012c) +#define RKVPSS_CMSC_WIN7_L0_VTX (RKVPSS_CMSC_BASE + 0x0130) +#define RKVPSS_CMSC_WIN7_L0_SLP (RKVPSS_CMSC_BASE + 0x0134) +#define RKVPSS_CMSC_WIN7_L1_VTX (RKVPSS_CMSC_BASE + 0x0138) +#define RKVPSS_CMSC_WIN7_L1_SLP (RKVPSS_CMSC_BASE + 0x013c) +#define RKVPSS_CMSC_WIN7_L2_VTX (RKVPSS_CMSC_BASE + 0x0140) +#define RKVPSS_CMSC_WIN7_L2_SLP (RKVPSS_CMSC_BASE + 0x0144) +#define RKVPSS_CMSC_WIN7_L3_VTX (RKVPSS_CMSC_BASE + 0x0148) +#define RKVPSS_CMSC_WIN7_L3_SLP (RKVPSS_CMSC_BASE + 0x014c) + +#define RKVPSS_CROP0_BASE 0x0700 +#define RKVPSS_CROP0_CTRL (RKVPSS_CROP0_BASE + 0x0000) +#define RKVPSS_CROP0_UPDATE (RKVPSS_CROP0_BASE + 0x0004) +#define RKVPSS_CROP0_0_H_OFFS (RKVPSS_CROP0_BASE + 0x0010) +#define RKVPSS_CROP0_0_V_OFFS (RKVPSS_CROP0_BASE + 0x0014) +#define RKVPSS_CROP0_0_H_SIZE (RKVPSS_CROP0_BASE + 0x0018) +#define RKVPSS_CROP0_0_V_SIZE (RKVPSS_CROP0_BASE + 0x001c) +#define RKVPSS_CROP0_1_H_OFFS (RKVPSS_CROP0_BASE + 0x0020) +#define RKVPSS_CROP0_1_V_OFFS (RKVPSS_CROP0_BASE + 0x0024) +#define RKVPSS_CROP0_1_H_SIZE (RKVPSS_CROP0_BASE + 0x0028) +#define RKVPSS_CROP0_1_V_SIZE (RKVPSS_CROP0_BASE + 0x002c) +#define RKVPSS_CROP0_2_H_OFFS (RKVPSS_CROP0_BASE + 0x0030) +#define RKVPSS_CROP0_2_V_OFFS (RKVPSS_CROP0_BASE + 0x0034) +#define RKVPSS_CROP0_2_H_SIZE (RKVPSS_CROP0_BASE + 0x0038) +#define RKVPSS_CROP0_2_V_SIZE (RKVPSS_CROP0_BASE + 0x003c) +#define RKVPSS_CROP0_3_H_OFFS (RKVPSS_CROP0_BASE + 0x0040) +#define RKVPSS_CROP0_3_V_OFFS (RKVPSS_CROP0_BASE + 0x0044) +#define RKVPSS_CROP0_3_H_SIZE (RKVPSS_CROP0_BASE + 0x0048) +#define RKVPSS_CROP0_3_V_SIZE (RKVPSS_CROP0_BASE + 0x004c) +#define RKVPSS_CROP0_CTRL_SHD (RKVPSS_CROP0_BASE + 0x0060) +#define RKVPSS_CROP0_0_H_OFFS_SHD (RKVPSS_CROP0_BASE + 0x0070) +#define RKVPSS_CROP0_0_V_OFFS_SHD (RKVPSS_CROP0_BASE + 0x0074) +#define RKVPSS_CROP0_0_H_SIZE_SHD (RKVPSS_CROP0_BASE + 0x0078) +#define RKVPSS_CROP0_0_V_SIZE_SHD (RKVPSS_CROP0_BASE + 0x007c) +#define RKVPSS_CROP0_1_H_OFFS_SHD (RKVPSS_CROP0_BASE + 0x0080) +#define RKVPSS_CROP0_1_V_OFFS_SHD (RKVPSS_CROP0_BASE + 0x0084) +#define RKVPSS_CROP0_1_H_SIZE_SHD (RKVPSS_CROP0_BASE + 0x0088) +#define RKVPSS_CROP0_1_V_SIZE_SHD (RKVPSS_CROP0_BASE + 0x008c) +#define RKVPSS_CROP0_2_H_OFFS_SHD (RKVPSS_CROP0_BASE + 0x0090) +#define RKVPSS_CROP0_2_V_OFFS_SHD (RKVPSS_CROP0_BASE + 0x0094) +#define RKVPSS_CROP0_2_H_SIZE_SHD (RKVPSS_CROP0_BASE + 0x0098) +#define RKVPSS_CROP0_2_V_SIZE_SHD (RKVPSS_CROP0_BASE + 0x009c) +#define RKVPSS_CROP0_3_H_OFFS_SHD (RKVPSS_CROP0_BASE + 0x00a0) +#define RKVPSS_CROP0_3_V_OFFS_SHD (RKVPSS_CROP0_BASE + 0x00a4) +#define RKVPSS_CROP0_3_H_SIZE_SHD (RKVPSS_CROP0_BASE + 0x00a8) +#define RKVPSS_CROP0_3_V_SIZE_SHD (RKVPSS_CROP0_BASE + 0x00ac) + +#define RKVPSS_CROP1_BASE 0x0800 +#define RKVPSS_CROP1_CTRL (RKVPSS_CROP1_BASE + 0x0000) +#define RKVPSS_CROP1_UPDATE (RKVPSS_CROP1_BASE + 0x0004) +#define RKVPSS_CROP1_0_H_OFFS (RKVPSS_CROP1_BASE + 0x0010) +#define RKVPSS_CROP1_0_V_OFFS (RKVPSS_CROP1_BASE + 0x0014) +#define RKVPSS_CROP1_0_H_SIZE (RKVPSS_CROP1_BASE + 0x0018) +#define RKVPSS_CROP1_0_V_SIZE (RKVPSS_CROP1_BASE + 0x001c) +#define RKVPSS_CROP1_1_H_OFFS (RKVPSS_CROP1_BASE + 0x0020) +#define RKVPSS_CROP1_1_V_OFFS (RKVPSS_CROP1_BASE + 0x0024) +#define RKVPSS_CROP1_1_H_SIZE (RKVPSS_CROP1_BASE + 0x0028) +#define RKVPSS_CROP1_1_V_SIZE (RKVPSS_CROP1_BASE + 0x002c) +#define RKVPSS_CROP1_2_H_OFFS (RKVPSS_CROP1_BASE + 0x0030) +#define RKVPSS_CROP1_2_V_OFFS (RKVPSS_CROP1_BASE + 0x0034) +#define RKVPSS_CROP1_2_H_SIZE (RKVPSS_CROP1_BASE + 0x0038) +#define RKVPSS_CROP1_2_V_SIZE (RKVPSS_CROP1_BASE + 0x003c) +#define RKVPSS_CROP1_3_H_OFFS (RKVPSS_CROP1_BASE + 0x0040) +#define RKVPSS_CROP1_3_V_OFFS (RKVPSS_CROP1_BASE + 0x0044) +#define RKVPSS_CROP1_3_H_SIZE (RKVPSS_CROP1_BASE + 0x0048) +#define RKVPSS_CROP1_3_V_SIZE (RKVPSS_CROP1_BASE + 0x004c) +#define RKVPSS_CROP1_CTRL_SHD (RKVPSS_CROP1_BASE + 0x0060) +#define RKVPSS_CROP1_0_H_OFFS_SHD (RKVPSS_CROP1_BASE + 0x0070) +#define RKVPSS_CROP1_0_V_OFFS_SHD (RKVPSS_CROP1_BASE + 0x0074) +#define RKVPSS_CROP1_0_H_SIZE_SHD (RKVPSS_CROP1_BASE + 0x0078) +#define RKVPSS_CROP1_0_V_SIZE_SHD (RKVPSS_CROP1_BASE + 0x007c) +#define RKVPSS_CROP1_1_H_OFFS_SHD (RKVPSS_CROP1_BASE + 0x0080) +#define RKVPSS_CROP1_1_V_OFFS_SHD (RKVPSS_CROP1_BASE + 0x0084) +#define RKVPSS_CROP1_1_H_SIZE_SHD (RKVPSS_CROP1_BASE + 0x0088) +#define RKVPSS_CROP1_1_V_SIZE_SHD (RKVPSS_CROP1_BASE + 0x008c) +#define RKVPSS_CROP1_2_H_OFFS_SHD (RKVPSS_CROP1_BASE + 0x0090) +#define RKVPSS_CROP1_2_V_OFFS_SHD (RKVPSS_CROP1_BASE + 0x0094) +#define RKVPSS_CROP1_2_H_SIZE_SHD (RKVPSS_CROP1_BASE + 0x0098) +#define RKVPSS_CROP1_2_V_SIZE_SHD (RKVPSS_CROP1_BASE + 0x009c) +#define RKVPSS_CROP1_3_H_OFFS_SHD (RKVPSS_CROP1_BASE + 0x00a0) +#define RKVPSS_CROP1_3_V_OFFS_SHD (RKVPSS_CROP1_BASE + 0x00a4) +#define RKVPSS_CROP1_3_H_SIZE_SHD (RKVPSS_CROP1_BASE + 0x00a8) +#define RKVPSS_CROP1_3_V_SIZE_SHD (RKVPSS_CROP1_BASE + 0x00ac) + +#define RKVPSS_ZME_BASE 0x1000 +#define RKVPSS_ZME_Y_HOR_COE0_10 (RKVPSS_ZME_BASE + 0x0000) +#define RKVPSS_ZME_Y_HOR_COE0_32 (RKVPSS_ZME_BASE + 0x0004) +#define RKVPSS_ZME_Y_HOR_COE0_54 (RKVPSS_ZME_BASE + 0x0008) +#define RKVPSS_ZME_Y_HOR_COE0_76 (RKVPSS_ZME_BASE + 0x000c) +#define RKVPSS_ZME_Y_HOR_COE1_10 (RKVPSS_ZME_BASE + 0x0010) +#define RKVPSS_ZME_Y_HOR_COE1_32 (RKVPSS_ZME_BASE + 0x0014) +#define RKVPSS_ZME_Y_HOR_COE1_54 (RKVPSS_ZME_BASE + 0x0018) +#define RKVPSS_ZME_Y_HOR_COE1_76 (RKVPSS_ZME_BASE + 0x001c) +#define RKVPSS_ZME_Y_HOR_COE2_10 (RKVPSS_ZME_BASE + 0x0020) +#define RKVPSS_ZME_Y_HOR_COE2_32 (RKVPSS_ZME_BASE + 0x0024) +#define RKVPSS_ZME_Y_HOR_COE2_54 (RKVPSS_ZME_BASE + 0x0028) +#define RKVPSS_ZME_Y_HOR_COE2_76 (RKVPSS_ZME_BASE + 0x002c) +#define RKVPSS_ZME_Y_HOR_COE3_10 (RKVPSS_ZME_BASE + 0x0030) +#define RKVPSS_ZME_Y_HOR_COE3_32 (RKVPSS_ZME_BASE + 0x0034) +#define RKVPSS_ZME_Y_HOR_COE3_54 (RKVPSS_ZME_BASE + 0x0038) +#define RKVPSS_ZME_Y_HOR_COE3_76 (RKVPSS_ZME_BASE + 0x003c) +#define RKVPSS_ZME_Y_HOR_COE4_10 (RKVPSS_ZME_BASE + 0x0040) +#define RKVPSS_ZME_Y_HOR_COE4_32 (RKVPSS_ZME_BASE + 0x0044) +#define RKVPSS_ZME_Y_HOR_COE4_54 (RKVPSS_ZME_BASE + 0x0048) +#define RKVPSS_ZME_Y_HOR_COE4_76 (RKVPSS_ZME_BASE + 0x004c) +#define RKVPSS_ZME_Y_HOR_COE5_10 (RKVPSS_ZME_BASE + 0x0050) +#define RKVPSS_ZME_Y_HOR_COE5_32 (RKVPSS_ZME_BASE + 0x0054) +#define RKVPSS_ZME_Y_HOR_COE5_54 (RKVPSS_ZME_BASE + 0x0058) +#define RKVPSS_ZME_Y_HOR_COE5_76 (RKVPSS_ZME_BASE + 0x005c) +#define RKVPSS_ZME_Y_HOR_COE6_10 (RKVPSS_ZME_BASE + 0x0060) +#define RKVPSS_ZME_Y_HOR_COE6_32 (RKVPSS_ZME_BASE + 0x0064) +#define RKVPSS_ZME_Y_HOR_COE6_54 (RKVPSS_ZME_BASE + 0x0068) +#define RKVPSS_ZME_Y_HOR_COE6_76 (RKVPSS_ZME_BASE + 0x006c) +#define RKVPSS_ZME_Y_HOR_COE7_10 (RKVPSS_ZME_BASE + 0x0070) +#define RKVPSS_ZME_Y_HOR_COE7_32 (RKVPSS_ZME_BASE + 0x0074) +#define RKVPSS_ZME_Y_HOR_COE7_54 (RKVPSS_ZME_BASE + 0x0078) +#define RKVPSS_ZME_Y_HOR_COE7_76 (RKVPSS_ZME_BASE + 0x007c) +#define RKVPSS_ZME_Y_HOR_COE8_10 (RKVPSS_ZME_BASE + 0x0080) +#define RKVPSS_ZME_Y_HOR_COE8_32 (RKVPSS_ZME_BASE + 0x0084) +#define RKVPSS_ZME_Y_HOR_COE8_54 (RKVPSS_ZME_BASE + 0x0088) +#define RKVPSS_ZME_Y_HOR_COE8_76 (RKVPSS_ZME_BASE + 0x008c) +#define RKVPSS_ZME_Y_HOR_COE9_10 (RKVPSS_ZME_BASE + 0x0090) +#define RKVPSS_ZME_Y_HOR_COE9_32 (RKVPSS_ZME_BASE + 0x0094) +#define RKVPSS_ZME_Y_HOR_COE9_54 (RKVPSS_ZME_BASE + 0x0098) +#define RKVPSS_ZME_Y_HOR_COE9_76 (RKVPSS_ZME_BASE + 0x009c) +#define RKVPSS_ZME_Y_HOR_COE10_10 (RKVPSS_ZME_BASE + 0x00a0) +#define RKVPSS_ZME_Y_HOR_COE10_32 (RKVPSS_ZME_BASE + 0x00a4) +#define RKVPSS_ZME_Y_HOR_COE10_54 (RKVPSS_ZME_BASE + 0x00a8) +#define RKVPSS_ZME_Y_HOR_COE10_76 (RKVPSS_ZME_BASE + 0x00ac) +#define RKVPSS_ZME_Y_HOR_COE11_10 (RKVPSS_ZME_BASE + 0x00b0) +#define RKVPSS_ZME_Y_HOR_COE11_32 (RKVPSS_ZME_BASE + 0x00b4) +#define RKVPSS_ZME_Y_HOR_COE11_54 (RKVPSS_ZME_BASE + 0x00b8) +#define RKVPSS_ZME_Y_HOR_COE11_76 (RKVPSS_ZME_BASE + 0x00bc) +#define RKVPSS_ZME_Y_HOR_COE12_10 (RKVPSS_ZME_BASE + 0x00c0) +#define RKVPSS_ZME_Y_HOR_COE12_32 (RKVPSS_ZME_BASE + 0x00c4) +#define RKVPSS_ZME_Y_HOR_COE12_54 (RKVPSS_ZME_BASE + 0x00c8) +#define RKVPSS_ZME_Y_HOR_COE12_76 (RKVPSS_ZME_BASE + 0x00cc) +#define RKVPSS_ZME_Y_HOR_COE13_10 (RKVPSS_ZME_BASE + 0x00d0) +#define RKVPSS_ZME_Y_HOR_COE13_32 (RKVPSS_ZME_BASE + 0x00d4) +#define RKVPSS_ZME_Y_HOR_COE13_54 (RKVPSS_ZME_BASE + 0x00d8) +#define RKVPSS_ZME_Y_HOR_COE13_76 (RKVPSS_ZME_BASE + 0x00dc) +#define RKVPSS_ZME_Y_HOR_COE14_10 (RKVPSS_ZME_BASE + 0x00e0) +#define RKVPSS_ZME_Y_HOR_COE14_32 (RKVPSS_ZME_BASE + 0x00e4) +#define RKVPSS_ZME_Y_HOR_COE14_54 (RKVPSS_ZME_BASE + 0x00e8) +#define RKVPSS_ZME_Y_HOR_COE14_76 (RKVPSS_ZME_BASE + 0x00ec) +#define RKVPSS_ZME_Y_HOR_COE15_10 (RKVPSS_ZME_BASE + 0x00f0) +#define RKVPSS_ZME_Y_HOR_COE15_32 (RKVPSS_ZME_BASE + 0x00f4) +#define RKVPSS_ZME_Y_HOR_COE15_54 (RKVPSS_ZME_BASE + 0x00f8) +#define RKVPSS_ZME_Y_HOR_COE15_76 (RKVPSS_ZME_BASE + 0x00fc) +#define RKVPSS_ZME_Y_HOR_COE16_10 (RKVPSS_ZME_BASE + 0x0100) +#define RKVPSS_ZME_Y_HOR_COE16_32 (RKVPSS_ZME_BASE + 0x0104) +#define RKVPSS_ZME_Y_HOR_COE16_54 (RKVPSS_ZME_BASE + 0x0108) +#define RKVPSS_ZME_Y_HOR_COE16_76 (RKVPSS_ZME_BASE + 0x010c) +#define RKVPSS_ZME_Y_VER_COE0_10 (RKVPSS_ZME_BASE + 0x0200) +#define RKVPSS_ZME_Y_VER_COE0_32 (RKVPSS_ZME_BASE + 0x0204) +#define RKVPSS_ZME_Y_VER_COE0_54 (RKVPSS_ZME_BASE + 0x0208) +#define RKVPSS_ZME_Y_VER_COE0_76 (RKVPSS_ZME_BASE + 0x020c) +#define RKVPSS_ZME_Y_VER_COE1_10 (RKVPSS_ZME_BASE + 0x0210) +#define RKVPSS_ZME_Y_VER_COE1_32 (RKVPSS_ZME_BASE + 0x0214) +#define RKVPSS_ZME_Y_VER_COE1_54 (RKVPSS_ZME_BASE + 0x0218) +#define RKVPSS_ZME_Y_VER_COE1_76 (RKVPSS_ZME_BASE + 0x021c) +#define RKVPSS_ZME_Y_VER_COE2_10 (RKVPSS_ZME_BASE + 0x0220) +#define RKVPSS_ZME_Y_VER_COE2_32 (RKVPSS_ZME_BASE + 0x0224) +#define RKVPSS_ZME_Y_VER_COE2_54 (RKVPSS_ZME_BASE + 0x0228) +#define RKVPSS_ZME_Y_VER_COE2_76 (RKVPSS_ZME_BASE + 0x022c) +#define RKVPSS_ZME_Y_VER_COE3_10 (RKVPSS_ZME_BASE + 0x0230) +#define RKVPSS_ZME_Y_VER_COE3_32 (RKVPSS_ZME_BASE + 0x0234) +#define RKVPSS_ZME_Y_VER_COE3_54 (RKVPSS_ZME_BASE + 0x0238) +#define RKVPSS_ZME_Y_VER_COE3_76 (RKVPSS_ZME_BASE + 0x023c) +#define RKVPSS_ZME_Y_VER_COE4_10 (RKVPSS_ZME_BASE + 0x0240) +#define RKVPSS_ZME_Y_VER_COE4_32 (RKVPSS_ZME_BASE + 0x0244) +#define RKVPSS_ZME_Y_VER_COE4_54 (RKVPSS_ZME_BASE + 0x0248) +#define RKVPSS_ZME_Y_VER_COE4_76 (RKVPSS_ZME_BASE + 0x024c) +#define RKVPSS_ZME_Y_VER_COE5_10 (RKVPSS_ZME_BASE + 0x0250) +#define RKVPSS_ZME_Y_VER_COE5_32 (RKVPSS_ZME_BASE + 0x0254) +#define RKVPSS_ZME_Y_VER_COE5_54 (RKVPSS_ZME_BASE + 0x0258) +#define RKVPSS_ZME_Y_VER_COE5_76 (RKVPSS_ZME_BASE + 0x025c) +#define RKVPSS_ZME_Y_VER_COE6_10 (RKVPSS_ZME_BASE + 0x0260) +#define RKVPSS_ZME_Y_VER_COE6_32 (RKVPSS_ZME_BASE + 0x0264) +#define RKVPSS_ZME_Y_VER_COE6_54 (RKVPSS_ZME_BASE + 0x0268) +#define RKVPSS_ZME_Y_VER_COE6_76 (RKVPSS_ZME_BASE + 0x026c) +#define RKVPSS_ZME_Y_VER_COE7_10 (RKVPSS_ZME_BASE + 0x0270) +#define RKVPSS_ZME_Y_VER_COE7_32 (RKVPSS_ZME_BASE + 0x0274) +#define RKVPSS_ZME_Y_VER_COE7_54 (RKVPSS_ZME_BASE + 0x0278) +#define RKVPSS_ZME_Y_VER_COE7_76 (RKVPSS_ZME_BASE + 0x027c) +#define RKVPSS_ZME_Y_VER_COE8_10 (RKVPSS_ZME_BASE + 0x0280) +#define RKVPSS_ZME_Y_VER_COE8_32 (RKVPSS_ZME_BASE + 0x0284) +#define RKVPSS_ZME_Y_VER_COE8_54 (RKVPSS_ZME_BASE + 0x0288) +#define RKVPSS_ZME_Y_VER_COE8_76 (RKVPSS_ZME_BASE + 0x028c) +#define RKVPSS_ZME_Y_VER_COE9_10 (RKVPSS_ZME_BASE + 0x0290) +#define RKVPSS_ZME_Y_VER_COE9_32 (RKVPSS_ZME_BASE + 0x0294) +#define RKVPSS_ZME_Y_VER_COE9_54 (RKVPSS_ZME_BASE + 0x0298) +#define RKVPSS_ZME_Y_VER_COE9_76 (RKVPSS_ZME_BASE + 0x029c) +#define RKVPSS_ZME_Y_VER_COE10_10 (RKVPSS_ZME_BASE + 0x02a0) +#define RKVPSS_ZME_Y_VER_COE10_32 (RKVPSS_ZME_BASE + 0x02a4) +#define RKVPSS_ZME_Y_VER_COE10_54 (RKVPSS_ZME_BASE + 0x02a8) +#define RKVPSS_ZME_Y_VER_COE10_76 (RKVPSS_ZME_BASE + 0x02ac) +#define RKVPSS_ZME_Y_VER_COE11_10 (RKVPSS_ZME_BASE + 0x02b0) +#define RKVPSS_ZME_Y_VER_COE11_32 (RKVPSS_ZME_BASE + 0x02b4) +#define RKVPSS_ZME_Y_VER_COE11_54 (RKVPSS_ZME_BASE + 0x02b8) +#define RKVPSS_ZME_Y_VER_COE11_76 (RKVPSS_ZME_BASE + 0x02bc) +#define RKVPSS_ZME_Y_VER_COE12_10 (RKVPSS_ZME_BASE + 0x02c0) +#define RKVPSS_ZME_Y_VER_COE12_32 (RKVPSS_ZME_BASE + 0x02c4) +#define RKVPSS_ZME_Y_VER_COE12_54 (RKVPSS_ZME_BASE + 0x02c8) +#define RKVPSS_ZME_Y_VER_COE12_76 (RKVPSS_ZME_BASE + 0x02cc) +#define RKVPSS_ZME_Y_VER_COE13_10 (RKVPSS_ZME_BASE + 0x02d0) +#define RKVPSS_ZME_Y_VER_COE13_32 (RKVPSS_ZME_BASE + 0x02d4) +#define RKVPSS_ZME_Y_VER_COE13_54 (RKVPSS_ZME_BASE + 0x02d8) +#define RKVPSS_ZME_Y_VER_COE13_76 (RKVPSS_ZME_BASE + 0x02dc) +#define RKVPSS_ZME_Y_VER_COE14_10 (RKVPSS_ZME_BASE + 0x02e0) +#define RKVPSS_ZME_Y_VER_COE14_32 (RKVPSS_ZME_BASE + 0x02e4) +#define RKVPSS_ZME_Y_VER_COE14_54 (RKVPSS_ZME_BASE + 0x02e8) +#define RKVPSS_ZME_Y_VER_COE14_76 (RKVPSS_ZME_BASE + 0x02ec) +#define RKVPSS_ZME_Y_VER_COE15_10 (RKVPSS_ZME_BASE + 0x02f0) +#define RKVPSS_ZME_Y_VER_COE15_32 (RKVPSS_ZME_BASE + 0x02f4) +#define RKVPSS_ZME_Y_VER_COE15_54 (RKVPSS_ZME_BASE + 0x02f8) +#define RKVPSS_ZME_Y_VER_COE15_76 (RKVPSS_ZME_BASE + 0x02fc) +#define RKVPSS_ZME_Y_VER_COE16_10 (RKVPSS_ZME_BASE + 0x0300) +#define RKVPSS_ZME_Y_VER_COE16_32 (RKVPSS_ZME_BASE + 0x0304) +#define RKVPSS_ZME_Y_VER_COE16_54 (RKVPSS_ZME_BASE + 0x0308) +#define RKVPSS_ZME_Y_VER_COE16_76 (RKVPSS_ZME_BASE + 0x030c) +#define RKVPSS_ZME_UV_HOR_COE0_10 (RKVPSS_ZME_BASE + 0x0400) +#define RKVPSS_ZME_UV_HOR_COE0_32 (RKVPSS_ZME_BASE + 0x0404) +#define RKVPSS_ZME_UV_HOR_COE0_54 (RKVPSS_ZME_BASE + 0x0408) +#define RKVPSS_ZME_UV_HOR_COE0_76 (RKVPSS_ZME_BASE + 0x040c) +#define RKVPSS_ZME_UV_HOR_COE1_10 (RKVPSS_ZME_BASE + 0x0410) +#define RKVPSS_ZME_UV_HOR_COE1_32 (RKVPSS_ZME_BASE + 0x0414) +#define RKVPSS_ZME_UV_HOR_COE1_54 (RKVPSS_ZME_BASE + 0x0418) +#define RKVPSS_ZME_UV_HOR_COE1_76 (RKVPSS_ZME_BASE + 0x041c) +#define RKVPSS_ZME_UV_HOR_COE2_10 (RKVPSS_ZME_BASE + 0x0420) +#define RKVPSS_ZME_UV_HOR_COE2_32 (RKVPSS_ZME_BASE + 0x0424) +#define RKVPSS_ZME_UV_HOR_COE2_54 (RKVPSS_ZME_BASE + 0x0428) +#define RKVPSS_ZME_UV_HOR_COE2_76 (RKVPSS_ZME_BASE + 0x042c) +#define RKVPSS_ZME_UV_HOR_COE3_10 (RKVPSS_ZME_BASE + 0x0430) +#define RKVPSS_ZME_UV_HOR_COE3_32 (RKVPSS_ZME_BASE + 0x0434) +#define RKVPSS_ZME_UV_HOR_COE3_54 (RKVPSS_ZME_BASE + 0x0438) +#define RKVPSS_ZME_UV_HOR_COE3_76 (RKVPSS_ZME_BASE + 0x043c) +#define RKVPSS_ZME_UV_HOR_COE4_10 (RKVPSS_ZME_BASE + 0x0440) +#define RKVPSS_ZME_UV_HOR_COE4_32 (RKVPSS_ZME_BASE + 0x0444) +#define RKVPSS_ZME_UV_HOR_COE4_54 (RKVPSS_ZME_BASE + 0x0448) +#define RKVPSS_ZME_UV_HOR_COE4_76 (RKVPSS_ZME_BASE + 0x044c) +#define RKVPSS_ZME_UV_HOR_COE5_10 (RKVPSS_ZME_BASE + 0x0450) +#define RKVPSS_ZME_UV_HOR_COE5_32 (RKVPSS_ZME_BASE + 0x0454) +#define RKVPSS_ZME_UV_HOR_COE5_54 (RKVPSS_ZME_BASE + 0x0458) +#define RKVPSS_ZME_UV_HOR_COE5_76 (RKVPSS_ZME_BASE + 0x045c) +#define RKVPSS_ZME_UV_HOR_COE6_10 (RKVPSS_ZME_BASE + 0x0460) +#define RKVPSS_ZME_UV_HOR_COE6_32 (RKVPSS_ZME_BASE + 0x0464) +#define RKVPSS_ZME_UV_HOR_COE6_54 (RKVPSS_ZME_BASE + 0x0468) +#define RKVPSS_ZME_UV_HOR_COE6_76 (RKVPSS_ZME_BASE + 0x046c) +#define RKVPSS_ZME_UV_HOR_COE7_10 (RKVPSS_ZME_BASE + 0x0470) +#define RKVPSS_ZME_UV_HOR_COE7_32 (RKVPSS_ZME_BASE + 0x0474) +#define RKVPSS_ZME_UV_HOR_COE7_54 (RKVPSS_ZME_BASE + 0x0478) +#define RKVPSS_ZME_UV_HOR_COE7_76 (RKVPSS_ZME_BASE + 0x047c) +#define RKVPSS_ZME_UV_HOR_COE8_10 (RKVPSS_ZME_BASE + 0x0480) +#define RKVPSS_ZME_UV_HOR_COE8_32 (RKVPSS_ZME_BASE + 0x0484) +#define RKVPSS_ZME_UV_HOR_COE8_54 (RKVPSS_ZME_BASE + 0x0488) +#define RKVPSS_ZME_UV_HOR_COE8_76 (RKVPSS_ZME_BASE + 0x048c) +#define RKVPSS_ZME_UV_HOR_COE9_10 (RKVPSS_ZME_BASE + 0x0490) +#define RKVPSS_ZME_UV_HOR_COE9_32 (RKVPSS_ZME_BASE + 0x0494) +#define RKVPSS_ZME_UV_HOR_COE9_54 (RKVPSS_ZME_BASE + 0x0498) +#define RKVPSS_ZME_UV_HOR_COE9_76 (RKVPSS_ZME_BASE + 0x049c) +#define RKVPSS_ZME_UV_HOR_COE10_10 (RKVPSS_ZME_BASE + 0x04a0) +#define RKVPSS_ZME_UV_HOR_COE10_32 (RKVPSS_ZME_BASE + 0x04a4) +#define RKVPSS_ZME_UV_HOR_COE10_54 (RKVPSS_ZME_BASE + 0x04a8) +#define RKVPSS_ZME_UV_HOR_COE10_76 (RKVPSS_ZME_BASE + 0x04ac) +#define RKVPSS_ZME_UV_HOR_COE11_10 (RKVPSS_ZME_BASE + 0x04b0) +#define RKVPSS_ZME_UV_HOR_COE11_32 (RKVPSS_ZME_BASE + 0x04b4) +#define RKVPSS_ZME_UV_HOR_COE11_54 (RKVPSS_ZME_BASE + 0x04b8) +#define RKVPSS_ZME_UV_HOR_COE11_76 (RKVPSS_ZME_BASE + 0x04bc) +#define RKVPSS_ZME_UV_HOR_COE12_10 (RKVPSS_ZME_BASE + 0x04c0) +#define RKVPSS_ZME_UV_HOR_COE12_32 (RKVPSS_ZME_BASE + 0x04c4) +#define RKVPSS_ZME_UV_HOR_COE12_54 (RKVPSS_ZME_BASE + 0x04c8) +#define RKVPSS_ZME_UV_HOR_COE12_76 (RKVPSS_ZME_BASE + 0x04cc) +#define RKVPSS_ZME_UV_HOR_COE13_10 (RKVPSS_ZME_BASE + 0x04d0) +#define RKVPSS_ZME_UV_HOR_COE13_32 (RKVPSS_ZME_BASE + 0x04d4) +#define RKVPSS_ZME_UV_HOR_COE13_54 (RKVPSS_ZME_BASE + 0x04d8) +#define RKVPSS_ZME_UV_HOR_COE13_76 (RKVPSS_ZME_BASE + 0x04dc) +#define RKVPSS_ZME_UV_HOR_COE14_10 (RKVPSS_ZME_BASE + 0x04e0) +#define RKVPSS_ZME_UV_HOR_COE14_32 (RKVPSS_ZME_BASE + 0x04e4) +#define RKVPSS_ZME_UV_HOR_COE14_54 (RKVPSS_ZME_BASE + 0x04e8) +#define RKVPSS_ZME_UV_HOR_COE14_76 (RKVPSS_ZME_BASE + 0x04ec) +#define RKVPSS_ZME_UV_HOR_COE15_10 (RKVPSS_ZME_BASE + 0x04f0) +#define RKVPSS_ZME_UV_HOR_COE15_32 (RKVPSS_ZME_BASE + 0x04f4) +#define RKVPSS_ZME_UV_HOR_COE15_54 (RKVPSS_ZME_BASE + 0x04f8) +#define RKVPSS_ZME_UV_HOR_COE15_76 (RKVPSS_ZME_BASE + 0x04fc) +#define RKVPSS_ZME_UV_HOR_COE16_10 (RKVPSS_ZME_BASE + 0x0500) +#define RKVPSS_ZME_UV_HOR_COE16_32 (RKVPSS_ZME_BASE + 0x0504) +#define RKVPSS_ZME_UV_HOR_COE16_54 (RKVPSS_ZME_BASE + 0x0508) +#define RKVPSS_ZME_UV_HOR_COE16_76 (RKVPSS_ZME_BASE + 0x050c) +#define RKVPSS_ZME_UV_VER_COE0_10 (RKVPSS_ZME_BASE + 0x0600) +#define RKVPSS_ZME_UV_VER_COE0_32 (RKVPSS_ZME_BASE + 0x0604) +#define RKVPSS_ZME_UV_VER_COE0_54 (RKVPSS_ZME_BASE + 0x0608) +#define RKVPSS_ZME_UV_VER_COE0_76 (RKVPSS_ZME_BASE + 0x060c) +#define RKVPSS_ZME_UV_VER_COE1_10 (RKVPSS_ZME_BASE + 0x0610) +#define RKVPSS_ZME_UV_VER_COE1_32 (RKVPSS_ZME_BASE + 0x0614) +#define RKVPSS_ZME_UV_VER_COE1_54 (RKVPSS_ZME_BASE + 0x0618) +#define RKVPSS_ZME_UV_VER_COE1_76 (RKVPSS_ZME_BASE + 0x061c) +#define RKVPSS_ZME_UV_VER_COE2_10 (RKVPSS_ZME_BASE + 0x0620) +#define RKVPSS_ZME_UV_VER_COE2_32 (RKVPSS_ZME_BASE + 0x0624) +#define RKVPSS_ZME_UV_VER_COE2_54 (RKVPSS_ZME_BASE + 0x0628) +#define RKVPSS_ZME_UV_VER_COE2_76 (RKVPSS_ZME_BASE + 0x062c) +#define RKVPSS_ZME_UV_VER_COE3_10 (RKVPSS_ZME_BASE + 0x0630) +#define RKVPSS_ZME_UV_VER_COE3_32 (RKVPSS_ZME_BASE + 0x0634) +#define RKVPSS_ZME_UV_VER_COE3_54 (RKVPSS_ZME_BASE + 0x0638) +#define RKVPSS_ZME_UV_VER_COE3_76 (RKVPSS_ZME_BASE + 0x063c) +#define RKVPSS_ZME_UV_VER_COE4_10 (RKVPSS_ZME_BASE + 0x0640) +#define RKVPSS_ZME_UV_VER_COE4_32 (RKVPSS_ZME_BASE + 0x0644) +#define RKVPSS_ZME_UV_VER_COE4_54 (RKVPSS_ZME_BASE + 0x0648) +#define RKVPSS_ZME_UV_VER_COE4_76 (RKVPSS_ZME_BASE + 0x064c) +#define RKVPSS_ZME_UV_VER_COE5_10 (RKVPSS_ZME_BASE + 0x0650) +#define RKVPSS_ZME_UV_VER_COE5_32 (RKVPSS_ZME_BASE + 0x0654) +#define RKVPSS_ZME_UV_VER_COE5_54 (RKVPSS_ZME_BASE + 0x0658) +#define RKVPSS_ZME_UV_VER_COE5_76 (RKVPSS_ZME_BASE + 0x065c) +#define RKVPSS_ZME_UV_VER_COE6_10 (RKVPSS_ZME_BASE + 0x0660) +#define RKVPSS_ZME_UV_VER_COE6_32 (RKVPSS_ZME_BASE + 0x0664) +#define RKVPSS_ZME_UV_VER_COE6_54 (RKVPSS_ZME_BASE + 0x0668) +#define RKVPSS_ZME_UV_VER_COE6_76 (RKVPSS_ZME_BASE + 0x066c) +#define RKVPSS_ZME_UV_VER_COE7_10 (RKVPSS_ZME_BASE + 0x0670) +#define RKVPSS_ZME_UV_VER_COE7_32 (RKVPSS_ZME_BASE + 0x0674) +#define RKVPSS_ZME_UV_VER_COE7_54 (RKVPSS_ZME_BASE + 0x0678) +#define RKVPSS_ZME_UV_VER_COE7_76 (RKVPSS_ZME_BASE + 0x067c) +#define RKVPSS_ZME_UV_VER_COE8_10 (RKVPSS_ZME_BASE + 0x0680) +#define RKVPSS_ZME_UV_VER_COE8_32 (RKVPSS_ZME_BASE + 0x0684) +#define RKVPSS_ZME_UV_VER_COE8_54 (RKVPSS_ZME_BASE + 0x0688) +#define RKVPSS_ZME_UV_VER_COE8_76 (RKVPSS_ZME_BASE + 0x068c) +#define RKVPSS_ZME_UV_VER_COE9_10 (RKVPSS_ZME_BASE + 0x0690) +#define RKVPSS_ZME_UV_VER_COE9_32 (RKVPSS_ZME_BASE + 0x0694) +#define RKVPSS_ZME_UV_VER_COE9_54 (RKVPSS_ZME_BASE + 0x0698) +#define RKVPSS_ZME_UV_VER_COE9_76 (RKVPSS_ZME_BASE + 0x069c) +#define RKVPSS_ZME_UV_VER_COE10_10 (RKVPSS_ZME_BASE + 0x06a0) +#define RKVPSS_ZME_UV_VER_COE10_32 (RKVPSS_ZME_BASE + 0x06a4) +#define RKVPSS_ZME_UV_VER_COE10_54 (RKVPSS_ZME_BASE + 0x06a8) +#define RKVPSS_ZME_UV_VER_COE10_76 (RKVPSS_ZME_BASE + 0x06ac) +#define RKVPSS_ZME_UV_VER_COE11_10 (RKVPSS_ZME_BASE + 0x06b0) +#define RKVPSS_ZME_UV_VER_COE11_32 (RKVPSS_ZME_BASE + 0x06b4) +#define RKVPSS_ZME_UV_VER_COE11_54 (RKVPSS_ZME_BASE + 0x06b8) +#define RKVPSS_ZME_UV_VER_COE11_76 (RKVPSS_ZME_BASE + 0x06bc) +#define RKVPSS_ZME_UV_VER_COE12_10 (RKVPSS_ZME_BASE + 0x06c0) +#define RKVPSS_ZME_UV_VER_COE12_32 (RKVPSS_ZME_BASE + 0x06c4) +#define RKVPSS_ZME_UV_VER_COE12_54 (RKVPSS_ZME_BASE + 0x06c8) +#define RKVPSS_ZME_UV_VER_COE12_76 (RKVPSS_ZME_BASE + 0x06cc) +#define RKVPSS_ZME_UV_VER_COE13_10 (RKVPSS_ZME_BASE + 0x06d0) +#define RKVPSS_ZME_UV_VER_COE13_32 (RKVPSS_ZME_BASE + 0x06d4) +#define RKVPSS_ZME_UV_VER_COE13_54 (RKVPSS_ZME_BASE + 0x06d8) +#define RKVPSS_ZME_UV_VER_COE13_76 (RKVPSS_ZME_BASE + 0x06dc) +#define RKVPSS_ZME_UV_VER_COE14_10 (RKVPSS_ZME_BASE + 0x06e0) +#define RKVPSS_ZME_UV_VER_COE14_32 (RKVPSS_ZME_BASE + 0x06e4) +#define RKVPSS_ZME_UV_VER_COE14_54 (RKVPSS_ZME_BASE + 0x06e8) +#define RKVPSS_ZME_UV_VER_COE14_76 (RKVPSS_ZME_BASE + 0x06ec) +#define RKVPSS_ZME_UV_VER_COE15_10 (RKVPSS_ZME_BASE + 0x06f0) +#define RKVPSS_ZME_UV_VER_COE15_32 (RKVPSS_ZME_BASE + 0x06f4) +#define RKVPSS_ZME_UV_VER_COE15_54 (RKVPSS_ZME_BASE + 0x06f8) +#define RKVPSS_ZME_UV_VER_COE15_76 (RKVPSS_ZME_BASE + 0x06fc) +#define RKVPSS_ZME_UV_VER_COE16_10 (RKVPSS_ZME_BASE + 0x0700) +#define RKVPSS_ZME_UV_VER_COE16_32 (RKVPSS_ZME_BASE + 0x0704) +#define RKVPSS_ZME_UV_VER_COE16_54 (RKVPSS_ZME_BASE + 0x0708) +#define RKVPSS_ZME_UV_VER_COE16_76 (RKVPSS_ZME_BASE + 0x070c) +#define RKVPSS_ZME_CTRL (RKVPSS_ZME_BASE + 0x0800) +#define RKVPSS_ZME_UPDATE (RKVPSS_ZME_BASE + 0x0804) +#define RKVPSS_ZME_H_SIZE (RKVPSS_ZME_BASE + 0x0808) +#define RKVPSS_ZME_H_OFFS (RKVPSS_ZME_BASE + 0x080c) +#define RKVPSS_ZME_Y_SCL_CTRL (RKVPSS_ZME_BASE + 0x0810) +#define RKVPSS_ZME_Y_SRC_SIZE (RKVPSS_ZME_BASE + 0x0814) +#define RKVPSS_ZME_Y_DST_SIZE (RKVPSS_ZME_BASE + 0x0818) +#define RKVPSS_ZME_Y_DERING_PARA (RKVPSS_ZME_BASE + 0x081c) +#define RKVPSS_ZME_Y_XSCL_FACTOR (RKVPSS_ZME_BASE + 0x0820) +#define RKVPSS_ZME_Y_YSCL_FACTOR (RKVPSS_ZME_BASE + 0x0824) +#define RKVPSS_ZME_UV_SCL_CTRL (RKVPSS_ZME_BASE + 0x0830) +#define RKVPSS_ZME_UV_SRC_SIZE (RKVPSS_ZME_BASE + 0x0834) +#define RKVPSS_ZME_UV_DST_SIZE (RKVPSS_ZME_BASE + 0x0838) +#define RKVPSS_ZME_UV_DERING_PARA (RKVPSS_ZME_BASE + 0x083c) +#define RKVPSS_ZME_UV_XSCL_FACTOR (RKVPSS_ZME_BASE + 0x0840) +#define RKVPSS_ZME_UV_YSCL_FACTOR (RKVPSS_ZME_BASE + 0x0844) +#define RKVPSS_ZME_SCL_CTRL_SHD (RKVPSS_ZME_BASE + 0x084c) +#define RKVPSS_ZME_Y_SRC_SIZE_SHD (RKVPSS_ZME_BASE + 0x0850) +#define RKVPSS_ZME_Y_DST_SIZE_SHD (RKVPSS_ZME_BASE + 0x0854) +#define RKVPSS_ZME_Y_XSCL_FACTOR_SHD (RKVPSS_ZME_BASE + 0x0858) +#define RKVPSS_ZME_Y_YSCL_FACTOR_SHD (RKVPSS_ZME_BASE + 0x085c) +#define RKVPSS_ZME_UV_SRC_SIZE_SHD (RKVPSS_ZME_BASE + 0x0860) +#define RKVPSS_ZME_UV_DST_SIZE_SHD (RKVPSS_ZME_BASE + 0x0864) +#define RKVPSS_ZME_UV_XSCL_FACTOR_SHD (RKVPSS_ZME_BASE + 0x0868) +#define RKVPSS_ZME_UV_YSCL_FACTOR_SHD (RKVPSS_ZME_BASE + 0x086c) +#define RKVPSS_ZME_H_SIZE_SHD (RKVPSS_ZME_BASE + 0x0870) +#define RKVPSS_ZME_H_OFFS_SHD (RKVPSS_ZME_BASE + 0x0874) + +#define RKVPSS_RATIO0_BASE 0x1900 +#define RKVPSS_RATIO0_CTRL (RKVPSS_RATIO0_BASE + 0x0000) +#define RKVPSS_RATIO0_UPDATE (RKVPSS_RATIO0_BASE + 0x0004) +#define RKVPSS_RATIO0_ACT_SIZE (RKVPSS_RATIO0_BASE + 0x0010) +#define RKVPSS_RATIO0_VIR_SIZE (RKVPSS_RATIO0_BASE + 0x0014) +#define RKVPSS_RATIO0_OFFS (RKVPSS_RATIO0_BASE + 0x0018) +#define RKVPSS_RATIO0_COLOR (RKVPSS_RATIO0_BASE + 0x001c) +#define RKVPSS_RATIO0_ACT_SIZE_SHD (RKVPSS_RATIO0_BASE + 0x0030) +#define RKVPSS_RATIO0_VIR_SIZE_SHD (RKVPSS_RATIO0_BASE + 0x0034) +#define RKVPSS_RATIO0_OFFS_SHD (RKVPSS_RATIO0_BASE + 0x0038) +#define RKVPSS_RATIO0_COLOR_SHD (RKVPSS_RATIO0_BASE + 0x003c) + +#define RKVPSS_RATIO1_BASE 0x2500 +#define RKVPSS_RATIO1_CTRL (RKVPSS_RATIO1_BASE + 0x0000) +#define RKVPSS_RATIO1_UPDATE (RKVPSS_RATIO1_BASE + 0x0004) +#define RKVPSS_RATIO1_ACT_SIZE (RKVPSS_RATIO1_BASE + 0x0010) +#define RKVPSS_RATIO1_VIR_SIZE (RKVPSS_RATIO1_BASE + 0x0014) +#define RKVPSS_RATIO1_OFFS (RKVPSS_RATIO1_BASE + 0x0018) +#define RKVPSS_RATIO1_COLOR (RKVPSS_RATIO1_BASE + 0x001c) +#define RKVPSS_RATIO1_ACT_SIZE_SHD (RKVPSS_RATIO1_BASE + 0x0030) +#define RKVPSS_RATIO1_VIR_SIZE_SHD (RKVPSS_RATIO1_BASE + 0x0034) +#define RKVPSS_RATIO1_OFFS_SHD (RKVPSS_RATIO1_BASE + 0x0038) +#define RKVPSS_RATIO1_COLOR_SHD (RKVPSS_RATIO1_BASE + 0x003c) + +#define RKVPSS_RATIO2_BASE 0x2900 +#define RKVPSS_RATIO2_CTRL (RKVPSS_RATIO2_BASE + 0x0000) +#define RKVPSS_RATIO2_UPDATE (RKVPSS_RATIO2_BASE + 0x0004) +#define RKVPSS_RATIO2_ACT_SIZE (RKVPSS_RATIO2_BASE + 0x0010) +#define RKVPSS_RATIO2_VIR_SIZE (RKVPSS_RATIO2_BASE + 0x0014) +#define RKVPSS_RATIO2_OFFS (RKVPSS_RATIO2_BASE + 0x0018) +#define RKVPSS_RATIO2_COLOR (RKVPSS_RATIO2_BASE + 0x001c) +#define RKVPSS_RATIO2_ACT_SIZE_SHD (RKVPSS_RATIO2_BASE + 0x0030) +#define RKVPSS_RATIO2_VIR_SIZE_SHD (RKVPSS_RATIO2_BASE + 0x0034) +#define RKVPSS_RATIO2_OFFS_SHD (RKVPSS_RATIO2_BASE + 0x0038) +#define RKVPSS_RATIO2_COLOR_SHD (RKVPSS_RATIO2_BASE + 0x003c) + +#define RKVPSS_RATIO3_BASE 0x2d00 +#define RKVPSS_RATIO3_CTRL (RKVPSS_RATIO3_BASE + 0x0000) +#define RKVPSS_RATIO3_UPDATE (RKVPSS_RATIO3_BASE + 0x0004) +#define RKVPSS_RATIO3_ACT_SIZE (RKVPSS_RATIO3_BASE + 0x0010) +#define RKVPSS_RATIO3_VIR_SIZE (RKVPSS_RATIO3_BASE + 0x0014) +#define RKVPSS_RATIO3_OFFS (RKVPSS_RATIO3_BASE + 0x0018) +#define RKVPSS_RATIO3_COLOR (RKVPSS_RATIO3_BASE + 0x001c) +#define RKVPSS_RATIO3_ACT_SIZE_SHD (RKVPSS_RATIO3_BASE + 0x0030) +#define RKVPSS_RATIO3_VIR_SIZE_SHD (RKVPSS_RATIO3_BASE + 0x0034) +#define RKVPSS_RATIO3_OFFS_SHD (RKVPSS_RATIO3_BASE + 0x0038) +#define RKVPSS_RATIO3_COLOR_SHD (RKVPSS_RATIO3_BASE + 0x003c) + +#define RKVPSS_SCALE1_BASE 0x1c00 +#define RKVPSS_SCALE1_CTRL (RKVPSS_SCALE1_BASE + 0x0000) +#define RKVPSS_SCALE1_UPDATE (RKVPSS_SCALE1_BASE + 0x0004) +#define RKVPSS_SCALE1_SRC_SIZE (RKVPSS_SCALE1_BASE + 0x0008) +#define RKVPSS_SCALE1_DST_SIZE (RKVPSS_SCALE1_BASE + 0x000c) +#define RKVPSS_SCALE1_HY_FAC (RKVPSS_SCALE1_BASE + 0x0010) +#define RKVPSS_SCALE1_HC_FAC (RKVPSS_SCALE1_BASE + 0x0014) +#define RKVPSS_SCALE1_VY_FAC (RKVPSS_SCALE1_BASE + 0x0018) +#define RKVPSS_SCALE1_VC_FAC (RKVPSS_SCALE1_BASE + 0x001c) +#define RKVPSS_SCALE1_HY_OFFS (RKVPSS_SCALE1_BASE + 0x0020) +#define RKVPSS_SCALE1_HC_OFFS (RKVPSS_SCALE1_BASE + 0x0024) +#define RKVPSS_SCALE1_VY_OFFS (RKVPSS_SCALE1_BASE + 0x0028) +#define RKVPSS_SCALE1_VC_OFFS (RKVPSS_SCALE1_BASE + 0x002c) +#define RKVPSS_SCALE1_HY_SIZE (RKVPSS_SCALE1_BASE + 0x0040) +#define RKVPSS_SCALE1_HC_SIZE (RKVPSS_SCALE1_BASE + 0x0044) +#define RKVPSS_SCALE1_HY_OFFS_MI (RKVPSS_SCALE1_BASE + 0x0048) +#define RKVPSS_SCALE1_HC_OFFS_MI (RKVPSS_SCALE1_BASE + 0x004c) +#define RKVPSS_SCALE1_IN_CROP_OFFSET (RKVPSS_SCALE1_BASE + 0x0050) +#define RKVPSS_SCALE1_CTRL_SHD (RKVPSS_SCALE1_BASE + 0x0080) +#define RKVPSS_SCALE1_SRC_SIZE_SHD (RKVPSS_SCALE1_BASE + 0x0088) +#define RKVPSS_SCALE1_DST_SIZE_SHD (RKVPSS_SCALE1_BASE + 0x008c) +#define RKVPSS_SCALE1_HY_FAC_SHD (RKVPSS_SCALE1_BASE + 0x0090) +#define RKVPSS_SCALE1_HC_FAC_SHD (RKVPSS_SCALE1_BASE + 0x0094) +#define RKVPSS_SCALE1_VY_FAC_SHD (RKVPSS_SCALE1_BASE + 0x0098) +#define RKVPSS_SCALE1_VC_FAC_SHD (RKVPSS_SCALE1_BASE + 0x009c) +#define RKVPSS_SCALE1_HY_OFFS_SHD (RKVPSS_SCALE1_BASE + 0x00a0) +#define RKVPSS_SCALE1_HC_OFFS_SHD (RKVPSS_SCALE1_BASE + 0x00a4) +#define RKVPSS_SCALE1_VY_OFFS_SHD (RKVPSS_SCALE1_BASE + 0x00a8) +#define RKVPSS_SCALE1_VC_OFFS_SHD (RKVPSS_SCALE1_BASE + 0x00ac) +#define RKVPSS_SCALE1_HY_SIZE_SHD (RKVPSS_SCALE1_BASE + 0x00c0) +#define RKVPSS_SCALE1_HC_SIZE_SHD (RKVPSS_SCALE1_BASE + 0x00c4) +#define RKVPSS_SCALE1_HY_OFFS_MI_SHD (RKVPSS_SCALE1_BASE + 0x00c8) +#define RKVPSS_SCALE1_HC_OFFS_MI_SHD (RKVPSS_SCALE1_BASE + 0x00cc) +#define RKVPSS_SCALE1_IN_CROP_OFFSET_SHD (RKVPSS_SCALE1_BASE + 0x00d0) + +#define RKVPSS_SCALE2_BASE 0x2800 +#define RKVPSS_SCALE2_CTRL (RKVPSS_SCALE2_BASE + 0x0000) +#define RKVPSS_SCALE2_UPDATE (RKVPSS_SCALE2_BASE + 0x0004) +#define RKVPSS_SCALE2_SRC_SIZE (RKVPSS_SCALE2_BASE + 0x0008) +#define RKVPSS_SCALE2_DST_SIZE (RKVPSS_SCALE2_BASE + 0x000c) +#define RKVPSS_SCALE2_HY_FAC (RKVPSS_SCALE2_BASE + 0x0010) +#define RKVPSS_SCALE2_HC_FAC (RKVPSS_SCALE2_BASE + 0x0014) +#define RKVPSS_SCALE2_VY_FAC (RKVPSS_SCALE2_BASE + 0x0018) +#define RKVPSS_SCALE2_VC_FAC (RKVPSS_SCALE2_BASE + 0x001c) +#define RKVPSS_SCALE2_HY_OFFS (RKVPSS_SCALE2_BASE + 0x0020) +#define RKVPSS_SCALE2_HC_OFFS (RKVPSS_SCALE2_BASE + 0x0024) +#define RKVPSS_SCALE2_VY_OFFS (RKVPSS_SCALE2_BASE + 0x0028) +#define RKVPSS_SCALE2_VC_OFFS (RKVPSS_SCALE2_BASE + 0x002c) +#define RKVPSS_SCALE2_HY_SIZE (RKVPSS_SCALE2_BASE + 0x0040) +#define RKVPSS_SCALE2_HC_SIZE (RKVPSS_SCALE2_BASE + 0x0044) +#define RKVPSS_SCALE2_HY_OFFS_MI (RKVPSS_SCALE2_BASE + 0x0048) +#define RKVPSS_SCALE2_HC_OFFS_MI (RKVPSS_SCALE2_BASE + 0x004c) +#define RKVPSS_SCALE2_IN_CROP_OFFSET (RKVPSS_SCALE2_BASE + 0x0050) +#define RKVPSS_SCALE2_CTRL_SHD (RKVPSS_SCALE2_BASE + 0x0080) +#define RKVPSS_SCALE2_SRC_SIZE_SHD (RKVPSS_SCALE2_BASE + 0x0088) +#define RKVPSS_SCALE2_DST_SIZE_SHD (RKVPSS_SCALE2_BASE + 0x008c) +#define RKVPSS_SCALE2_HY_FAC_SHD (RKVPSS_SCALE2_BASE + 0x0090) +#define RKVPSS_SCALE2_HC_FAC_SHD (RKVPSS_SCALE2_BASE + 0x0094) +#define RKVPSS_SCALE2_VY_FAC_SHD (RKVPSS_SCALE2_BASE + 0x0098) +#define RKVPSS_SCALE2_VC_FAC_SHD (RKVPSS_SCALE2_BASE + 0x009c) +#define RKVPSS_SCALE2_HY_OFFS_SHD (RKVPSS_SCALE2_BASE + 0x00a0) +#define RKVPSS_SCALE2_HC_OFFS_SHD (RKVPSS_SCALE2_BASE + 0x00a4) +#define RKVPSS_SCALE2_VY_OFFS_SHD (RKVPSS_SCALE2_BASE + 0x00a8) +#define RKVPSS_SCALE2_VC_OFFS_SHD (RKVPSS_SCALE2_BASE + 0x00ac) +#define RKVPSS_SCALE2_HY_SIZE_SHD (RKVPSS_SCALE2_BASE + 0x00c0) +#define RKVPSS_SCALE2_HC_SIZE_SHD (RKVPSS_SCALE2_BASE + 0x00c4) +#define RKVPSS_SCALE2_HY_OFFS_MI_SHD (RKVPSS_SCALE2_BASE + 0x00c8) +#define RKVPSS_SCALE2_HC_OFFS_MI_SHD (RKVPSS_SCALE2_BASE + 0x00cc) +#define RKVPSS_SCALE2_IN_CROP_OFFSET_SHD (RKVPSS_SCALE2_BASE + 0x00d0) + +#define RKVPSS_SCALE3_BASE 0x2c00 +#define RKVPSS_SCALE3_CTRL (RKVPSS_SCALE3_BASE + 0x0000) +#define RKVPSS_SCALE3_UPDATE (RKVPSS_SCALE3_BASE + 0x0004) +#define RKVPSS_SCALE3_SRC_SIZE (RKVPSS_SCALE3_BASE + 0x0008) +#define RKVPSS_SCALE3_DST_SIZE (RKVPSS_SCALE3_BASE + 0x000c) +#define RKVPSS_SCALE3_HY_FAC (RKVPSS_SCALE3_BASE + 0x0010) +#define RKVPSS_SCALE3_HC_FAC (RKVPSS_SCALE3_BASE + 0x0014) +#define RKVPSS_SCALE3_VY_FAC (RKVPSS_SCALE3_BASE + 0x0018) +#define RKVPSS_SCALE3_VC_FAC (RKVPSS_SCALE3_BASE + 0x001c) +#define RKVPSS_SCALE3_HY_OFFS (RKVPSS_SCALE3_BASE + 0x0020) +#define RKVPSS_SCALE3_HC_OFFS (RKVPSS_SCALE3_BASE + 0x0024) +#define RKVPSS_SCALE3_VY_OFFS (RKVPSS_SCALE3_BASE + 0x0028) +#define RKVPSS_SCALE3_VC_OFFS (RKVPSS_SCALE3_BASE + 0x002c) +#define RKVPSS_SCALE3_HY_SIZE (RKVPSS_SCALE3_BASE + 0x0040) +#define RKVPSS_SCALE3_HC_SIZE (RKVPSS_SCALE3_BASE + 0x0044) +#define RKVPSS_SCALE3_HY_OFFS_MI (RKVPSS_SCALE3_BASE + 0x0048) +#define RKVPSS_SCALE3_HC_OFFS_MI (RKVPSS_SCALE3_BASE + 0x004c) +#define RKVPSS_SCALE3_IN_CROP_OFFSET (RKVPSS_SCALE3_BASE + 0x0050) +#define RKVPSS_SCALE3_CTRL_SHD (RKVPSS_SCALE3_BASE + 0x0080) +#define RKVPSS_SCALE3_SRC_SIZE_SHD (RKVPSS_SCALE3_BASE + 0x0088) +#define RKVPSS_SCALE3_DST_SIZE_SHD (RKVPSS_SCALE3_BASE + 0x008c) +#define RKVPSS_SCALE3_HY_FAC_SHD (RKVPSS_SCALE3_BASE + 0x0090) +#define RKVPSS_SCALE3_HC_FAC_SHD (RKVPSS_SCALE3_BASE + 0x0094) +#define RKVPSS_SCALE3_VY_FAC_SHD (RKVPSS_SCALE3_BASE + 0x0098) +#define RKVPSS_SCALE3_VC_FAC_SHD (RKVPSS_SCALE3_BASE + 0x009c) +#define RKVPSS_SCALE3_HY_OFFS_SHD (RKVPSS_SCALE3_BASE + 0x00a0) +#define RKVPSS_SCALE3_HC_OFFS_SHD (RKVPSS_SCALE3_BASE + 0x00a4) +#define RKVPSS_SCALE3_VY_OFFS_SHD (RKVPSS_SCALE3_BASE + 0x00a8) +#define RKVPSS_SCALE3_VC_OFFS_SHD (RKVPSS_SCALE3_BASE + 0x00ac) +#define RKVPSS_SCALE3_HY_SIZE_SHD (RKVPSS_SCALE3_BASE + 0x00c0) +#define RKVPSS_SCALE3_HC_SIZE_SHD (RKVPSS_SCALE3_BASE + 0x00c4) +#define RKVPSS_SCALE3_HY_OFFS_MI_SHD (RKVPSS_SCALE3_BASE + 0x00c8) +#define RKVPSS_SCALE3_HC_OFFS_MI_SHD (RKVPSS_SCALE3_BASE + 0x00cc) +#define RKVPSS_SCALE3_IN_CROP_OFFSET_SHD (RKVPSS_SCALE3_BASE + 0x00d0) + +#define RKVPSS_MI_BASE 0x3000 +#define RKVPSS_MI_WR_CTRL (RKVPSS_MI_BASE + 0x0000) +#define RKVPSS_MI_WR_INIT (RKVPSS_MI_BASE + 0x0004) +#define RKVPSS_MI_WR_WRAP_CTRL (RKVPSS_MI_BASE + 0x0010) +#define RKVPSS_MI_WR_VFLIP_CTRL (RKVPSS_MI_BASE + 0x0014) +#define RKVPSS_MI_HURRY_CTRL (RKVPSS_MI_BASE + 0x0040) +#define RKVPSS_MI_ARQOS_CTRL (RKVPSS_MI_BASE + 0x0044) +#define RKVPSS_MI_AWQOS_CTRL (RKVPSS_MI_BASE + 0x0048) +#define RKVPSS_MI_IMSC (RKVPSS_MI_BASE + 0x0050) +#define RKVPSS_MI_RIS (RKVPSS_MI_BASE + 0x0054) +#define RKVPSS_MI_MIS (RKVPSS_MI_BASE + 0x0058) +#define RKVPSS_MI_ICR (RKVPSS_MI_BASE + 0x005c) +#define RKVPSS_MI_ISR (RKVPSS_MI_BASE + 0x0060) +#define RKVPSS_MI_STATUS (RKVPSS_MI_BASE + 0x0070) +#define RKVPSS_MI_STATUS_CLR (RKVPSS_MI_BASE + 0x0074) +#define RKVPSS_MI_RD_CTRL (RKVPSS_MI_BASE + 0x0100) +#define RKVPSS_MI_RD_INIT (RKVPSS_MI_BASE + 0x0104) +#define RKVPSS_MI_RD_START (RKVPSS_MI_BASE + 0x0108) +#define RKVPSS_MI_RD_Y_BASE (RKVPSS_MI_BASE + 0x0110) +#define RKVPSS_MI_RD_Y_WIDTH (RKVPSS_MI_BASE + 0x0114) +#define RKVPSS_MI_RD_Y_HEIGHT (RKVPSS_MI_BASE + 0x0118) +#define RKVPSS_MI_RD_Y_STRIDE (RKVPSS_MI_BASE + 0x011c) +#define RKVPSS_MI_RD_C_BASE (RKVPSS_MI_BASE + 0x0120) +#define RKVPSS_MI_RD_Y_BASE_SHD (RKVPSS_MI_BASE + 0x0130) +#define RKVPSS_MI_RD_C_BASE_SHD (RKVPSS_MI_BASE + 0x0134) +#define RKVPSS_MI_RD_Y_WIDTH_SHD (RKVPSS_MI_BASE + 0x0138) +#define RKVPSS_MI_RD_Y_HEIGHT_SHD (RKVPSS_MI_BASE + 0x013c) +#define RKVPSS_AXI_CFG_RD_CTRL (RKVPSS_MI_BASE + 0x0160) +#define RKVPSS_AXI_CFG_RD_BASE (RKVPSS_MI_BASE + 0x0164) +#define RKVPSS_AXI_CFG_RD_H_WSIZE (RKVPSS_MI_BASE + 0x0168) +#define RKVPSS_AXI_CFG_RD_V_SIZE (RKVPSS_MI_BASE + 0x016c) +#define RKVPSS_MI_CHN0_WR_CTRL (RKVPSS_MI_BASE + 0x0200) +#define RKVPSS_MI_CHN0_WR_Y_BASE (RKVPSS_MI_BASE + 0x0210) +#define RKVPSS_MI_CHN0_WR_Y_SIZE (RKVPSS_MI_BASE + 0x0214) +#define RKVPSS_MI_CHN0_WR_Y_OFFS_CNT (RKVPSS_MI_BASE + 0x0218) +#define RKVPSS_MI_CHN0_WR_Y_OFFS_CNT_START (RKVPSS_MI_BASE + 0x021c) +#define RKVPSS_MI_CHN0_WR_CB_BASE (RKVPSS_MI_BASE + 0x0220) +#define RKVPSS_MI_CHN0_WR_CB_SIZE (RKVPSS_MI_BASE + 0x0224) +#define RKVPSS_MI_CHN0_WR_CB_OFFS_CNT (RKVPSS_MI_BASE + 0x0228) +#define RKVPSS_MI_CHN0_WR_CB_OFFS_CNT_START (RKVPSS_MI_BASE + 0x022c) +#define RKVPSS_MI_CHN0_WR_Y_STRIDE (RKVPSS_MI_BASE + 0x0230) +#define RKVPSS_MI_CHN0_WR_Y_PIC_WIDTH (RKVPSS_MI_BASE + 0x0234) +#define RKVPSS_MI_CHN0_WR_Y_PIC_SIZE (RKVPSS_MI_BASE + 0x023c) +#define RKVPSS_MI_CHN0_WR_CTRL_SHD (RKVPSS_MI_BASE + 0x0250) +#define RKVPSS_MI_CHN0_WR_Y_BASE_SHD (RKVPSS_MI_BASE + 0x0260) +#define RKVPSS_MI_CHN0_WR_Y_SIZE_SHD (RKVPSS_MI_BASE + 0x0264) +#define RKVPSS_MI_CHN0_WR_Y_OFFS_CNT_SHD (RKVPSS_MI_BASE + 0x0268) +#define RKVPSS_MI_CHN0_WR_CB_BASE_SHD (RKVPSS_MI_BASE + 0x0270) +#define RKVPSS_MI_CHN0_WR_CB_SIZE_SHD (RKVPSS_MI_BASE + 0x0274) +#define RKVPSS_MI_CHN0_WR_CB_OFFS_CNT_SHD (RKVPSS_MI_BASE + 0x0278) +#define RKVPSS_MI_CHN0_WR_Y_END_ADDR (RKVPSS_MI_BASE + 0x0280) +#define RKVPSS_MI_CHN0_WR_CB_END_ADDR (RKVPSS_MI_BASE + 0x0284) +#define RKVPSS_MI_CHN0_WR_LINE_CNT (RKVPSS_MI_BASE + 0x0288) +#define RKVPSS_MI_CHN1_WR_CTRL (RKVPSS_MI_BASE + 0x0300) +#define RKVPSS_MI_CHN1_WR_Y_BASE (RKVPSS_MI_BASE + 0x0310) +#define RKVPSS_MI_CHN1_WR_Y_SIZE (RKVPSS_MI_BASE + 0x0314) +#define RKVPSS_MI_CHN1_WR_Y_OFFS_CNT (RKVPSS_MI_BASE + 0x0318) +#define RKVPSS_MI_CHN1_WR_Y_OFFS_CNT_START (RKVPSS_MI_BASE + 0x031c) +#define RKVPSS_MI_CHN1_WR_CB_BASE (RKVPSS_MI_BASE + 0x0320) +#define RKVPSS_MI_CHN1_WR_CB_SIZE (RKVPSS_MI_BASE + 0x0324) +#define RKVPSS_MI_CHN1_WR_CB_OFFS_CNT (RKVPSS_MI_BASE + 0x0328) +#define RKVPSS_MI_CHN1_WR_CB_OFFS_CNT_START (RKVPSS_MI_BASE + 0x032c) +#define RKVPSS_MI_CHN1_WR_Y_STRIDE (RKVPSS_MI_BASE + 0x0330) +#define RKVPSS_MI_CHN1_WR_Y_PIC_WIDTH (RKVPSS_MI_BASE + 0x0334) +#define RKVPSS_MI_CHN1_WR_Y_PIC_SIZE (RKVPSS_MI_BASE + 0x033c) +#define RKVPSS_MI_CHN1_WR_CTRL_SHD (RKVPSS_MI_BASE + 0x0350) +#define RKVPSS_MI_CHN1_WR_Y_BASE_SHD (RKVPSS_MI_BASE + 0x0360) +#define RKVPSS_MI_CHN1_WR_Y_SIZE_SHD (RKVPSS_MI_BASE + 0x0364) +#define RKVPSS_MI_CHN1_WR_Y_OFFS_CNT_SHD (RKVPSS_MI_BASE + 0x0368) +#define RKVPSS_MI_CHN1_WR_CB_BASE_SHD (RKVPSS_MI_BASE + 0x0370) +#define RKVPSS_MI_CHN1_WR_CB_SIZE_SHD (RKVPSS_MI_BASE + 0x0374) +#define RKVPSS_MI_CHN1_WR_CB_OFFS_CNT_SHD (RKVPSS_MI_BASE + 0x0378) +#define RKVPSS_MI_CHN1_WR_Y_END_ADDR (RKVPSS_MI_BASE + 0x0380) +#define RKVPSS_MI_CHN1_WR_CB_END_ADDR (RKVPSS_MI_BASE + 0x0384) +#define RKVPSS_MI_CHN1_WR_LINE_CNT (RKVPSS_MI_BASE + 0x0388) +#define RKVPSS_MI_CHN1_WR_Y2R_COE00 (RKVPSS_MI_BASE + 0x03a0) +#define RKVPSS_MI_CHN1_WR_Y2R_COE01 (RKVPSS_MI_BASE + 0x03a4) +#define RKVPSS_MI_CHN1_WR_Y2R_COE02 (RKVPSS_MI_BASE + 0x03a8) +#define RKVPSS_MI_CHN1_WR_Y2R_OFF0 (RKVPSS_MI_BASE + 0x03ac) +#define RKVPSS_MI_CHN1_WR_Y2R_COE10 (RKVPSS_MI_BASE + 0x03b0) +#define RKVPSS_MI_CHN1_WR_Y2R_COE11 (RKVPSS_MI_BASE + 0x03b4) +#define RKVPSS_MI_CHN1_WR_Y2R_COE12 (RKVPSS_MI_BASE + 0x03b8) +#define RKVPSS_MI_CHN1_WR_Y2R_OFF1 (RKVPSS_MI_BASE + 0x03bc) +#define RKVPSS_MI_CHN1_WR_Y2R_COE20 (RKVPSS_MI_BASE + 0x03c0) +#define RKVPSS_MI_CHN1_WR_Y2R_COE21 (RKVPSS_MI_BASE + 0x03c4) +#define RKVPSS_MI_CHN1_WR_Y2R_COE32 (RKVPSS_MI_BASE + 0x03c8) +#define RKVPSS_MI_CHN1_WR_Y2R_OFF2 (RKVPSS_MI_BASE + 0x03cc) +#define RKVPSS_MI_CHN2_WR_CTRL (RKVPSS_MI_BASE + 0x0400) +#define RKVPSS_MI_CHN2_WR_Y_BASE (RKVPSS_MI_BASE + 0x0410) +#define RKVPSS_MI_CHN2_WR_Y_SIZE (RKVPSS_MI_BASE + 0x0414) +#define RKVPSS_MI_CHN2_WR_Y_OFFS_CNT (RKVPSS_MI_BASE + 0x0418) +#define RKVPSS_MI_CHN2_WR_Y_OFFS_CNT_START (RKVPSS_MI_BASE + 0x041c) +#define RKVPSS_MI_CHN2_WR_CB_BASE (RKVPSS_MI_BASE + 0x0420) +#define RKVPSS_MI_CHN2_WR_CB_SIZE (RKVPSS_MI_BASE + 0x0424) +#define RKVPSS_MI_CHN2_WR_CB_OFFS_CNT (RKVPSS_MI_BASE + 0x0428) +#define RKVPSS_MI_CHN2_WR_CB_OFFS_CNT_START (RKVPSS_MI_BASE + 0x042c) +#define RKVPSS_MI_CHN2_WR_Y_STRIDE (RKVPSS_MI_BASE + 0x0430) +#define RKVPSS_MI_CHN2_WR_Y_PIC_WIDTH (RKVPSS_MI_BASE + 0x0434) +#define RKVPSS_MI_CHN2_WR_Y_PIC_SIZE (RKVPSS_MI_BASE + 0x043c) +#define RKVPSS_MI_CHN2_WR_CTRL_SHD (RKVPSS_MI_BASE + 0x0450) +#define RKVPSS_MI_CHN2_WR_Y_BASE_SHD (RKVPSS_MI_BASE + 0x0460) +#define RKVPSS_MI_CHN2_WR_Y_SIZE_SHD (RKVPSS_MI_BASE + 0x0464) +#define RKVPSS_MI_CHN2_WR_Y_OFFS_CNT_SHD (RKVPSS_MI_BASE + 0x0468) +#define RKVPSS_MI_CHN2_WR_CB_BASE_SHD (RKVPSS_MI_BASE + 0x0470) +#define RKVPSS_MI_CHN2_WR_CB_SIZE_SHD (RKVPSS_MI_BASE + 0x0474) +#define RKVPSS_MI_CHN2_WR_CB_OFFS_CNT_SHD (RKVPSS_MI_BASE + 0x0478) +#define RKVPSS_MI_CHN2_WR_Y_END_ADDR (RKVPSS_MI_BASE + 0x0480) +#define RKVPSS_MI_CHN2_WR_CB_END_ADDR (RKVPSS_MI_BASE + 0x0484) +#define RKVPSS_MI_CHN2_WR_LINE_CNT (RKVPSS_MI_BASE + 0x0488) +#define RKVPSS_MI_CHN3_WR_CTRL (RKVPSS_MI_BASE + 0x0500) +#define RKVPSS_MI_CHN3_WR_Y_BASE (RKVPSS_MI_BASE + 0x0510) +#define RKVPSS_MI_CHN3_WR_Y_SIZE (RKVPSS_MI_BASE + 0x0514) +#define RKVPSS_MI_CHN3_WR_Y_OFFS_CNT (RKVPSS_MI_BASE + 0x0518) +#define RKVPSS_MI_CHN3_WR_Y_OFFS_CNT_START (RKVPSS_MI_BASE + 0x051c) +#define RKVPSS_MI_CHN3_WR_CB_BASE (RKVPSS_MI_BASE + 0x0520) +#define RKVPSS_MI_CHN3_WR_CB_SIZE (RKVPSS_MI_BASE + 0x0524) +#define RKVPSS_MI_CHN3_WR_CB_OFFS_CNT (RKVPSS_MI_BASE + 0x0528) +#define RKVPSS_MI_CHN3_WR_CB_OFFS_CNT_START (RKVPSS_MI_BASE + 0x052c) +#define RKVPSS_MI_CHN3_WR_Y_STRIDE (RKVPSS_MI_BASE + 0x0530) +#define RKVPSS_MI_CHN3_WR_Y_PIC_WIDTH (RKVPSS_MI_BASE + 0x0534) +#define RKVPSS_MI_CHN3_WR_Y_PIC_SIZE (RKVPSS_MI_BASE + 0x053c) +#define RKVPSS_MI_CHN3_WR_CTRL_SHD (RKVPSS_MI_BASE + 0x0550) +#define RKVPSS_MI_CHN3_WR_Y_BASE_SHD (RKVPSS_MI_BASE + 0x0560) +#define RKVPSS_MI_CHN3_WR_Y_SIZE_SHD (RKVPSS_MI_BASE + 0x0564) +#define RKVPSS_MI_CHN3_WR_Y_OFFS_CNT_SHD (RKVPSS_MI_BASE + 0x0568) +#define RKVPSS_MI_CHN3_WR_CB_BASE_SHD (RKVPSS_MI_BASE + 0x0570) +#define RKVPSS_MI_CHN3_WR_CB_SIZE_SHD (RKVPSS_MI_BASE + 0x0574) +#define RKVPSS_MI_CHN3_WR_CB_OFFS_CNT_SHD (RKVPSS_MI_BASE + 0x0578) +#define RKVPSS_MI_CHN3_WR_Y_END_ADDR (RKVPSS_MI_BASE + 0x0580) +#define RKVPSS_MI_CHN3_WR_CB_END_ADDR (RKVPSS_MI_BASE + 0x0584) +#define RKVPSS_MI_CHN3_WR_LINE_CNT (RKVPSS_MI_BASE + 0x0588) +#define RKVPSS_MI_RD_R2Y_COE00 (RKVPSS_MI_BASE + 0x058c) +#define RKVPSS_MI_RD_R2Y_COE01 (RKVPSS_MI_BASE + 0x0590) +#define RKVPSS_MI_RD_R2Y_COE02 (RKVPSS_MI_BASE + 0x0594) +#define RKVPSS_MI_RD_R2Y_OFF0 (RKVPSS_MI_BASE + 0x0598) +#define RKVPSS_MI_RD_R2Y_COE10 (RKVPSS_MI_BASE + 0x059c) +#define RKVPSS_MI_RD_R2Y_COE11 (RKVPSS_MI_BASE + 0x05a0) +#define RKVPSS_MI_RD_R2Y_COE12 (RKVPSS_MI_BASE + 0x05a4) +#define RKVPSS_MI_RD_R2Y_OFF1 (RKVPSS_MI_BASE + 0x05a8) +#define RKVPSS_MI_RD_R2Y_COE20 (RKVPSS_MI_BASE + 0x05ac) +#define RKVPSS_MI_RD_R2Y_COE21 (RKVPSS_MI_BASE + 0x05b0) +#define RKVPSS_MI_RD_R2Y_COE22 (RKVPSS_MI_BASE + 0x05b4) +#define RKVPSS_MI_RD_R2Y_OFF2 (RKVPSS_MI_BASE + 0x05b8) + +/* VPSS_CTRL */ +#define RKVPSS_MIR_EN BIT(4) +#define RKVPSS_VPSS2ENC_SEL BIT(8) +#define RKVPSS_VPSS2ENC_PIPE_EN BIT(9) +#define RKVPSS_VPSS2ENC_CNT_SEL BIT(10) +#define RKVPSS_FRM_END_MODE BIT(24) +#define RKVPSS_SUB_FRM_ST_EN BIT(25) +#define RKVPSS_NOC_IDLE_DIS BIT(28) +#define RKVPSS_ACK_FRM_PRO_DIS BIT(29) +#define RKVPSS_CHN_JUDGE_DIS BIT(30) +#define RKVPSS_ALL_CLK_DIS BIT(31) + +/* VPSS_ONLINE */ +#define RKVPSS_ISP2VPSS_CHN0_SEL(x) ((x) & 0x3) +#define RKVPSS_ISP2VPSS_CHN1_SEL(x) (((x) & 0x3) << 2) +#define RKVPSS_ISP2VPSS_CHN2_SEL(x) (((x) & 0x3) << 4) +#define RKVPSS_ISP2VPSS_CHN3_SEL(x) (((x) & 0x3) << 6) +#define RKVPSS_ONLINE_MODE_MASK GENMASK(17, 16) +#define RKVPSS_VPSS_OFFLINE 0 +#define RKVPSS_ISP2VPSS_ONLINE1 BIT(16) +#define RKVPSS_ISP2VPSS_ONLINE2 BIT(17) +#define RKVPSS_ISP2VPSS_ONLINE2_CMSC_EN BIT(18) + +/* VPSS_UPDATE */ +#define RKVPSS_CFG_FORCE_UPD BIT(0) +#define RKVPSS_CFG_GEN_UPD BIT(1) +#define RKVPSS_MIR_FORCE_UPD BIT(4) +#define RKVPSS_MIR_GEN_UPD BIT(5) +#define RKVPSS_CHN_FORCE_UPD BIT(8) +#define RKVPSS_ONLINE2_CHN_FORCE_UPD BIT(9) + +/* VPSS_CLK_EN */ +#define RKVPSS_MIR_CLK_EN BIT(0) +#define RKVPSS_CMSC_CLK_EN BIT(1) +#define RKVPSS_CR_CLK_EN BIT(2) +#define RKVPSS_CR1_CLK_EN BIT(3) +#define RKVPSS_SCL0_CLK_EN BIT(4) +#define RKVPSS_SCL1_CLK_EN BIT(5) +#define RKVPSS_SCL2_CLK_EN BIT(6) +#define RKVPSS_SCL3_CLK_EN BIT(7) +#define RKVPSS_RATIO0_CLK_EN BIT(8) +#define RKVPSS_RATIO1_CLK_EN BIT(9) +#define RKVPSS_RATIO2_CLK_EN BIT(10) +#define RKVPSS_RATIO3_CLK_EN BIT(11) +#define RKVPSS_MI_CLK_EN BIT(16) + +/* VPSS_CLK_GATE */ +#define RKVPSS_MIR_CKG_DIS BIT(0) +#define RKVPSS_CMSC_CKG_DIS BIT(1) +#define RKVPSS_SCL0_CKG_DIS BIT(4) +#define RKVPSS_SCL1_CKG_DIS BIT(5) +#define RKVPSS_SCL2_CKG_DIS BIT(6) +#define RKVPSS_SCL3_CKG_DIS BIT(7) +#define RKVPSS_RATIO0_CKG_DIS BIT(8) +#define RKVPSS_RATIO1_CKG_DIS BIT(9) +#define RKVPSS_RATIO2_CKG_DIS BIT(10) +#define RKVPSS_RATIO3_CKG_DIS BIT(11) +#define RKVPSS_MI_CHN0_CKG_DIS BIT(16) +#define RKVPSS_MI_CHN1_CKG_DIS BIT(17) +#define RKVPSS_MI_CHN2_CKG_DIS BIT(18) +#define RKVPSS_MI_CHN3_CKG_DIS BIT(19) +#define RKVPSS_MI_RD_CKG_DIS BIT(24) + +/* VPSS_RESET */ +#define RKVPSS_SOFT_RST BIT(0) +#define RKVPSS_RST_PROTECT_DIS BIT(30) +#define RKVPSS_RST_SAFETY_DONE_CLR BIT(31) + +/* VPSS_SLICE_CTRL */ +#define RKVPSS_SLICE_ST BIT(0) +#define RKVPSS_SLICE_EN BIT(1) + +/* VPSS_IRQ_CFG */ +#define RKVPSS_CROP_IN_IRQ_LINE(x) ((x) & 0x3fff) + +/* VPSS_IMSC */ +#define RKVPSS_CROP_IN_FRM_ST BIT(0) +#define RKVPSS_CROP_IN_FRM_END BIT(1) +#define RKVPSS_CROP_IN_CFG_IRQ BIT(2) +#define RKVPSS_ALL_FRM_END BIT(3) +#define RKVPSS_MIR_FRM_END BIT(4) +#define RKVPSS_CROP1_IN_FRM_END BIT(5) +#define RKVPSS_CROP1_IN_CFG_IRQ BIT(6) +#define RKVPSS_ISP_ALL_FRM_END BIT(7) +#define RKVPSS_SCL0_IN_FRM_END BIT(8) +#define RKVPSS_SCL1_IN_FRM_END BIT(9) +#define RKVPSS_SCL2_IN_FRM_END BIT(10) +#define RKVPSS_SCL3_IN_FRM_END BIT(11) +#define RKVPSS_RATIO0_IN_FRM_END BIT(12) +#define RKVPSS_RATIO1_IN_FRM_END BIT(13) +#define RKVPSS_RATIO2_IN_FRM_END BIT(14) +#define RKVPSS_RATIO3_IN_FRM_END BIT(15) + +/* VPSS_CTRL_SHD */ +#define RKVPSS_MIR_EN_SHD BIT(4) + +/* VPSS_ONLINE_SHD */ +#define RKVPSS_CHN0_EN_SHD BIT(0) +#define RKVPSS_ISP_CHN0_EN_SHD BIT(1) +#define RKVPSS_CHN1_EN_SHD BIT(2) +#define RKVPSS_ISP_CHN1_EN_SHD BIT(3) +#define RKVPSS_CHN2_EN_SHD BIT(4) +#define RKVPSS_ISP_CHN2_EN_SHD BIT(5) +#define RKVPSS_CHN3_EN_SHD BIT(6) +#define RKVPSS_ISP_CHN3_EN_SHD BIT(7) +#define RKVPSS_ISP2VPSS_ONLINE1_EN_SHD BIT(16) +#define RKVPSS_ISP2VPSS_ONLINE2_EN_SHD BIT(17) +#define RKVPSS_ISP2VPSS_CMSC_EN_SHD BIT(18) + +/* VPSS_WORKING */ +#define RKVPSS_MIR_WORKING BIT(0) +#define RKVPSS_CMSC_WORKING BIT(1) +#define RKVPSS_SCL0_WORKING BIT(4) +#define RKVPSS_SCL1_WORKING BIT(5) +#define RKVPSS_SCL2_WORKING BIT(6) +#define RKVPSS_SCL3_WORKING BIT(7) +#define RKVPSS_RATIO0_WORKING BTI(8) +#define RKVPSS_RATIO1_WORKING BTI(9) +#define RKVPSS_RATIO2_WORKING BTI(10) +#define RKVPSS_RATIO3_WORKING BTI(11) +#define RKVPSS_ISP_WORKING BIT(28) +#define RKVPSS_VPSS_WORKING BIT(29) +#define RKVPSS_ISP2VPSS_WORKING BIT(30) +#define RKVPSS_OFFLINE_WORKING BIT(31) + +/* VPSS_LINE_CNT0 */ +#define RKVPSS_CROP_IN_LINE(x) ((x) & 0x3fff) +#define RKVPSS_CHN0_IN_LINE(x) (((x) & 0x3fff) >> 16) + +/* VPSS_LINE_CNT1 */ +#define RKVPSS_CROP1_IN_LINE(x) ((x) & 0x3fff) + +/* VPSS_Y2R_COE00 */ +#define RKVPSS_Y2R_RB_SWAP BIT(30) +#define RKVPSS_Y2R_EN BIT(31) + +/* CMSC_CTRL */ +#define RKVPSS_CMSC_EN BIT(0) +#define RKVPSS_CMSC_CHN_EN(x) (BIT(4) << (x)) +#define RKVPSS_CMSC_BLK_SZIE(x) (((x) & 0x3) << 8) +#define RKVPSS_CMSC_EN_SHD BIT(16) +#define RKVPSS_CMSC_CHN0_EN_SHD BIT(20) +#define RKVPSS_CMSC_CHN1_EN_SHD BIT(21) +#define RKVPSS_CMSC_CHN2_EN_SHD BIT(22) +#define RKVPSS_CMSC_CHN3_EN_SHD BIT(23) + +/* CMSC_UPDATE */ +#define RKVPSS_CMSC_FORCE_UPD BIT(4) +#define RKVPSS_CMSC_GEN_UPD BIT(5) + +/* CMSC_INSCT_CORR */ +#define RKVPSS_CMSC_WIN0_INTSCT_EXIST_CORR BIT(0) +#define RKVPSS_CMSC_WIN1_INTSCT_EXIST_CORR BIT(1) +#define RKVPSS_CMSC_WIN2_INTSCT_EXIST_CORR BIT(2) +#define RKVPSS_CMSC_WIN3_INTSCT_EXIST_CORR BIT(3) +#define RKVPSS_CMSC_WIN4_INTSCT_EXIST_CORR BIT(4) +#define RKVPSS_CMSC_WIN5_INTSCT_EXIST_CORR BIT(5) +#define RKVPSS_CMSC_WIN6_INTSCT_EXIST_CORR BIT(6) +#define RKVPSS_CMSC_WIN7_INTSCT_EXIST_CORR BIT(7) +#define RKVPSS_CMSC_WIN0_INTSCT_EXIST_CORR_DIS BIT(8) +#define RKVPSS_CMSC_WIN1_INTSCT_EXIST_CORR_DIS BIT(9) +#define RKVPSS_CMSC_WIN2_INTSCT_EXIST_CORR_DIS BIT(10) +#define RKVPSS_CMSC_WIN3_INTSCT_EXIST_CORR_DIS BIT(11) +#define RKVPSS_CMSC_WIN4_INTSCT_EXIST_CORR_DIS BIT(12) +#define RKVPSS_CMSC_WIN5_INTSCT_EXIST_CORR_DIS BIT(13) +#define RKVPSS_CMSC_WIN6_INTSCT_EXIST_CORR_DIS BIT(14) +#define RKVPSS_CMSC_WIN7_INTSCT_EXIST_CORR_DIS BIT(15) +#define RKVPSS_CMSC_WIN0_INTSCT_CONTI_CORR_DIS BIT(16) +#define RKVPSS_CMSC_WIN1_INTSCT_CONTI_CORR_DIS BIT(17) +#define RKVPSS_CMSC_WIN2_INTSCT_CONTI_CORR_DIS BIT(18) +#define RKVPSS_CMSC_WIN3_INTSCT_CONTI_CORR_DIS BIT(19) +#define RKVPSS_CMSC_WIN4_INTSCT_CONTI_CORR_DIS BIT(20) +#define RKVPSS_CMSC_WIN5_INTSCT_CONTI_CORR_DIS BIT(21) +#define RKVPSS_CMSC_WIN6_INTSCT_CONTI_CORR_DIS BIT(22) +#define RKVPSS_CMSC_WIN7_INTSCT_CONTI_CORR_DIS BIT(23) +#define RKVPSS_CMSC_WIN0_INTSCT_PTY_CORR_DIS BIT(24) +#define RKVPSS_CMSC_WIN1_INTSCT_PTY_CORR_DIS BIT(25) +#define RKVPSS_CMSC_WIN2_INTSCT_PTY_CORR_DIS BIT(26) +#define RKVPSS_CMSC_WIN3_INTSCT_PTY_CORR_DIS BIT(27) +#define RKVPSS_CMSC_WIN4_INTSCT_PTY_CORR_DIS BIT(28) +#define RKVPSS_CMSC_WIN5_INTSCT_PTY_CORR_DIS BIT(29) +#define RKVPSS_CMSC_WIN6_INTSCT_PTY_CORR_DIS BIT(30) +#define RKVPSS_CMSC_WIN7_INTSCT_PTY_CORR_DIS BIT(31) + +#define RKVPSS_CMSC_CHN_WIN_EN(x) ((x) & 0xff) +#define RKVPSS_CMSC_CHN_MODE(x) ((x) & 0xff) +#define RKVPSS_CMSK_WIN_YUV(y, u, v) ((y) | ((u) << 8) | ((v) << 16)) +#define RKVPSS_CMSC_WIN_ALPHA(x) (((x) & 0xf) << 24) +#define RKVPSS_CMSC_WIN_INTSCT_PTY_CORR(x) (((x) & 0x3) << 28) + +#define RKVPSS_CMSC_WIN_VTX(x, y) ((x) | ((y) << 16)) +#define RKVPSS_CMSC_WIN_SLP(x, y) (((x) & 0x3ffff) | (y) << 20) + +/* CROP_CTRL */ +#define RKVPSS_CROP_CHN_EN(x) (1 << (x)) +#define RKVPSS_CROP_IN_UPD_DIS BIT(31) + +/* CROP_UPDATE */ +#define RKVPSS_CROP_FORCE_UPD BIT(4) +#define RKVPSS_CROP_GEN_UPD BIT(5) +#define RKVPSS_CROP_CHN_FORCE_UPD(x) (0x100 << (x)) + +/* CROP_CTRL_SHD */ +#define RKVPSS_CROP_CHN0_EN_SHD BIT(0) +#define RKVPSS_CROP_CHN1_EN_SHD BIT(1) +#define RKVPSS_CROP_CHN2_EN_SHD BIT(2) +#define RKVPSS_CROP_CHN3_EN_SHD BIT(3) + +/* ZME_CTRL */ +#define RKVPSS_ZME_GATING_EN BIT(0) +#define RKVPSS_ZME_CLIP_EN BIT(13) +#define RKVPSS_ZME_IN_CLIP_EN BIT(14) +#define RKVPSS_ZME_8K_EN BIT(15) +#define RKVPSS_ZME_422TO420_EN BIT(30) +#define RKVPSS_ZME_SCL_YUV420_REAL_EN BIT(31) + +/* ZME_UPDATE */ +#define RKVPSS_ZME_FORCE_UPD BIT(4) +#define RKVPSS_ZME_GEN_UPD BIT(5) + +/* Y/UV_SCL_CTRL */ +#define RKVPSS_ZME_XSD_EN BIT(0) +#define RKVPSS_ZME_XSU_EN BIT(1) +#define RKVPSS_ZME_XSCL_MODE (BIT(2) | BIT(3)) +#define RKVPSS_ZME_YSD_EN BIT(4) +#define RKVPSS_ZME_YSU_EN BIT(5) +#define RKVPSS_ZME_YSCL_MODE (BIT(6) | BIT(7)) +#define RKVPSS_ZME_DERING_EN BIT(8) +#define RKVPSS_ZME_GT_EN BIT(9) +#define RKVPSS_ZME_GT_MODE(x) (((x) & 0x3) << 10) +#define RKVPSS_ZME_XGT_EN BIT(12) +#define RKVPSS_ZME_XGT_MODE(x) (((x) & 0x3) << 13) + +/* RATIO_CTRL */ +#define RKVPSS_RATIO_EN BIT(0) + +/* RATIO_UPDATE */ +#define RKVPSS_RATIO_FORCE_UPD BIT(4) +#define RKVPSS_RATIO_GEN_UPD BIT(5) + +/* SCALE_CTRL */ +#define RKVPSS_SCL_HY_EN BIT(0) +#define RKVPSS_SCL_HC_EN BIT(1) +#define RKVPSS_SCL_VY_EN BIT(2) +#define RKVPSS_SCL_VC_EN BIT(3) +#define RKVPSS_SCL_CLIP_EN BIT(13) +#define RKVPSS_SCL_IN_CLIP_EN BIT(14) +#define RKVPSS_SCL_422TO420_EN BIT(30) +#define RKVPSS_SCL_YUV420_REAL_EN BIT(31) + +/* SCALE_UPDATE */ +#define RKVPSS_SCL_FORCE_UPD BIT(4) +#define RKVPSS_SCL_GEN_UPD BIT(5) + +/* MI_WR_CTRL */ +#define RKVPSS_MI_WR_INIT_BASE_EN BIT(4) +#define RKVPSS_MI_WR_UV_SWAP BIT(7) +#define RKVPSS_MI_WR_TILE_SEL(x) (((x) & 0x3) << 8) +#define RKVPSS_MI_WR_STRIDE_CFG_DIS BIT(15) +#define RKVPSS_MI_WR_GROUP_MODE(x) (((x) & 0x3) << 16) +#define RKVPSS_MI_WR_BURST4_LEN_LUM 0 +#define RKVPSS_MI_WR_BURST8_LEN_LUM BIT(20) +#define RKVPSS_MI_WR_BURST16_LEN_LUM BIT(21) +#define RKVPSS_MI_WR_BURST4_LEN_CHROM 0 +#define RKVPSS_MI_WR_BURST8_LEN_CHROM BIT(22) +#define RKVPSS_MI_WR_BURST16_LEN_CHROM BIT(23) +#define RKVPSS_MI_WR_ISP_PRIOR_DIS BIT(24) +#define RKVPSS_MI_WR_GLB_EN_DIS BIT(25) +#define RKVPSS_MI_ID_POLL_CLR BIT(26) +#define RKVPSS_MI_WR_ID_POLL_DIS BIT(27) +#define RKVPSS_MI_WR_GROUP_FIX BIT(28) +#define RKVPSS_MI_WR_ALIGN_DIS BIT(29) +#define RKVPSS_MI_PATH_SEL_FIX_DIS BIT(30) +#define RKVPSS_MI_NEW_WR_BURST_DIS BIT(31) + +/* MI_FORCE_UPDATE */ +#define RKVPSS_MI_FORCE_UPD BIT(0) +#define RKVPSS_MI_UPD_NEW_MODE BIT(1) +#define RKVPSS_MI_CHN0_FORCE_UPD BIT(4) +#define RKVPSS_MI_CHN1_FORCE_UPD BIT(5) +#define RKVPSS_MI_CHN2_FORCE_UPD BIT(6) +#define RKVPSS_MI_CHN3_FORCE_UPD BIT(7) +#define RKVPSS_MI_FORCE_PRO_DIS BIT(31) + +/* MI_WR_WRAP_CTRL */ +#define RKVPSS_MI_CHN_INIT_OFFSET_EN(x) (1 << (x)) +#define RKVPSS_MI_CHN_DYNAMIC_UPD_ADDR(x) (BIT(8) << (x)) +#define RKVPSS_MI_CHN_FRMEND_UPD_DIS(x) (BIT(24) << (x)) + +/* MI_WR_VFLIP_CTRL */ +#define RKVPSS_MI_CHN_V_FLIP(x) (1 << (x)) + +/* MI_HURRY_CTRL */ +#define RKVPSS_NOC_HURRY_VAL(x) ((x) & 0x7) + +/* MI_ARQOS_CTRL */ +#define RKVPSS_AR_QOS_EN BIT(0) +#define RKVPSS_AR_MMU_QOS(x) (((x) & 0x7) << 4) +#define RKVPSS_AR_YUV_QOS2(x) (((x) & 0x7) << 8) +#define RKVPSS_AR_YUV_QOS1(x) (((x) & 0x7) << 12) + +/* MI_AWQOS_CTRL */ +#define RKVPSS_AW_QOS_EN BIT(0) +#define RKVPSS_AW_YUV_QOS2(x) (((x) & 0x7) << 8) +#define RKVPSS_AW_YUV_QOS1(x) (((x) & 0x7) << 12) + +/* MI_IMSC */ +#define RKVPSS_MI_CHN0_FRM_END BIT(0) +#define RKVPSS_MI_CHN1_FRM_END BIT(1) +#define RKVPSS_MI_CHN2_FRM_END BIT(2) +#define RKVPSS_MI_CHN3_FRM_END BIT(3) +#define RKVPSS_MI_RD_FRM_END BIT(4) +#define RKVPSS_MI_WRAP_CHN0_Y BIT(8) +#define RKVPSS_MI_WRAP_CHN0_CB BIT(9) +#define RKVPSS_MI_WRAP_CHN1_Y BIT(10) +#define RKVPSS_MI_WRAP_CHN1_CB BIT(11) +#define RKVPSS_MI_WRAP_CHN2_Y BIT(12) +#define RKVPSS_MI_WRAP_CHN2_CB BIT(13) +#define RKVPSS_MI_WRAP_CHN3_Y BIT(14) +#define RKVPSS_MI_WRAP_CHN3_CB BIT(15) +#define RKVPSS_MI_BUS_ERR BIT(28) +#define RKVPSS_ISP2CHN_FRM_END BIT(30) +#define RKVPSS_MI_ALL_FRM_END BIT(31) + +/* MI_RD_CTRL */ +#define RKVPSS_MI_RD_INPUT_MASK GENMASK(2, 0) +#define RKVPSS_MI_RD_INPUT_420SP 1 +#define RKVPSS_MI_RD_INPUT_422SP 2 +#define RKVPSS_MI_RD_INPUT_BGR565 4 +#define RKVPSS_MI_RD_INPUT_ABGR888 6 +#define RKVPSS_MI_RD_INPUT_BGR888 7 +#define RKVPSS_MI_RD_UV_SWAP BIT(4) +#define RKVPSS_MI_RD_RB_SWAP BIT(5) +#define RKVPSS_MI_RD_ALPHA_SWAP BIT(6) +#define RKVPSS_MI_RD_R2Y_UV_SEL BIT(7) +#define RKVPSS_MI_RD_MODE(x) (((x) & 0x3) << 8) +#define RKVPSS_MI_RD_ROT_0 0 +#define RKVPSS_MI_RD_ROT_90 BIT(10) +#define RKVPSS_MI_RD_ROT_180 BIT(11) +#define RKVPSS_MI_RD_ROT_270 (BIT(10) | BIT(11)) +#define RKVPSS_MI_RD_GROUP_MODE(x) (((x) & 0x3) << 16) +#define RKVPSS_MI_RD_BURST4_LEN 0 +#define RKVPSS_MI_RD_BURST8_LEN BIT(20) +#define RKVPSS_MI_RD_BURST16_LEN (BIT(20) | BIT(21)) +#define RKVPSS_MI_RD_R2Y_BYPASS BIT(28) +#define RKVPSS_MI_RD_OLD BIT(30) +#define RKVPSS_MI_RD_NEW_BURST_DIS BIT(31) + +/* MI_RD_INIT */ +#define RKVPSS_MI_RD_FORCE_UPD BIT(4) + +/* MI_RD_START */ +#define RKVPSS_MI_RD_ST BIT(0) + +/* AXI_CFG_RD_CTRL */ +#define RKVPSS_AXI_CFG_RD_ST BIT(0) +#define RKVPSS_AXI_CFG_RD_ST_MODE BIT(1) +#define RKVPSS_AXI_CFG_RD_CLEAR BIT(4) +#define RKVPSS_AXI_CFG_RD_DIS BIT(7) +#define RKVPSS_AXI_CFG_MIGHT_FAIL BIT(30) +#define RKVPSS_AXI_CFG_RD_DONE BIT(31) + +/* MI_CHN_WR_CTRL */ +#define RKVPSS_MI_CHN_WR_EN BIT(0) +#define RKVPSS_MI_CHN_WR_AUTO_UPD BIT(1) +#define RKVPSS_MI_CHN_WR_RB_SWAP BIT(2) +#define RKVPSS_MI_CHN_WR_42XSP BIT(4) +#define RKVPSS_MI_CHN_WR_422P BIT(5) +#define RKVPSS_MI_CHN_WR_OUTPUT_RGB888 (7 << 8) +#define RKVPSS_MI_CHN_WR_OUTPUT_ARGB888 (6 << 8) +#define RKVPSS_MI_CHN_WR_OUTPUT_RGB565 (4 << 8) +#define RKVPSS_MI_CHN_WR_OUTPUT_YUV422 (2 << 8) +#define RKVPSS_MI_CHN_WR_OUTPUT_YUV420 BIT(8) +#define RKVPSS_MI_CHN_WR_OUTPUT_YUV400 0 +#define RKVPSS_MI_CHN_WORKING BIT(31) + +#define IS_SYNC_REG(x) ({ \ + typeof(x) __x = (x); \ + (__x == RKVPSS_VPSS_CTRL || __x == RKVPSS_VPSS_ONLINE || \ + __x == RKVPSS_VPSS_UPDATE || __x == RKVPSS_MI_WR_INIT || \ + __x == RKVPSS_MI_WR_WRAP_CTRL || __x == RKVPSS_MI_WR_VFLIP_CTRL); \ +}) + +#endif diff --git a/drivers/media/platform/rockchip/vpss/stream.c b/drivers/media/platform/rockchip/vpss/stream.c new file mode 100644 index 000000000000..43b34a7b8ee0 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/stream.c @@ -0,0 +1,1785 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dev.h" +#include "regs.h" + +#define STREAM_OUT_REQ_BUFS_MIN 0 + +static void rkvpss_frame_end(struct rkvpss_stream *stream); +static int rkvpss_stream_crop(struct rkvpss_stream *stream, bool on, bool sync); +static int rkvpss_stream_scale(struct rkvpss_stream *stream, bool on, bool sync); +static void rkvpss_stream_mf(struct rkvpss_stream *stream); + +static const struct capture_fmt scl_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_GREY, + .fmt_type = FMT_YUV, + .bpp = { 8 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .fmt_type = FMT_YUV, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_422P, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + } +}; + +static const struct capture_fmt scl1_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_GREY, + .fmt_type = FMT_YUV, + .bpp = { 8 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .fmt_type = FMT_YUV, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_422P, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, + .fmt_type = FMT_RGB, + .bpp = { 16 }, + .mplanes = 1, + .cplanes = 1, + .wr_fmt = 0, + .swap = RKVPSS_MI_CHN_WR_RB_SWAP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB565, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, + .fmt_type = FMT_RGB, + .bpp = { 24 }, + .mplanes = 1, + .cplanes = 1, + .wr_fmt = 0, + .swap = RKVPSS_MI_CHN_WR_RB_SWAP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB888, + }, { + .fourcc = V4L2_PIX_FMT_XBGR32, + .fmt_type = FMT_RGB, + .bpp = { 32 }, + .mplanes = 1, + .cplanes = 1, + .wr_fmt = 0, + .swap = 0, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_ARGB888, + } +}; + +static struct stream_config scl0_config = { + .fmts = scl_fmts, + .fmt_size = ARRAY_SIZE(scl_fmts), + .frame_end_id = RKVPSS_MI_CHN0_FRM_END, + .crop = { + .ctrl = RKVPSS_CROP1_CTRL, + .update = RKVPSS_CROP1_UPDATE, + .h_offs = RKVPSS_CROP1_0_H_OFFS, + .v_offs = RKVPSS_CROP1_0_V_OFFS, + .h_size = RKVPSS_CROP1_0_H_SIZE, + .v_size = RKVPSS_CROP1_0_V_SIZE, + .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, + .h_offs_shd = RKVPSS_CROP1_0_H_OFFS_SHD, + .v_offs_shd = RKVPSS_CROP1_0_V_OFFS_SHD, + .h_size_shd = RKVPSS_CROP1_0_H_SIZE_SHD, + .v_size_shd = RKVPSS_CROP1_0_V_SIZE_SHD, + }, + .mi = { + .ctrl = RKVPSS_MI_CHN0_WR_CTRL, + .stride = RKVPSS_MI_CHN0_WR_Y_STRIDE, + .y_base = RKVPSS_MI_CHN0_WR_Y_BASE, + .uv_base = RKVPSS_MI_CHN0_WR_CB_BASE, + .y_size = RKVPSS_MI_CHN0_WR_Y_SIZE, + .uv_size = RKVPSS_MI_CHN0_WR_CB_SIZE, + .y_offs_cnt = RKVPSS_MI_CHN0_WR_Y_OFFS_CNT, + .uv_offs_cnt = RKVPSS_MI_CHN0_WR_CB_OFFS_CNT, + .y_pic_width = RKVPSS_MI_CHN0_WR_Y_PIC_WIDTH, + .y_pic_size = RKVPSS_MI_CHN0_WR_Y_PIC_SIZE, + + .ctrl_shd = RKVPSS_MI_CHN0_WR_CTRL_SHD, + .y_shd = RKVPSS_MI_CHN0_WR_Y_BASE_SHD, + .uv_shd = RKVPSS_MI_CHN0_WR_CB_BASE_SHD, + }, +}; + +static struct stream_config scl1_config = { + .fmts = scl1_fmts, + .fmt_size = ARRAY_SIZE(scl1_fmts), + .frame_end_id = RKVPSS_MI_CHN1_FRM_END, + .crop = { + .ctrl = RKVPSS_CROP1_CTRL, + .update = RKVPSS_CROP1_UPDATE, + .h_offs = RKVPSS_CROP1_1_H_OFFS, + .v_offs = RKVPSS_CROP1_1_V_OFFS, + .h_size = RKVPSS_CROP1_1_H_SIZE, + .v_size = RKVPSS_CROP1_1_V_SIZE, + .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, + .h_offs_shd = RKVPSS_CROP1_1_H_OFFS_SHD, + .v_offs_shd = RKVPSS_CROP1_1_V_OFFS_SHD, + .h_size_shd = RKVPSS_CROP1_1_H_SIZE_SHD, + .v_size_shd = RKVPSS_CROP1_1_V_SIZE_SHD, + }, + .scale = { + .ctrl = RKVPSS_SCALE1_CTRL, + .update = RKVPSS_SCALE1_UPDATE, + .src_size = RKVPSS_SCALE1_SRC_SIZE, + .dst_size = RKVPSS_SCALE1_DST_SIZE, + .hy_fac = RKVPSS_SCALE1_HY_FAC, + .hc_fac = RKVPSS_SCALE1_HC_FAC, + .vy_fac = RKVPSS_SCALE1_VY_FAC, + .vc_fac = RKVPSS_SCALE1_VC_FAC, + .hy_offs = RKVPSS_SCALE1_HY_OFFS, + .hc_offs = RKVPSS_SCALE1_HC_OFFS, + .vy_offs = RKVPSS_SCALE1_VY_OFFS, + .vc_offs = RKVPSS_SCALE1_VC_OFFS, + .hy_size = RKVPSS_SCALE1_HY_SIZE, + .hc_size = RKVPSS_SCALE1_HC_SIZE, + .hy_offs_mi = RKVPSS_SCALE1_HY_OFFS_MI, + .hc_offs_mi = RKVPSS_SCALE1_HC_OFFS_MI, + .in_crop_offs = RKVPSS_SCALE1_IN_CROP_OFFSET, + .ctrl_shd = RKVPSS_SCALE1_CTRL_SHD, + .src_size_shd = RKVPSS_SCALE1_SRC_SIZE_SHD, + .dst_size_shd = RKVPSS_SCALE1_DST_SIZE_SHD, + .hy_fac_shd = RKVPSS_SCALE1_HY_FAC_SHD, + .hc_fac_shd = RKVPSS_SCALE1_HC_FAC_SHD, + .vy_fac_shd = RKVPSS_SCALE1_VY_FAC_SHD, + .vc_fac_shd = RKVPSS_SCALE1_VC_FAC_SHD, + .hy_offs_shd = RKVPSS_SCALE1_HY_OFFS_SHD, + .hc_offs_shd = RKVPSS_SCALE1_HC_OFFS_SHD, + .vy_offs_shd = RKVPSS_SCALE1_VY_OFFS_SHD, + .vc_offs_shd = RKVPSS_SCALE1_VC_OFFS_SHD, + .hy_size_shd = RKVPSS_SCALE1_HY_SIZE_SHD, + .hc_size_shd = RKVPSS_SCALE1_HC_SIZE_SHD, + .hy_offs_mi_shd = RKVPSS_SCALE1_HY_OFFS_MI_SHD, + .hc_offs_mi_shd = RKVPSS_SCALE1_HC_OFFS_MI_SHD, + .in_crop_offs_shd = RKVPSS_SCALE1_IN_CROP_OFFSET_SHD, + }, + .mi = { + .ctrl = RKVPSS_MI_CHN1_WR_CTRL, + .stride = RKVPSS_MI_CHN1_WR_Y_STRIDE, + .y_base = RKVPSS_MI_CHN1_WR_Y_BASE, + .uv_base = RKVPSS_MI_CHN1_WR_CB_BASE, + .y_size = RKVPSS_MI_CHN1_WR_Y_SIZE, + .uv_size = RKVPSS_MI_CHN1_WR_CB_SIZE, + .y_offs_cnt = RKVPSS_MI_CHN1_WR_Y_OFFS_CNT, + .uv_offs_cnt = RKVPSS_MI_CHN1_WR_CB_OFFS_CNT, + .y_pic_width = RKVPSS_MI_CHN1_WR_Y_PIC_WIDTH, + .y_pic_size = RKVPSS_MI_CHN1_WR_Y_PIC_SIZE, + + .ctrl_shd = RKVPSS_MI_CHN1_WR_CTRL_SHD, + .y_shd = RKVPSS_MI_CHN1_WR_Y_BASE_SHD, + .uv_shd = RKVPSS_MI_CHN1_WR_CB_BASE_SHD, + }, +}; + +static struct stream_config scl2_config = { + .fmts = scl_fmts, + .fmt_size = ARRAY_SIZE(scl_fmts), + .frame_end_id = RKVPSS_MI_CHN2_FRM_END, + .crop = { + .ctrl = RKVPSS_CROP1_CTRL, + .update = RKVPSS_CROP1_UPDATE, + .h_offs = RKVPSS_CROP1_2_H_OFFS, + .v_offs = RKVPSS_CROP1_2_V_OFFS, + .h_size = RKVPSS_CROP1_2_H_SIZE, + .v_size = RKVPSS_CROP1_2_V_SIZE, + .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, + .h_offs_shd = RKVPSS_CROP1_2_H_OFFS_SHD, + .v_offs_shd = RKVPSS_CROP1_2_V_OFFS_SHD, + .h_size_shd = RKVPSS_CROP1_2_H_SIZE_SHD, + .v_size_shd = RKVPSS_CROP1_2_V_SIZE_SHD, + }, + .scale = { + .ctrl = RKVPSS_SCALE1_CTRL, + .update = RKVPSS_SCALE1_UPDATE, + .src_size = RKVPSS_SCALE1_SRC_SIZE, + .dst_size = RKVPSS_SCALE1_DST_SIZE, + .hy_fac = RKVPSS_SCALE1_HY_FAC, + .hc_fac = RKVPSS_SCALE1_HC_FAC, + .vy_fac = RKVPSS_SCALE1_VY_FAC, + .vc_fac = RKVPSS_SCALE1_VC_FAC, + .hy_offs = RKVPSS_SCALE1_HY_OFFS, + .hc_offs = RKVPSS_SCALE1_HC_OFFS, + .vy_offs = RKVPSS_SCALE1_VY_OFFS, + .vc_offs = RKVPSS_SCALE1_VC_OFFS, + .hy_size = RKVPSS_SCALE1_HY_SIZE, + .hc_size = RKVPSS_SCALE1_HC_SIZE, + .hy_offs_mi = RKVPSS_SCALE1_HY_OFFS_MI, + .hc_offs_mi = RKVPSS_SCALE1_HC_OFFS_MI, + .in_crop_offs = RKVPSS_SCALE1_IN_CROP_OFFSET, + .ctrl_shd = RKVPSS_SCALE1_CTRL_SHD, + .src_size_shd = RKVPSS_SCALE1_SRC_SIZE_SHD, + .dst_size_shd = RKVPSS_SCALE1_DST_SIZE_SHD, + .hy_fac_shd = RKVPSS_SCALE1_HY_FAC_SHD, + .hc_fac_shd = RKVPSS_SCALE1_HC_FAC_SHD, + .vy_fac_shd = RKVPSS_SCALE1_VY_FAC_SHD, + .vc_fac_shd = RKVPSS_SCALE1_VC_FAC_SHD, + .hy_offs_shd = RKVPSS_SCALE1_HY_OFFS_SHD, + .hc_offs_shd = RKVPSS_SCALE1_HC_OFFS_SHD, + .vy_offs_shd = RKVPSS_SCALE1_VY_OFFS_SHD, + .vc_offs_shd = RKVPSS_SCALE1_VC_OFFS_SHD, + .hy_size_shd = RKVPSS_SCALE1_HY_SIZE_SHD, + .hc_size_shd = RKVPSS_SCALE1_HC_SIZE_SHD, + .hy_offs_mi_shd = RKVPSS_SCALE1_HY_OFFS_MI_SHD, + .hc_offs_mi_shd = RKVPSS_SCALE1_HC_OFFS_MI_SHD, + .in_crop_offs_shd = RKVPSS_SCALE1_IN_CROP_OFFSET_SHD, + }, + .mi = { + .ctrl = RKVPSS_MI_CHN2_WR_CTRL, + .stride = RKVPSS_MI_CHN2_WR_Y_STRIDE, + .y_base = RKVPSS_MI_CHN2_WR_Y_BASE, + .uv_base = RKVPSS_MI_CHN2_WR_CB_BASE, + .y_size = RKVPSS_MI_CHN2_WR_Y_SIZE, + .uv_size = RKVPSS_MI_CHN2_WR_CB_SIZE, + .y_offs_cnt = RKVPSS_MI_CHN2_WR_Y_OFFS_CNT, + .uv_offs_cnt = RKVPSS_MI_CHN2_WR_CB_OFFS_CNT, + .y_pic_width = RKVPSS_MI_CHN2_WR_Y_PIC_WIDTH, + .y_pic_size = RKVPSS_MI_CHN2_WR_Y_PIC_SIZE, + + .ctrl_shd = RKVPSS_MI_CHN2_WR_CTRL_SHD, + .y_shd = RKVPSS_MI_CHN2_WR_Y_BASE_SHD, + .uv_shd = RKVPSS_MI_CHN2_WR_CB_BASE_SHD, + }, +}; + +static struct stream_config scl3_config = { + .fmts = scl_fmts, + .fmt_size = ARRAY_SIZE(scl_fmts), + .frame_end_id = RKVPSS_MI_CHN3_FRM_END, + .crop = { + .ctrl = RKVPSS_CROP1_CTRL, + .update = RKVPSS_CROP1_UPDATE, + .h_offs = RKVPSS_CROP1_3_H_OFFS, + .v_offs = RKVPSS_CROP1_3_V_OFFS, + .h_size = RKVPSS_CROP1_3_H_SIZE, + .v_size = RKVPSS_CROP1_3_V_SIZE, + .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, + .h_offs_shd = RKVPSS_CROP1_3_H_OFFS_SHD, + .v_offs_shd = RKVPSS_CROP1_3_V_OFFS_SHD, + .h_size_shd = RKVPSS_CROP1_3_H_SIZE_SHD, + .v_size_shd = RKVPSS_CROP1_3_V_SIZE_SHD, + }, + .scale = { + .ctrl = RKVPSS_SCALE3_CTRL, + .update = RKVPSS_SCALE3_UPDATE, + .src_size = RKVPSS_SCALE3_SRC_SIZE, + .dst_size = RKVPSS_SCALE3_DST_SIZE, + .hy_fac = RKVPSS_SCALE3_HY_FAC, + .hc_fac = RKVPSS_SCALE3_HC_FAC, + .vy_fac = RKVPSS_SCALE3_VY_FAC, + .vc_fac = RKVPSS_SCALE3_VC_FAC, + .hy_offs = RKVPSS_SCALE3_HY_OFFS, + .hc_offs = RKVPSS_SCALE3_HC_OFFS, + .vy_offs = RKVPSS_SCALE3_VY_OFFS, + .vc_offs = RKVPSS_SCALE3_VC_OFFS, + .hy_size = RKVPSS_SCALE3_HY_SIZE, + .hc_size = RKVPSS_SCALE3_HC_SIZE, + .hy_offs_mi = RKVPSS_SCALE3_HY_OFFS_MI, + .hc_offs_mi = RKVPSS_SCALE3_HC_OFFS_MI, + .in_crop_offs = RKVPSS_SCALE3_IN_CROP_OFFSET, + .ctrl_shd = RKVPSS_SCALE3_CTRL_SHD, + .src_size_shd = RKVPSS_SCALE3_SRC_SIZE_SHD, + .dst_size_shd = RKVPSS_SCALE3_DST_SIZE_SHD, + .hy_fac_shd = RKVPSS_SCALE3_HY_FAC_SHD, + .hc_fac_shd = RKVPSS_SCALE3_HC_FAC_SHD, + .vy_fac_shd = RKVPSS_SCALE3_VY_FAC_SHD, + .vc_fac_shd = RKVPSS_SCALE3_VC_FAC_SHD, + .hy_offs_shd = RKVPSS_SCALE3_HY_OFFS_SHD, + .hc_offs_shd = RKVPSS_SCALE3_HC_OFFS_SHD, + .vy_offs_shd = RKVPSS_SCALE3_VY_OFFS_SHD, + .vc_offs_shd = RKVPSS_SCALE3_VC_OFFS_SHD, + .hy_size_shd = RKVPSS_SCALE3_HY_SIZE_SHD, + .hc_size_shd = RKVPSS_SCALE3_HC_SIZE_SHD, + .hy_offs_mi_shd = RKVPSS_SCALE3_HY_OFFS_MI_SHD, + .hc_offs_mi_shd = RKVPSS_SCALE3_HC_OFFS_MI_SHD, + .in_crop_offs_shd = RKVPSS_SCALE3_IN_CROP_OFFSET_SHD, + }, + .mi = { + .ctrl = RKVPSS_MI_CHN3_WR_CTRL, + .stride = RKVPSS_MI_CHN3_WR_Y_STRIDE, + .y_base = RKVPSS_MI_CHN3_WR_Y_BASE, + .uv_base = RKVPSS_MI_CHN3_WR_CB_BASE, + .y_size = RKVPSS_MI_CHN3_WR_Y_SIZE, + .uv_size = RKVPSS_MI_CHN3_WR_CB_SIZE, + .y_offs_cnt = RKVPSS_MI_CHN3_WR_Y_OFFS_CNT, + .uv_offs_cnt = RKVPSS_MI_CHN3_WR_CB_OFFS_CNT, + .y_pic_width = RKVPSS_MI_CHN3_WR_Y_PIC_WIDTH, + .y_pic_size = RKVPSS_MI_CHN3_WR_Y_PIC_SIZE, + + .ctrl_shd = RKVPSS_MI_CHN3_WR_CTRL_SHD, + .y_shd = RKVPSS_MI_CHN3_WR_Y_BASE_SHD, + .uv_shd = RKVPSS_MI_CHN3_WR_CB_BASE_SHD, + }, +}; + +static void stream_frame_start(struct rkvpss_stream *stream, u32 irq) +{ + if (stream->is_crop_upd) { + rkvpss_stream_scale(stream, true, !irq); + rkvpss_stream_crop(stream, true, !irq); + } + if (!irq && !stream->curr_buf && + !stream->dev->hw_dev->is_single) + stream->ops->update_mi(stream); +} + +static void scl_force_update(struct rkvpss_stream *stream) +{ + u32 val; + + switch (stream->id) { + case RKVPSS_OUTPUT_CH0: + val = RKVPSS_MI_CHN0_FORCE_UPD; + break; + case RKVPSS_OUTPUT_CH1: + val = RKVPSS_MI_CHN1_FORCE_UPD; + break; + case RKVPSS_OUTPUT_CH2: + val = RKVPSS_MI_CHN2_FORCE_UPD; + break; + case RKVPSS_OUTPUT_CH3: + val = RKVPSS_MI_CHN3_FORCE_UPD; + break; + default: + return; + } + /* need update two for online2 mode */ + rkvpss_write(stream->dev, RKVPSS_MI_WR_INIT, val); + rkvpss_write(stream->dev, RKVPSS_MI_WR_INIT, val); +} + +static void scl_update_mi(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + struct rkvpss_buffer *buf = NULL; + unsigned long lock_flags = 0; + u32 val; + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + if (!list_empty(&stream->buf_queue) && !stream->curr_buf) { + buf = list_first_entry(&stream->buf_queue, struct rkvpss_buffer, queue); + list_del(&buf->queue); + stream->curr_buf = buf; + } + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + + if (buf) { + val = buf->dma[0]; + rkvpss_write(dev, stream->config->mi.y_base, val); + val = buf->dma[1]; + rkvpss_write(dev, stream->config->mi.uv_base, val); + scl_force_update(stream); + if (stream->is_pause) { + stream->is_pause = false; + stream->ops->enable_mi(stream); + } + } else if (!stream->is_pause) { + stream->is_pause = true; + stream->ops->disable_mi(stream); + } + v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev, + "%s id:%d Y:0x%x UV:0x%x | Y_SHD:0x%x\n", + __func__, stream->id, + rkvpss_read(dev, stream->config->mi.y_base), + rkvpss_read(dev, stream->config->mi.uv_base), + rkvpss_hw_read(dev->hw_dev, stream->config->mi.y_shd)); +} + +static void scl_config_mi(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + struct capture_fmt *fmt = &stream->out_cap_fmt; + struct v4l2_pix_format_mplane *out_fmt = &stream->out_fmt; + u32 reg, val; + + val = out_fmt->plane_fmt[0].bytesperline; + reg = stream->config->mi.stride; + rkvpss_write(dev, reg, val); + + val *= out_fmt->height; + reg = stream->config->mi.y_pic_size; + rkvpss_write(dev, reg, val); + + val = out_fmt->plane_fmt[0].bytesperline * out_fmt->height; + reg = stream->config->mi.y_size; + rkvpss_write(dev, reg, val); + + val = out_fmt->plane_fmt[1].sizeimage; + reg = stream->config->mi.uv_size; + rkvpss_write(dev, reg, val); + + reg = stream->config->mi.y_offs_cnt; + rkvpss_write(dev, reg, 0); + reg = stream->config->mi.uv_offs_cnt; + rkvpss_write(dev, reg, 0); + + val = fmt->wr_fmt | fmt->output_fmt | fmt->swap | + RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD; + reg = stream->config->mi.ctrl; + rkvpss_write(dev, reg, val); + + stream->is_mf_upd = true; + rkvpss_frame_end(stream); +} + +static void scl_enable_mi(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + u32 val, mask; + + switch (stream->id) { + case RKVPSS_OUTPUT_CH0: + val = RKVPSS_ISP2VPSS_CHN0_SEL(2); + mask = RKVPSS_ISP2VPSS_CHN0_SEL(3); + break; + case RKVPSS_OUTPUT_CH1: + val = RKVPSS_ISP2VPSS_CHN1_SEL(2); + mask = RKVPSS_ISP2VPSS_CHN1_SEL(3); + break; + case RKVPSS_OUTPUT_CH2: + val = RKVPSS_ISP2VPSS_CHN2_SEL(2); + mask = RKVPSS_ISP2VPSS_CHN2_SEL(3); + break; + case RKVPSS_OUTPUT_CH3: + val = RKVPSS_ISP2VPSS_CHN3_SEL(2); + mask = RKVPSS_ISP2VPSS_CHN3_SEL(3); + break; + default: + return; + } + val |= RKVPSS_ISP2VPSS_ONLINE2; + if (!dev->hw_dev->is_ofl_cmsc) + val |= RKVPSS_ISP2VPSS_ONLINE2_CMSC_EN; + rkvpss_set_bits(dev, RKVPSS_VPSS_ONLINE, mask, val); + rkvpss_write(dev, RKVPSS_VPSS_UPDATE, RKVPSS_ONLINE2_CHN_FORCE_UPD); +} + +static void scl_disable_mi(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + u32 val; + + switch (stream->id) { + case RKVPSS_OUTPUT_CH0: + val = RKVPSS_ISP2VPSS_CHN0_SEL(3); + break; + case RKVPSS_OUTPUT_CH1: + val = RKVPSS_ISP2VPSS_CHN1_SEL(3); + break; + case RKVPSS_OUTPUT_CH2: + val = RKVPSS_ISP2VPSS_CHN2_SEL(3); + break; + case RKVPSS_OUTPUT_CH3: + val = RKVPSS_ISP2VPSS_CHN3_SEL(3); + break; + default: + return; + } + + rkvpss_clear_bits(dev, RKVPSS_VPSS_ONLINE, val); + rkvpss_write(dev, RKVPSS_VPSS_UPDATE, RKVPSS_ONLINE2_CHN_FORCE_UPD); +} + +static struct streams_ops scl_stream_ops = { + .config_mi = scl_config_mi, + .enable_mi = scl_enable_mi, + .disable_mi = scl_disable_mi, + .update_mi = scl_update_mi, + .frame_start = stream_frame_start, +}; + +static void rkvpss_buf_done_task(unsigned long arg) +{ + struct rkvpss_stream *stream = (struct rkvpss_stream *)arg; + struct rkvpss_vdev_node *node = &stream->vnode; + struct rkvpss_buffer *buf = NULL; + unsigned long lock_flags = 0; + LIST_HEAD(local_list); + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + list_replace_init(&stream->buf_done_list, &local_list); + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + + while (!list_empty(&local_list)) { + buf = list_first_entry(&local_list, struct rkvpss_buffer, queue); + list_del(&buf->queue); + v4l2_dbg(2, rkvpss_debug, &stream->dev->v4l2_dev, + "%s id:%d seq:%d buf:0x%x done\n", + node->vdev.name, stream->id, buf->vb.sequence, buf->dma[0]); + vb2_buffer_done(&buf->vb.vb2_buf, + stream->streaming ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR); + } +} + +static void rkvpss_stream_buf_done(struct rkvpss_stream *stream, + struct rkvpss_buffer *buf) +{ + unsigned long lock_flags = 0; + + if (!stream || !buf) + return; + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + list_add_tail(&buf->queue, &stream->buf_done_list); + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + tasklet_schedule(&stream->buf_done_tasklet); +} + +static void rkvpss_frame_end(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + struct rkvpss_subdev *sdev = &dev->vpss_sdev; + struct rkvpss_buffer *buf = NULL; + unsigned long lock_flags = 0; + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + if (stream->curr_buf) { + buf = stream->curr_buf; + stream->curr_buf = NULL; + } + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + + if (buf) { + struct vb2_buffer *vb2_buf = &buf->vb.vb2_buf; + struct capture_fmt *fmt = &stream->out_cap_fmt; + u64 ns = sdev->frame_timestamp; + int i; + + for (i = 0; i < fmt->mplanes; i++) + vb2_set_plane_payload(vb2_buf, i, stream->out_fmt.plane_fmt[i].sizeimage); + if (!ns) + ns = ktime_get_ns(); + buf->vb.vb2_buf.timestamp = ns; + buf->vb.sequence = sdev->frame_seq; + rkvpss_stream_buf_done(stream, buf); + } + + rkvpss_stream_mf(stream); + stream->ops->update_mi(stream); +} + +static int rkvpss_queue_setup(struct vb2_queue *queue, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_ctxs[]) +{ + struct rkvpss_stream *stream = queue->drv_priv; + struct rkvpss_device *dev = stream->dev; + const struct v4l2_pix_format_mplane *pixm = NULL; + const struct capture_fmt *cap_fmt = NULL; + u32 i; + + pixm = &stream->out_fmt; + if (!pixm->width || !pixm->height) + return -EINVAL; + cap_fmt = &stream->out_cap_fmt; + *num_planes = cap_fmt->mplanes; + + for (i = 0; i < cap_fmt->mplanes; i++) { + const struct v4l2_plane_pix_format *plane_fmt; + + plane_fmt = &pixm->plane_fmt[i]; + sizes[i] = plane_fmt->sizeimage / pixm->height * ALIGN(pixm->height, 16); + } + + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s stream:%d count %d, size %d\n", + v4l2_type_names[queue->type], + stream->id, *num_buffers, sizes[0]); + + return 0; +} + +static void rkvpss_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkvpss_buffer *vpssbuf = to_rkvpss_buffer(vbuf); + struct vb2_queue *queue = vb->vb2_queue; + struct rkvpss_stream *stream = queue->drv_priv; + struct rkvpss_device *dev = stream->dev; + struct v4l2_pix_format_mplane *pixm = &stream->out_fmt; + struct capture_fmt *cap_fmt = &stream->out_cap_fmt; + unsigned long lock_flags = 0; + struct sg_table *sgt; + u32 height, size; + int i; + + for (i = 0; i < cap_fmt->mplanes; i++) { + sgt = vb2_dma_sg_plane_desc(vb, i); + vpssbuf->dma[i] = sg_dma_address(sgt->sgl); + } + /* + * NOTE: plane_fmt[0].sizeimage is total size of all planes for single + * memory plane formats, so calculate the size explicitly. + */ + if (cap_fmt->mplanes == 1) { + for (i = 0; i < cap_fmt->cplanes - 1; i++) { + height = pixm->height; + size = (i == 0) ? + pixm->plane_fmt[i].bytesperline * height : + pixm->plane_fmt[i].sizeimage; + vpssbuf->dma[i + 1] = vpssbuf->dma[i] + size; + } + } + + v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev, + "%s stream:%d buf:0x%x\n", __func__, + stream->id, vpssbuf->dma[0]); + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + list_add_tail(&vpssbuf->queue, &stream->buf_queue); + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + if (dev->hw_dev->is_single && + stream->streaming && !stream->curr_buf) + stream->ops->update_mi(stream); +} + +static void destroy_buf_queue(struct rkvpss_stream *stream, + enum vb2_buffer_state state) +{ + unsigned long lock_flags = 0; + struct rkvpss_buffer *buf; + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + if (stream->curr_buf) { + list_add_tail(&stream->curr_buf->queue, &stream->buf_queue); + stream->curr_buf = NULL; + } + while (!list_empty(&stream->buf_queue)) { + buf = list_first_entry(&stream->buf_queue, struct rkvpss_buffer, queue); + list_del(&buf->queue); + buf->vb.vb2_buf.synced = false; + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); +} + +static int rkvpss_stream_crop(struct rkvpss_stream *stream, bool on, bool sync) +{ + struct rkvpss_device *dev = stream->dev; + struct v4l2_rect *crop = &stream->crop; + u32 reg_ctrl = stream->config->crop.ctrl; + u32 reg_upd = stream->config->crop.update; + u32 reg_h_offs = stream->config->crop.h_offs; + u32 reg_v_offs = stream->config->crop.v_offs; + u32 reg_h_size = stream->config->crop.h_size; + u32 reg_v_size = stream->config->crop.v_size; + u32 val; + + val = RKVPSS_CROP_CHN_EN(stream->id); + if (on) { + rkvpss_set_bits(dev, reg_ctrl, 0, val); + rkvpss_write(dev, reg_h_offs, crop->left); + rkvpss_write(dev, reg_v_offs, crop->top); + rkvpss_write(dev, reg_h_size, crop->width); + rkvpss_write(dev, reg_v_size, crop->height); + } else { + rkvpss_clear_bits(dev, reg_ctrl, val); + } + if (sync) { + val = RKVPSS_CROP_CHN_FORCE_UPD(stream->id); + val |= RKVPSS_CROP_GEN_UPD; + rkvpss_write(dev, reg_upd, val); + } + stream->is_crop_upd = false; + return 0; +} + +static void poly_phase_scale(struct rkvpss_stream *stream, bool on, bool sync) +{ + struct rkvpss_device *dev = stream->dev; + u32 out_w = stream->out_fmt.width; + u32 out_h = stream->out_fmt.height; + u32 in_w = stream->crop.width; + u32 in_h = stream->crop.height; + u32 ctrl, y_xscl_fac, y_yscl_fac, uv_xscl_fac, uv_yscl_fac; + u32 i, j, idx, ratio, val, in_div, out_div, factor; + bool dering_en = false, yuv420_in = false, yuv422_to_420 = false; + + if (!on) { + rkvpss_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0); + rkvpss_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0); + return; + } + + /* TODO diff for input and output format */ + if (yuv420_in) { + in_div = 2; + out_div = 2; + } else if (yuv422_to_420) { + in_div = 1; + out_div = 2; + } else { + in_div = 1; + out_div = 1; + } + + val = (in_w - 1) | ((in_h - 1) << 16); + rkvpss_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val); + rkvpss_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val); + val = (out_w - 1) | ((out_h - 1) << 16); + rkvpss_write(dev, RKVPSS_ZME_Y_DST_SIZE, val); + rkvpss_write(dev, RKVPSS_ZME_UV_DST_SIZE, val); + + ctrl = RKVPSS_ZME_XSCL_MODE | RKVPSS_ZME_YSCL_MODE; + if (dering_en) { + ctrl |= RKVPSS_ZME_DERING_EN; + rkvpss_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410); + rkvpss_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410); + } + if (in_w != out_w) { + if (in_w > out_w) { + factor = 4096; + ctrl |= RKVPSS_ZME_XSD_EN; + } else { + factor = 65536; + ctrl |= RKVPSS_ZME_XSU_EN; + } + y_xscl_fac = (in_w - 1) * factor / (out_w - 1); + uv_xscl_fac = (in_w / 2 - 1) * factor / (out_w / 2 - 1); + + ratio = y_xscl_fac * 10000 / factor; + idx = rkvpss_get_zme_tap_coe_index(ratio); + for (i = 0; i < 17; i++) { + for (j = 0; j < 8; j += 2) { + val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap8_coe[idx][i][j], + rkvpss_zme_tap8_coe[idx][i][j + 1]); + rkvpss_write(dev, RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, val); + rkvpss_write(dev, RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, val); + } + } + } else { + y_xscl_fac = 0; + uv_xscl_fac = 0; + } + rkvpss_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac); + rkvpss_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac); + if (in_h != out_h) { + if (in_h > out_h) { + factor = 4096; + ctrl |= RKVPSS_ZME_YSD_EN; + } else { + factor = 65536; + ctrl |= RKVPSS_ZME_YSU_EN; + } + y_yscl_fac = (in_h - 1) * factor / (out_h - 1); + uv_yscl_fac = (in_h / in_div - 1) * factor / (out_h / out_div - 1); + + ratio = y_yscl_fac * 10000 / factor; + idx = rkvpss_get_zme_tap_coe_index(ratio); + for (i = 0; i < 17; i++) { + for (j = 0; j < 8; j += 2) { + val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap6_coe[idx][i][j], + rkvpss_zme_tap6_coe[idx][i][j + 1]); + rkvpss_write(dev, RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, val); + rkvpss_write(dev, RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, val); + } + } + } else { + y_yscl_fac = 0; + uv_yscl_fac = 0; + } + rkvpss_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac); + rkvpss_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac); + + rkvpss_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl); + rkvpss_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl); + + ctrl = RKVPSS_ZME_GATING_EN; + if (yuv420_in) + ctrl |= RKVPSS_ZME_SCL_YUV420_REAL_EN; + if (yuv422_to_420) + ctrl |= RKVPSS_ZME_422TO420_EN; + rkvpss_write(dev, RKVPSS_ZME_CTRL, ctrl); + + val = RKVPSS_ZME_GEN_UPD; + if (sync) + val |= RKVPSS_ZME_FORCE_UPD; + rkvpss_write(dev, RKVPSS_ZME_UPDATE, val); +} + +static void bilinear_scale(struct rkvpss_stream *stream, bool on, bool sync) +{ + struct rkvpss_device *dev = stream->dev; + u32 out_w = stream->out_fmt.width; + u32 out_h = stream->out_fmt.height; + u32 in_w = stream->crop.width; + u32 in_h = stream->crop.height; + u32 in_div, out_div; + u32 reg, val, ctrl = 0; + bool yuv420_in = false, yuv422_to_420 = false; + + if (!on) { + reg = stream->config->scale.ctrl; + rkvpss_write(dev, reg, 0); + return; + } + + /* TODO diff for input and output format */ + if (yuv420_in) { + in_div = 2; + out_div = 2; + } else if (yuv422_to_420) { + in_div = 1; + out_div = 2; + } else { + in_div = 1; + out_div = 1; + } + + reg = stream->config->scale.hy_offs; + rkvpss_write(dev, reg, 0); + reg = stream->config->scale.hc_offs; + rkvpss_write(dev, reg, 0); + reg = stream->config->scale.vy_offs; + rkvpss_write(dev, reg, 0); + reg = stream->config->scale.vc_offs; + rkvpss_write(dev, reg, 0); + + val = in_w | (in_h << 16); + reg = stream->config->scale.src_size; + rkvpss_write(dev, reg, val); + val = out_w | (out_h << 16); + reg = stream->config->scale.dst_size; + rkvpss_write(dev, reg, val); + + if (in_w != out_w) { + val = (in_w - 1) * 4096 / (out_w - 1); + reg = stream->config->scale.hy_fac; + rkvpss_write(dev, reg, val); + + val = (in_w / 2 - 1) * 4096 / (out_w / 2 - 1); + reg = stream->config->scale.hc_fac; + rkvpss_write(dev, reg, val); + + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; + } + if (in_h != out_h) { + val = (in_h - 1) * 4096 / (out_h - 1); + reg = stream->config->scale.vy_fac; + rkvpss_write(dev, reg, val); + + val = (in_h / in_div - 1) * 4096 / (out_h / out_div - 1); + reg = stream->config->scale.vc_fac; + rkvpss_write(dev, reg, val); + + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; + } + reg = stream->config->scale.ctrl; + rkvpss_write(dev, reg, ctrl); + + val = RKVPSS_SCL_GEN_UPD; + if (sync) + val |= RKVPSS_SCL_FORCE_UPD; + reg = stream->config->scale.update; + rkvpss_write(dev, reg, val); +} + +static int rkvpss_stream_scale(struct rkvpss_stream *stream, bool on, bool sync) +{ + if (stream->id == RKVPSS_OUTPUT_CH0) + poly_phase_scale(stream, on, sync); + else + bilinear_scale(stream, on, sync); + return 0; +} + +static void rkvpss_stream_stop(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + int ret; + + stream->stopping = true; + ret = wait_event_timeout(stream->done, !stream->streaming, + msecs_to_jiffies(300)); + if (!ret) + v4l2_warn(&dev->v4l2_dev, "%s id:%d timeout\n", __func__, stream->id); + stream->stopping = false; + stream->streaming = false; + if (stream->ops->disable_mi) + stream->ops->disable_mi(stream); + rkvpss_stream_crop(stream, false, true); + rkvpss_stream_scale(stream, false, true); +} + +static void rkvpss_stop_streaming(struct vb2_queue *queue) +{ + struct rkvpss_stream *stream = queue->drv_priv; + struct rkvpss_vdev_node *node = &stream->vnode; + struct rkvpss_device *dev = stream->dev; + struct rkvpss_hw_dev *hw = dev->hw_dev; + + if (!stream->streaming) + return; + + mutex_lock(&hw->dev_lock); + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s %s id:%d enter\n", __func__, + node->vdev.name, stream->id); + rkvpss_stream_stop(stream); + rkvpss_pipeline_stream(dev, false); + destroy_buf_queue(stream, VB2_BUF_STATE_ERROR); + rkvpss_pipeline_close(dev); + tasklet_disable(&stream->buf_done_tasklet); + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s %s id:%d exit\n", __func__, + node->vdev.name, stream->id); + mutex_unlock(&hw->dev_lock); +} + +static int rkvpss_stream_start(struct rkvpss_stream *stream) +{ + int ret = 0; + + stream->is_crop_upd = true; + ret = rkvpss_stream_scale(stream, true, true); + if (ret < 0) + return ret; + ret = rkvpss_stream_crop(stream, true, true); + if (ret < 0) + return ret; + + if (stream->ops->config_mi) + stream->ops->config_mi(stream); + + stream->streaming = true; + return ret; +} + +static int rkvpss_start_streaming(struct vb2_queue *queue, unsigned int count) +{ + struct rkvpss_stream *stream = queue->drv_priv; + struct rkvpss_vdev_node *node = &stream->vnode; + struct rkvpss_device *dev = stream->dev; + struct rkvpss_hw_dev *hw = dev->hw_dev; + int ret = -EINVAL; + + if (stream->streaming) + return -EBUSY; + + mutex_lock(&hw->dev_lock); + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s %s id:%d enter\n", __func__, + node->vdev.name, stream->id); + + stream->is_pause = true; + if (!dev->inp || !stream->linked) { + v4l2_err(&dev->v4l2_dev, "no link or invalid input source\n"); + goto free_buf_queue; + } + + rkvpss_pipeline_open(dev); + + ret = rkvpss_stream_start(stream); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "start %s failed\n", node->vdev.name); + goto pipe_close; + } + + ret = rkvpss_pipeline_stream(dev, true); + if (ret < 0) + goto stop_stream; + + tasklet_enable(&stream->buf_done_tasklet); + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s %s id:%d exit\n", __func__, + node->vdev.name, stream->id); + mutex_unlock(&hw->dev_lock); + return 0; +stop_stream: + stream->streaming = false; + rkvpss_stream_stop(stream); +pipe_close: + rkvpss_pipeline_close(dev); +free_buf_queue: + destroy_buf_queue(stream, VB2_BUF_STATE_QUEUED); + mutex_unlock(&hw->dev_lock); + return ret; +} + +static const struct vb2_ops stream_vb2_ops = { + .queue_setup = rkvpss_queue_setup, + .buf_queue = rkvpss_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .stop_streaming = rkvpss_stop_streaming, + .start_streaming = rkvpss_start_streaming, +}; + +static int rkvpss_init_vb2_queue(struct vb2_queue *q, + struct rkvpss_stream *stream, + enum v4l2_buf_type buf_type) +{ + q->type = buf_type; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR; + q->drv_priv = stream; + q->ops = &stream_vb2_ops; + q->mem_ops = stream->dev->hw_dev->mem_ops; + q->buf_struct_size = sizeof(struct rkvpss_buffer); + q->min_buffers_needed = STREAM_OUT_REQ_BUFS_MIN; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + + q->lock = &stream->dev->apilock; + q->dev = stream->dev->hw_dev->dev; + q->allow_cache_hints = 1; + q->bidirectional = 1; + if (stream->dev->hw_dev->is_dma_contig) + q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS; + q->gfp_flags = GFP_DMA32; + return vb2_queue_init(q); +} + +static const +struct capture_fmt *find_fmt(struct rkvpss_stream *stream, u32 pixelfmt) +{ + const struct capture_fmt *fmt; + u32 i; + + for (i = 0; i < stream->config->fmt_size; i++) { + fmt = &stream->config->fmts[i]; + if (fmt->fourcc == pixelfmt) + return fmt; + } + return NULL; +} + +static int fcc_xysubs(u32 fcc, u32 *xsubs, u32 *ysubs) +{ + switch (fcc) { + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_YUV444M: + *xsubs = 1; + *ysubs = 1; + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_YVU422M: + case V4L2_PIX_FMT_FBC2: + *xsubs = 2; + *ysubs = 1; + break; + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21M: + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_FBC0: + *xsubs = 2; + *ysubs = 2; + break; + default: + return -EINVAL; + } + return 0; +} + +static int rkvpss_set_fmt(struct rkvpss_stream *stream, + struct v4l2_pix_format_mplane *pixm, + bool try) +{ + struct rkvpss_device *dev = stream->dev; + u32 i, planes, xsubs = 1, ysubs = 1, imagsize = 0; + const struct capture_fmt *fmt; + + fmt = find_fmt(stream, pixm->pixelformat); + if (!fmt) { + v4l2_err(&dev->v4l2_dev, + "nonsupport pixelformat:%c%c%c%c\n", + pixm->pixelformat, + pixm->pixelformat >> 8, + pixm->pixelformat >> 16, + pixm->pixelformat >> 24); + return -EINVAL; + } + + pixm->num_planes = fmt->mplanes; + pixm->field = V4L2_FIELD_NONE; + if (!pixm->quantization) + pixm->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fcc_xysubs(fmt->fourcc, &xsubs, &ysubs); + planes = fmt->cplanes ? fmt->cplanes : fmt->mplanes; + for (i = 0; i < planes; i++) { + struct v4l2_plane_pix_format *plane_fmt; + u32 width, height, bytesperline, w, h; + + plane_fmt = pixm->plane_fmt + i; + w = pixm->width; + h = pixm->height; + width = i ? w / xsubs : w; + height = i ? h / ysubs : h; + bytesperline = width * DIV_ROUND_UP(fmt->bpp[i], 8); + if (i != 0 || plane_fmt->bytesperline < bytesperline) + plane_fmt->bytesperline = bytesperline; + plane_fmt->sizeimage = plane_fmt->bytesperline * height; + imagsize += plane_fmt->sizeimage; + } + if (fmt->mplanes == 1) + pixm->plane_fmt[0].sizeimage = imagsize; + if (!try) { + stream->out_cap_fmt = *fmt; + stream->out_fmt = *pixm; + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s: stream:%d req(%d, %d) out(%d, %d)\n", + __func__, stream->id, pixm->width, pixm->height, + stream->out_fmt.width, stream->out_fmt.height); + } + return 0; +} + +/************************* v4l2_file_operations***************************/ + +static int rkvpss_fh_open(struct file *file) +{ + struct rkvpss_stream *stream = video_drvdata(file); + struct rkvpss_device *dev = stream->dev; + int ret; + + if (!dev->is_probe_end) + return -EINVAL; + + ret = v4l2_fh_open(file); + if (!ret) { + ret = v4l2_pipeline_pm_get(&stream->vnode.vdev.entity); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, + "pipeline power on failed %d\n", ret); + vb2_fop_release(file); + } + } + return ret; +} + +static int rkvpss_fh_release(struct file *file) +{ + struct rkvpss_stream *stream = video_drvdata(file); + int ret; + + ret = vb2_fop_release(file); + if (!ret) + v4l2_pipeline_pm_put(&stream->vnode.vdev.entity); + return ret; +} + +static const struct v4l2_file_operations rkvpss_fops = { + .open = rkvpss_fh_open, + .release = rkvpss_fh_release, + .unlocked_ioctl = video_ioctl2, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = video_ioctl2, +#endif +}; + +static int rkvpss_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + if (input->index > 0) + return -EINVAL; + + input->type = V4L2_INPUT_TYPE_CAMERA; + strscpy(input->name, "Camera", sizeof(input->name)); + + return 0; +} + +static int rkvpss_try_fmt_vid_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct rkvpss_stream *stream = video_drvdata(file); + + return rkvpss_set_fmt(stream, &f->fmt.pix_mp, true); +} + +static int rkvpss_enum_fmt_vid_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct rkvpss_stream *stream = video_drvdata(file); + const struct capture_fmt *fmt = NULL; + + if (f->index >= stream->config->fmt_size) + return -EINVAL; + + fmt = &stream->config->fmts[f->index]; + f->pixelformat = fmt->fourcc; + + return 0; +} + +static int rkvpss_s_fmt_vid_mplane(struct file *file, + void *priv, struct v4l2_format *f) +{ + struct rkvpss_stream *stream = video_drvdata(file); + struct video_device *vdev = &stream->vnode.vdev; + struct rkvpss_vdev_node *node = vdev_to_node(vdev); + struct rkvpss_device *dev = stream->dev; + + /* Change not allowed if queue is streaming. */ + if (vb2_is_streaming(&node->buf_queue)) { + v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + return rkvpss_set_fmt(stream, &f->fmt.pix_mp, false); +} + +static int rkvpss_g_fmt_vid_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct rkvpss_stream *stream = video_drvdata(file); + + f->fmt.pix_mp = stream->out_fmt; + + return 0; +} + +static int rkvpss_g_selection(struct file *file, void *prv, + struct v4l2_selection *sel) +{ + struct rkvpss_stream *stream = video_drvdata(file); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = stream->crop; + break; + default: + return -EINVAL; + } + return 0; +} + +static int rkvpss_s_selection(struct file *file, void *prv, + struct v4l2_selection *sel) +{ + struct rkvpss_stream *stream = video_drvdata(file); + struct rkvpss_device *dev = stream->dev; + struct v4l2_rect *crop = &stream->crop; + u32 max_w = dev->vpss_sdev.in_fmt.width; + u32 max_h = dev->vpss_sdev.in_fmt.height; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + if (sel->flags != 0) + return -EINVAL; + + sel->r.left = ALIGN(sel->r.left, 2); + sel->r.top = ALIGN(sel->r.top, 2); + sel->r.width = ALIGN(sel->r.width, 2); + sel->r.height = ALIGN(sel->r.height, 2); + if (!sel->r.width || !sel->r.height || + sel->r.width + sel->r.left > max_w || + sel->r.height + sel->r.top > max_h) { + v4l2_err(&dev->v4l2_dev, + "invalid crop left:%d top:%d w:%d h:%d for %dx%d\n", + sel->r.left, sel->r.top, sel->r.width, sel->r.height, max_w, max_h); + return -EINVAL; + } + *crop = sel->r; + stream->is_crop_upd = true; + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "stream %d crop(%d,%d)/%dx%d\n", stream->id, + crop->left, crop->top, crop->width, crop->height); + return 0; +} + +static int rkvpss_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct rkvpss_stream *stream = video_drvdata(file); + struct device *dev = stream->dev->dev; + struct video_device *vdev = video_devdata(file); + + strscpy(cap->card, vdev->name, sizeof(cap->card)); + snprintf(cap->driver, sizeof(cap->driver), + "%s_v%d", dev->driver->name, + stream->dev->vpss_ver >> 4); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev_name(dev)); + + return 0; +} + +static void rkvpss_stream_mf(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + u32 val, mask; + + if (!stream->is_mf_upd) + return; + stream->is_mf_upd = false; + + if (!dev->hw_dev->is_ofl_cmsc) { + mask = RKVPSS_MIR_EN; + val = dev->mir_en ? RKVPSS_MIR_EN : 0; + rkvpss_set_bits(dev, RKVPSS_VPSS_CTRL, mask, val); + } + mask = RKVPSS_MI_CHN_V_FLIP(stream->id); + val = stream->flip_en ? mask : 0; + rkvpss_set_bits(dev, RKVPSS_MI_WR_VFLIP_CTRL, mask, val); +} + +static int rkvpss_get_mirror_flip(struct rkvpss_stream *stream, + struct rkvpss_mirror_flip *cfg) +{ + cfg->mirror = stream->dev->mir_en; + cfg->flip = stream->flip_en; + return 0; +} + +static int rkvpss_set_mirror_flip(struct rkvpss_stream *stream, + struct rkvpss_mirror_flip *cfg) +{ + struct rkvpss_device *dev = stream->dev; + + if (dev->hw_dev->is_ofl_cmsc && cfg->mirror) { + cfg->mirror = 0; + v4l2_warn(&stream->dev->v4l2_dev, + "mirror mux to vpss offline mode\n"); + } + if (dev->mir_en != !!cfg->mirror || + stream->flip_en != !!cfg->flip) + stream->is_mf_upd = true; + dev->mir_en = !!cfg->mirror; + stream->flip_en = !!cfg->flip; + return 0; +} + +void rkvpss_cmsc_config(struct rkvpss_device *dev, bool sync) +{ + unsigned long lock_flags = 0; + struct rkvpss_cmsc_cfg cfg; + int i, j, k, slope, hor; + u32 win_en, mode, val, ctrl = 0; + + spin_lock_irqsave(&dev->cmsc_lock, lock_flags); + if (dev->hw_dev->is_ofl_cmsc || !dev->cmsc_upd) { + spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); + return; + } + dev->cmsc_upd = false; + cfg = dev->cmsc_cfg; + spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); + + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + win_en = cfg.win[i].win_en; + if (win_en) + ctrl |= RKVPSS_CMSC_CHN_EN(i); + rkvpss_write(dev, RKVPSS_CMSC_CHN0_WIN + i * 4, win_en); + + mode = cfg.win[i].mode; + rkvpss_write(dev, RKVPSS_CMSC_CHN0_MODE + i * 4, mode); + + for (j = 0; j < RKVPSS_CMSC_WIN_MAX && win_en; j++) { + if (!(win_en & BIT(j))) + continue; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + val = RKVPSS_CMSC_WIN_VTX(cfg.win[j].point[k].x, + cfg.win[j].point[k].y); + rkvpss_write(dev, RKVPSS_CMSC_WIN0_L0_VTX + k * 8 + j * 32, val); + + rkvpss_cmsc_slop(&cfg.win[j].point[k], + (k + 1 == RKVPSS_CMSC_POINT_MAX) ? + &cfg.win[j].point[0] : &cfg.win[j].point[k + 1], + &slope, &hor); + val = RKVPSS_CMSC_WIN_SLP(slope, hor); + rkvpss_write(dev, RKVPSS_CMSC_WIN0_L0_SLP + k * 8 + j * 32, val); + } + + if ((mode & BIT(j))) + continue; + val = RKVPSS_CMSK_WIN_YUV(cfg.win[j].cover_color_y, + cfg.win[j].cover_color_u, + cfg.win[j].cover_color_v); + val |= RKVPSS_CMSC_WIN_ALPHA(cfg.win[j].cover_color_a); + rkvpss_write(dev, RKVPSS_CMSC_WIN0_PARA + j * 4, val); + } + } + + if (ctrl) { + ctrl |= RKVPSS_CMSC_EN; + ctrl |= RKVPSS_CMSC_BLK_SZIE(cfg.mosaic_block); + } + rkvpss_write(dev, RKVPSS_CMSC_CTRL, ctrl); + val = RKVPSS_CMSC_GEN_UPD; + if (sync) + val |= RKVPSS_CMSC_FORCE_UPD; + rkvpss_write(dev, RKVPSS_CMSC_UPDATE, val); +} + +static int rkvpss_get_cmsc(struct rkvpss_stream *stream, struct rkvpss_cmsc_cfg *cfg) +{ + struct rkvpss_device *dev = stream->dev; + unsigned long lock_flags = 0; + u32 i, win_en, mode; + + spin_lock_irqsave(&dev->cmsc_lock, lock_flags); + *cfg = dev->cmsc_cfg; + spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); + + win_en = cfg->win[stream->id].win_en; + mode = cfg->win[stream->id].mode; + for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { + cfg->win[i].win_en = !!(win_en & BIT(i)); + cfg->win[i].mode = !!(mode & BIT(i)); + } + cfg->width_ro = dev->vpss_sdev.in_fmt.width; + cfg->height_ro = dev->vpss_sdev.in_fmt.height; + return 0; +} + +static int rkvpss_set_cmsc(struct rkvpss_stream *stream, struct rkvpss_cmsc_cfg *cfg) +{ + struct rkvpss_device *dev = stream->dev; + unsigned long lock_flags = 0; + u16 i, j, k, win_en = 0, mode = 0; + int ret = 0; + + if (dev->hw_dev->is_ofl_cmsc) + return -EINVAL; + + spin_lock_irqsave(&dev->cmsc_lock, lock_flags); + for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { + if (!cfg->win[i].win_en) + continue; + + win_en |= BIT(i); + mode |= cfg->win[i].mode ? BIT(i) : 0; + + for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) { + for (k = j + 1; k < RKVPSS_CMSC_POINT_MAX; k++) { + if (cfg->win[i].point[j].x == cfg->win[i].point[k].x && + cfg->win[i].point[j].y == cfg->win[i].point[k].y) { + v4l2_warn(&dev->v4l2_dev, + "points should different, P%d(%d,%d) P%d(%d,%d)\n", + j, cfg->win[i].point[j].x, cfg->win[i].point[j].y, + k, cfg->win[i].point[k].x, cfg->win[i].point[k].y); + ret = -EINVAL; + goto unlock; + } + } + } + if (!cfg->win[i].mode) { + dev->cmsc_cfg.win[i].cover_color_y = cfg->win[i].cover_color_y; + dev->cmsc_cfg.win[i].cover_color_u = cfg->win[i].cover_color_u; + dev->cmsc_cfg.win[i].cover_color_v = cfg->win[i].cover_color_v; + dev->cmsc_cfg.win[i].cover_color_a = cfg->win[i].cover_color_a; + if (dev->cmsc_cfg.win[i].cover_color_a > 15) + dev->cmsc_cfg.win[i].cover_color_a = 15; + } + for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) + dev->cmsc_cfg.win[i].point[j] = cfg->win[i].point[j]; + } + dev->cmsc_cfg.win[stream->id].win_en = win_en; + dev->cmsc_cfg.win[stream->id].mode = mode; + dev->cmsc_cfg.mosaic_block = cfg->mosaic_block; + dev->cmsc_upd = true; +unlock: + spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); + return ret; +} + +static long rkvpss_ioctl_default(struct file *file, void *fh, + bool valid_prio, unsigned int cmd, void *arg) +{ + struct rkvpss_stream *stream = video_drvdata(file); + long ret = 0; + + if (!arg) + return -EINVAL; + + switch (cmd) { + case RKVPSS_CMD_GET_MIRROR_FLIP: + ret = rkvpss_get_mirror_flip(stream, arg); + break; + case RKVPSS_CMD_SET_MIRROR_FLIP: + ret = rkvpss_set_mirror_flip(stream, arg); + break; + case RKVPSS_CMD_GET_CMSC: + ret = rkvpss_get_cmsc(stream, arg); + break; + case RKVPSS_CMD_SET_CMSC: + ret = rkvpss_set_cmsc(stream, arg); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ioctl_ops rkvpss_v4l2_ioctl_ops = { + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_input = rkvpss_enum_input, + .vidioc_try_fmt_vid_cap_mplane = rkvpss_try_fmt_vid_mplane, + .vidioc_enum_fmt_vid_cap = rkvpss_enum_fmt_vid_mplane, + .vidioc_s_fmt_vid_cap_mplane = rkvpss_s_fmt_vid_mplane, + .vidioc_g_fmt_vid_cap_mplane = rkvpss_g_fmt_vid_mplane, + .vidioc_s_selection = rkvpss_s_selection, + .vidioc_g_selection = rkvpss_g_selection, + .vidioc_querycap = rkvpss_querycap, + .vidioc_default = rkvpss_ioctl_default, +}; + +static void rkvpss_unregister_stream_video(struct rkvpss_stream *stream) +{ + tasklet_kill(&stream->buf_done_tasklet); + media_entity_cleanup(&stream->vnode.vdev.entity); + video_unregister_device(&stream->vnode.vdev); +} + +static int rkvpss_register_stream_video(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + struct video_device *vdev = &stream->vnode.vdev; + struct rkvpss_vdev_node *node; + enum v4l2_buf_type buf_type; + int ret = 0; + + node = vdev_to_node(vdev); + vdev->release = video_device_release_empty; + vdev->fops = &rkvpss_fops; + vdev->minor = -1; + vdev->v4l2_dev = v4l2_dev; + vdev->lock = &dev->apilock; + video_set_drvdata(vdev, stream); + + vdev->ioctl_ops = &rkvpss_v4l2_ioctl_ops; + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE; + vdev->vfl_dir = VFL_DIR_RX; + node->pad.flags = MEDIA_PAD_FL_SINK; + buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + + rkvpss_init_vb2_queue(&node->buf_queue, stream, buf_type); + vdev->queue = &node->buf_queue; + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret < 0) { + v4l2_err(v4l2_dev, + "video register failed with error %d\n", ret); + return ret; + } + + ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); + if (ret < 0) + goto unreg; + + INIT_LIST_HEAD(&stream->buf_done_list); + tasklet_init(&stream->buf_done_tasklet, + rkvpss_buf_done_task, (unsigned long)stream); + tasklet_disable(&stream->buf_done_tasklet); + return 0; +unreg: + video_unregister_device(vdev); + return ret; +} + +void rkvpss_stream_default_fmt(struct rkvpss_device *dev, u32 id, + u32 width, u32 height, u32 pixelformat) +{ + struct rkvpss_stream *stream = &dev->stream_vdev.stream[id]; + struct v4l2_pix_format_mplane pixm = { 0 }; + + if (pixelformat) + pixm.pixelformat = pixelformat; + else + pixm.pixelformat = stream->out_cap_fmt.fourcc; + if (!pixm.pixelformat) + return; + + stream->crop.left = 0; + stream->crop.top = 0; + stream->crop.width = width; + stream->crop.height = height; + + pixm.width = width; + pixm.height = height; + rkvpss_set_fmt(stream, &pixm, false); +} + +int rkvpss_register_stream_vdevs(struct rkvpss_device *dev) +{ + struct rkvpss_stream_vdev *stream_vdev; + struct rkvpss_stream *stream; + struct video_device *vdev; + char *vdev_name; + int i, j, ret = 0; + + stream_vdev = &dev->stream_vdev; + memset(stream_vdev, 0, sizeof(*stream_vdev)); + + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + stream = &stream_vdev->stream[i]; + stream->id = i; + stream->dev = dev; + vdev = &stream->vnode.vdev; + INIT_LIST_HEAD(&stream->buf_queue); + init_waitqueue_head(&stream->done); + spin_lock_init(&stream->vbq_lock); + switch (i) { + case RKVPSS_OUTPUT_CH0: + vdev_name = S0_VDEV_NAME; + stream->ops = &scl_stream_ops; + stream->config = &scl0_config; + break; + case RKVPSS_OUTPUT_CH1: + vdev_name = S1_VDEV_NAME; + stream->ops = &scl_stream_ops; + stream->config = &scl1_config; + break; + case RKVPSS_OUTPUT_CH2: + vdev_name = S2_VDEV_NAME; + stream->ops = &scl_stream_ops; + stream->config = &scl2_config; + break; + case RKVPSS_OUTPUT_CH3: + vdev_name = S3_VDEV_NAME; + stream->ops = &scl_stream_ops; + stream->config = &scl3_config; + break; + default: + v4l2_err(&dev->v4l2_dev, "Invalid stream:%d\n", i); + return -EINVAL; + } + strscpy(vdev->name, vdev_name, sizeof(vdev->name)); + ret = rkvpss_register_stream_video(stream); + if (ret < 0) + goto err; + } + return 0; +err: + for (j = 0; j < i; j++) { + stream = &stream_vdev->stream[j]; + rkvpss_unregister_stream_video(stream); + } + return ret; +} + +void rkvpss_unregister_stream_vdevs(struct rkvpss_device *dev) +{ + struct rkvpss_stream_vdev *stream_vdev; + struct rkvpss_stream *stream; + int i; + + stream_vdev = &dev->stream_vdev; + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + stream = &stream_vdev->stream[i]; + rkvpss_unregister_stream_video(stream); + } +} + +void rkvpss_isr(struct rkvpss_device *dev, u32 mis_val) +{ + v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, + "isr:0x%x\n", mis_val); + if (mis_val & RKVPSS_ISP_ALL_FRM_END && dev->remote_sd) + v4l2_subdev_call(dev->remote_sd, core, ioctl, RKISP_VPSS_CMD_EOF, NULL); +} + +void rkvpss_mi_isr(struct rkvpss_device *dev, u32 mis_val) +{ + struct rkvpss_stream *stream; + int i, ris = readl(dev->hw_dev->base_addr + RKVPSS_MI_RIS); + + v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, + "mi isr:0x%x, ris:0x%x\n", mis_val, ris); + if (mis_val & RKVPSS_MI_BUS_ERR) + v4l2_warn(&dev->v4l2_dev, "axi bus error\n"); + + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + stream = &dev->stream_vdev.stream[i]; + + if (!stream->streaming || + !(ris & stream->config->frame_end_id)) + continue; + writel(stream->config->frame_end_id, dev->hw_dev->base_addr + RKVPSS_MI_ICR); + if (stream->stopping) { + stream->stopping = false; + stream->streaming = false; + stream->ops->disable_mi(stream); + wake_up(&stream->done); + } else { + rkvpss_frame_end(stream); + } + } +} diff --git a/drivers/media/platform/rockchip/vpss/stream.h b/drivers/media/platform/rockchip/vpss/stream.h new file mode 100644 index 000000000000..fc40f4200db9 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/stream.h @@ -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 +#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 diff --git a/drivers/media/platform/rockchip/vpss/version.h b/drivers/media/platform/rockchip/vpss/version.h new file mode 100644 index 000000000000..50db86e796a3 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/version.h @@ -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 +#include + +/* + * RKVPSS DRIVER VERSION NOTE + * + * v0.1.0: + * 1. First version; + * + */ + +#define RKVPSS_DRIVER_VERSION VPSS_API_VERSION + +#endif diff --git a/drivers/media/platform/rockchip/vpss/vpss.c b/drivers/media/platform/rockchip/vpss/vpss.c new file mode 100644 index 000000000000..ad8ee9fe5882 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */ + +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/drivers/media/platform/rockchip/vpss/vpss.h b/drivers/media/platform/rockchip/vpss/vpss.h new file mode 100644 index 000000000000..06ef58628e13 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss.h @@ -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 diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline.c b/drivers/media/platform/rockchip/vpss/vpss_offline.c new file mode 100644 index 000000000000..47c79ed970f1 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_offline.c @@ -0,0 +1,1075 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dev.h" +#include "hw.h" +#include "regs.h" + +struct rkvpss_output_ch { + u32 ctrl; + u32 size; + u32 c_offs; +}; + +struct rkvpss_offline_buf { + struct list_head list; + struct vb2_buffer vb; + struct vb2_queue vb2_queue; + struct file *file; + struct dma_buf *dbuf; + void *mem; + int dev_id; + int fd; + bool alloc; +}; + +static void init_vb2(struct rkvpss_offline_dev *ofl, + struct rkvpss_offline_buf *buf) +{ + struct rkvpss_hw_dev *hw = ofl->hw; + unsigned long attrs = DMA_ATTR_NO_KERNEL_MAPPING; + + if (!buf) + return; + memset(&buf->vb, 0, sizeof(buf->vb)); + memset(&buf->vb2_queue, 0, sizeof(buf->vb2_queue)); + buf->vb2_queue.gfp_flags = GFP_KERNEL | GFP_DMA32; + buf->vb2_queue.dma_dir = DMA_BIDIRECTIONAL; + if (hw->is_dma_contig) + attrs |= DMA_ATTR_FORCE_CONTIGUOUS; + buf->vb2_queue.dma_attrs = attrs; + buf->vb.vb2_queue = &buf->vb2_queue; +} + +static void buf_del(struct file *file, int id, int fd, bool is_all, bool running) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + const struct vb2_mem_ops *ops = hw->mem_ops; + struct rkvpss_offline_buf *buf, *next; + + mutex_lock(&hw->dev_lock); + list_for_each_entry_safe(buf, next, &ofl->list, list) { + if (buf->file == file && (is_all || buf->fd == fd)) { + if (!is_all && running && buf->alloc) + break; + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p dev_id:%d fd:%d dbuf:%p\n", + __func__, file, buf->dev_id, buf->fd, buf->dbuf); + if (!buf->alloc) { + ops->unmap_dmabuf(buf->mem); + ops->detach_dmabuf(buf->mem); + dma_buf_put(buf->dbuf); + } else { + dma_buf_put(buf->dbuf); + ops->put(buf->mem); + } + buf->file = NULL; + buf->mem = NULL; + buf->dbuf = NULL; + buf->fd = -1; + list_del(&buf->list); + kfree(buf); + if (!is_all) + break; + } + } + mutex_unlock(&hw->dev_lock); +} + +static struct rkvpss_offline_buf *buf_add(struct file *file, int id, int fd, int size) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + const struct vb2_mem_ops *ops = hw->mem_ops; + struct rkvpss_offline_buf *buf = NULL, *next = NULL; + struct dma_buf *dbuf; + void *mem = NULL; + bool is_add = true; + + dbuf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(dbuf)) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d invalid dmabuf fd:%d", id, fd); + return buf; + } + if (size && dbuf->size < size) { + v4l2_err(&ofl->v4l2_dev, + "dev_id:%d input fd:%d size error:%zu < %u\n", + id, fd, dbuf->size, size); + dma_buf_put(dbuf); + return buf; + } + + mutex_lock(&hw->dev_lock); + list_for_each_entry_safe(buf, next, &ofl->list, list) { + if (buf->file == file && buf->fd == fd && buf->dbuf == dbuf) { + is_add = false; + break; + } + } + + if (is_add) { + buf = kzalloc(sizeof(struct rkvpss_offline_buf), GFP_KERNEL); + if (!buf) + goto end; + init_vb2(ofl, buf); + mem = ops->attach_dmabuf(&buf->vb, hw->dev, dbuf, dbuf->size); + if (IS_ERR(mem)) { + v4l2_err(&ofl->v4l2_dev, "failed to attach dmabuf, fd:%d\n", fd); + dma_buf_put(dbuf); + kfree(buf); + buf = NULL; + goto end; + } + if (ops->map_dmabuf(mem)) { + v4l2_err(&ofl->v4l2_dev, "failed to map, fd:%d\n", fd); + ops->detach_dmabuf(mem); + dma_buf_put(dbuf); + mem = NULL; + kfree(buf); + buf = NULL; + goto end; + } + buf->dev_id = id; + buf->fd = fd; + buf->file = file; + buf->dbuf = dbuf; + buf->mem = mem; + buf->alloc = false; + list_add_tail(&buf->list, &ofl->list); + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p dev_id:%d fd:%d dbuf:%p\n", __func__, file, id, fd, dbuf); + } else { + dma_buf_put(dbuf); + } +end: + mutex_unlock(&hw->dev_lock); + return buf; +} + +static int internal_buf_alloc(struct file *file, struct rkvpss_buf_info *info) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + const struct vb2_mem_ops *ops = hw->mem_ops; + struct rkvpss_offline_buf *buf; + struct dma_buf *dbuf; + int fd, i, size; + void *mem; + + for (i = 0; i < info->buf_cnt; i++) { + info->buf_fd[i] = -1; + size = PAGE_ALIGN(info->buf_size[i]); + if (!size) + continue; + buf = kzalloc(sizeof(struct rkvpss_offline_buf), GFP_KERNEL); + if (!buf) + goto err; + init_vb2(ofl, buf); + mem = ops->alloc(&buf->vb, hw->dev, size); + if (IS_ERR_OR_NULL(mem)) { + kfree(buf); + goto err; + } + dbuf = ops->get_dmabuf(&buf->vb, mem, O_RDWR); + if (IS_ERR_OR_NULL(dbuf)) { + ops->put(mem); + kfree(buf); + goto err; + } + fd = dma_buf_fd(dbuf, O_CLOEXEC); + if (fd < 0) { + dma_buf_put(dbuf); + ops->put(mem); + kfree(buf); + goto err; + } + get_dma_buf(dbuf); + + info->buf_fd[i] = fd; + buf->fd = fd; + buf->file = file; + buf->dbuf = dbuf; + buf->mem = mem; + buf->alloc = true; + buf->dev_id = info->dev_id; + mutex_lock(&hw->dev_lock); + list_add_tail(&buf->list, &ofl->list); + mutex_unlock(&hw->dev_lock); + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p dev_id:%d fd:%d dbuf:%p\n", + __func__, file, buf->dev_id, fd, dbuf); + } + return 0; +err: + for (i -= 1; i >= 0; i--) + buf_del(file, info->dev_id, info->buf_fd[i], false, false); + return -ENOMEM; +} + +static int external_buf_add(struct file *file, struct rkvpss_buf_info *info) +{ + void *mem; + int i; + + for (i = 0; i < info->buf_cnt; i++) { + mem = buf_add(file, info->dev_id, info->buf_fd[i], info->buf_size[i]); + if (!mem) + goto err; + } + return 0; +err: + for (i -= 1; i >= 0; i--) + buf_del(file, info->dev_id, info->buf_fd[i], false, false); + return -ENOMEM; +} + +static int rkvpss_ofl_buf_add(struct file *file, struct rkvpss_buf_info *info) +{ + int ret; + + if (info->buf_alloc) + ret = internal_buf_alloc(file, info); + else + ret = external_buf_add(file, info); + return ret; +} + +static void rkvpss_ofl_buf_del(struct file *file, struct rkvpss_buf_info *info) +{ + int i; + + for (i = 0; i < info->buf_cnt; i++) + buf_del(file, info->dev_id, info->buf_fd[i], false, false); +} + +static void poly_phase_scale(struct rkvpss_offline_dev *ofl, + struct rkvpss_output_cfg *cfg) +{ + struct rkvpss_hw_dev *hw = ofl->hw; + u32 in_w = cfg->crop_width, in_h = cfg->crop_height; + u32 out_w = cfg->scl_width, out_h = cfg->scl_height; + u32 ctrl, y_xscl_fac, y_yscl_fac, uv_xscl_fac, uv_yscl_fac; + u32 i, j, idx, ratio, val, in_div, out_div, factor; + bool dering_en = false, yuv420_in = false, yuv422_to_420 = false; + + if (in_w == out_w && in_h == out_w) { + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SCL_CTRL, 0); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SCL_CTRL, 0); + goto end; + } + + /* TODO diff for input and output format */ + if (yuv420_in) { + in_div = 2; + out_div = 2; + } else if (yuv422_to_420) { + in_div = 1; + out_div = 2; + } else { + in_div = 1; + out_div = 1; + } + + val = (in_w - 1) | ((in_h - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); + val = (out_w - 1) | ((out_h - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_DST_SIZE, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_DST_SIZE, val); + + ctrl = RKVPSS_ZME_XSCL_MODE | RKVPSS_ZME_YSCL_MODE; + if (dering_en) { + ctrl |= RKVPSS_ZME_DERING_EN; + rkvpss_hw_write(hw, RKVPSS_ZME_Y_DERING_PARA, 0xd10410); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_DERING_PARA, 0xd10410); + } + if (in_w != out_w) { + if (in_w > out_w) { + factor = 4096; + ctrl |= RKVPSS_ZME_XSD_EN; + } else { + factor = 65536; + ctrl |= RKVPSS_ZME_XSU_EN; + } + y_xscl_fac = (in_w - 1) * factor / (out_w - 1); + uv_xscl_fac = (in_w / 2 - 1) * factor / (out_w / 2 - 1); + + ratio = y_xscl_fac * 10000 / factor; + idx = rkvpss_get_zme_tap_coe_index(ratio); + for (i = 0; i < 17; i++) { + for (j = 0; j < 8; j += 2) { + val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap8_coe[idx][i][j], + rkvpss_zme_tap8_coe[idx][i][j + 1]); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, val); + } + } + } else { + y_xscl_fac = 0; + uv_xscl_fac = 0; + } + rkvpss_hw_write(hw, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac); + + if (in_h != out_h) { + if (in_h > out_h) { + factor = 4096; + ctrl |= RKVPSS_ZME_YSD_EN; + } else { + factor = 65536; + ctrl |= RKVPSS_ZME_YSU_EN; + } + y_yscl_fac = (in_h - 1) * factor / (out_h - 1); + uv_yscl_fac = (in_h / in_div - 1) * factor / (out_h / out_div - 1); + + ratio = y_yscl_fac * 10000 / factor; + idx = rkvpss_get_zme_tap_coe_index(ratio); + for (i = 0; i < 17; i++) { + for (j = 0; j < 8; j += 2) { + val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap6_coe[idx][i][j], + rkvpss_zme_tap6_coe[idx][i][j + 1]); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, val); + } + } + } else { + y_yscl_fac = 0; + uv_yscl_fac = 0; + } + rkvpss_hw_write(hw, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac); + + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SCL_CTRL, ctrl); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SCL_CTRL, ctrl); + + ctrl = RKVPSS_ZME_GATING_EN; + if (yuv420_in) + ctrl |= RKVPSS_ZME_SCL_YUV420_REAL_EN; + if (yuv422_to_420) + ctrl |= RKVPSS_ZME_422TO420_EN; + rkvpss_hw_write(hw, RKVPSS_ZME_CTRL, ctrl); +end: + val = RKVPSS_ZME_GEN_UPD | RKVPSS_ZME_FORCE_UPD; + rkvpss_hw_write(hw, RKVPSS_ZME_UPDATE, val); +} + +static void bilinear_scale(struct rkvpss_offline_dev *ofl, + struct rkvpss_output_cfg *cfg, int idx) +{ + struct rkvpss_hw_dev *hw = ofl->hw; + u32 in_w = cfg->crop_width, in_h = cfg->crop_height; + u32 out_w = cfg->scl_width, out_h = cfg->scl_height; + u32 reg_base, in_div, out_div, val, ctrl = 0; + bool yuv420_in = false, yuv422_to_420 = false; + + switch (idx) { + case RKVPSS_OUTPUT_CH1: + reg_base = RKVPSS_SCALE1_BASE; + break; + case RKVPSS_OUTPUT_CH2: + reg_base = RKVPSS_SCALE2_BASE; + break; + case RKVPSS_OUTPUT_CH3: + reg_base = RKVPSS_SCALE3_BASE; + break; + default: + return; + } + + if (in_w == out_w && in_h == out_w) + goto end; + + /* TODO diff for input and output format */ + if (yuv420_in) { + in_div = 2; + out_div = 2; + } else if (yuv422_to_420) { + in_div = 1; + out_div = 2; + } else { + in_div = 1; + out_div = 1; + } + + val = in_w | (in_h << 16); + rkvpss_hw_write(hw, reg_base + 0x8, val); + val = out_w | (out_h << 16); + rkvpss_hw_write(hw, reg_base + 0xc, val); + + if (in_w != out_w) { + val = (in_w - 1) * 4096 / (out_w - 1); + rkvpss_hw_write(hw, reg_base + 0x10, val); + val = (in_w / 2 - 1) * 4096 / (out_w / 2 - 1); + rkvpss_hw_write(hw, reg_base + 0x14, val); + + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; + } + if (in_h != out_h) { + val = (in_h - 1) * 4096 / (out_h - 1); + rkvpss_hw_write(hw, reg_base + 0x18, val); + val = (in_h / in_div - 1) * 4096 / (out_h / out_div - 1); + rkvpss_hw_write(hw, reg_base + 0x1c, val); + + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; + } + +end: + rkvpss_hw_write(hw, reg_base, ctrl); + val = RKVPSS_SCL_GEN_UPD | RKVPSS_SCL_FORCE_UPD; + rkvpss_hw_write(hw, reg_base + 0x4, val); +} + +static void scale_config(struct rkvpss_offline_dev *ofl, + struct rkvpss_output_cfg *cfg, int idx) +{ + if (idx == RKVPSS_OUTPUT_CH0) + poly_phase_scale(ofl, cfg); + else + bilinear_scale(ofl, cfg, idx); +} + +static void cmsc_config(struct rkvpss_offline_dev *ofl, + struct rkvpss_frame_cfg *cfg) +{ + struct rkvpss_hw_dev *hw = ofl->hw; + struct rkvpss_cmsc_cfg *cmsc_cfg, tmp_cfg = { 0 }; + u32 i, j, k, slope, hor, win_en, win_mode, val, ctrl = 0; + bool is_en = false; + + if (!hw->is_ofl_cmsc) + return; + + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (!cfg->output[i].enable) + continue; + win_en = 0; + win_mode = 0; + cmsc_cfg = &cfg->output[i].cmsc; + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + if (!cmsc_cfg->win[j].win_en) + continue; + win_en |= BIT(j); + win_mode |= cmsc_cfg->win[j].mode ? BIT(j) : 0; + if (!cmsc_cfg->win[j].mode) { + tmp_cfg.win[j].cover_color_y = cmsc_cfg->win[j].cover_color_y; + tmp_cfg.win[j].cover_color_u = cmsc_cfg->win[j].cover_color_u; + tmp_cfg.win[j].cover_color_v = cmsc_cfg->win[j].cover_color_v; + tmp_cfg.win[j].cover_color_a = cmsc_cfg->win[j].cover_color_a; + if (tmp_cfg.win[j].cover_color_a > 15) + tmp_cfg.win[j].cover_color_a = 15; + } + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) + tmp_cfg.win[j].point[k] = cmsc_cfg->win[j].point[k]; + } + if (win_en) + is_en = true; + tmp_cfg.win[i].win_en = win_en; + tmp_cfg.win[i].mode = win_mode; + if (tmp_cfg.mosaic_block < cmsc_cfg->mosaic_block) + tmp_cfg.mosaic_block = cmsc_cfg->mosaic_block; + } + + if (!is_en) { + rkvpss_hw_write(hw, RKVPSS_CMSC_CTRL, 0); + rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_WIN, 0); + rkvpss_hw_write(hw, RKVPSS_CMSC_CHN1_WIN, 0); + rkvpss_hw_write(hw, RKVPSS_CMSC_CHN2_WIN, 0); + rkvpss_hw_write(hw, RKVPSS_CMSC_CHN3_WIN, 0); + goto end; + } + + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + win_en = tmp_cfg.win[i].win_en; + if (win_en) + ctrl |= RKVPSS_CMSC_CHN_EN(i); + rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_WIN + i * 4, win_en); + win_mode = tmp_cfg.win[i].mode; + rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_MODE + i * 4, win_mode); + for (j = 0; j < RKVPSS_CMSC_WIN_MAX && win_en; j++) { + if (!(win_en & BIT(j))) + continue; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + val = RKVPSS_CMSC_WIN_VTX(tmp_cfg.win[j].point[k].x, + tmp_cfg.win[j].point[k].y); + rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_L0_VTX + k * 8 + j * 32, val); + rkvpss_cmsc_slop(&tmp_cfg.win[j].point[k], + (k + 1 == RKVPSS_CMSC_POINT_MAX) ? + &tmp_cfg.win[j].point[0] : &tmp_cfg.win[j].point[k + 1], + &slope, &hor); + val = RKVPSS_CMSC_WIN_SLP(slope, hor); + rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_L0_SLP + k * 8 + j * 32, val); + } + + if ((win_mode & BIT(j))) + continue; + val = RKVPSS_CMSK_WIN_YUV(tmp_cfg.win[j].cover_color_y, + tmp_cfg.win[j].cover_color_u, + tmp_cfg.win[j].cover_color_v); + val |= RKVPSS_CMSC_WIN_ALPHA(tmp_cfg.win[j].cover_color_a); + rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_PARA + j * 4, val); + } + } + ctrl |= RKVPSS_CMSC_EN; + ctrl |= RKVPSS_CMSC_BLK_SZIE(tmp_cfg.mosaic_block); + rkvpss_hw_write(hw, RKVPSS_CMSC_CTRL, ctrl); +end: + val = RKVPSS_CMSC_GEN_UPD | RKVPSS_CMSC_FORCE_UPD; + rkvpss_hw_write(hw, RKVPSS_CMSC_UPDATE, val); +} + +static void aspt_config(struct rkvpss_offline_dev *ofl, + struct rkvpss_output_cfg *cfg, int idx) +{ + struct rkvpss_hw_dev *hw = ofl->hw; + u32 reg_base, val; + + switch (idx) { + case RKVPSS_OUTPUT_CH0: + reg_base = RKVPSS_RATIO0_BASE; + break; + case RKVPSS_OUTPUT_CH1: + reg_base = RKVPSS_RATIO1_BASE; + break; + case RKVPSS_OUTPUT_CH2: + reg_base = RKVPSS_RATIO2_BASE; + break; + case RKVPSS_OUTPUT_CH3: + reg_base = RKVPSS_RATIO3_BASE; + break; + default: + return; + } + + if (!cfg->aspt.enable) { + rkvpss_hw_write(hw, reg_base, 0); + goto end; + } + val = cfg->scl_width | (cfg->scl_height << 16); + rkvpss_hw_write(hw, reg_base + 0x10, val); + val = cfg->aspt.width | (cfg->aspt.height << 16); + rkvpss_hw_write(hw, reg_base + 0x14, val); + val = cfg->aspt.h_offs | (cfg->aspt.v_offs << 16); + rkvpss_hw_write(hw, reg_base + 0x18, val); + val = cfg->aspt.color_y | (cfg->aspt.color_u << 16) | (cfg->aspt.color_v << 24); + rkvpss_hw_write(hw, reg_base + 0x1c, val); + rkvpss_hw_write(hw, reg_base, RKVPSS_RATIO_EN); +end: + val = RKVPSS_RATIO_FORCE_UPD | RKVPSS_RATIO_GEN_UPD; + rkvpss_hw_write(hw, reg_base + 0x4, val); +} + +static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + const struct vb2_mem_ops *mem_ops = hw->mem_ops; + struct sg_table *sg_tbl; + struct rkvpss_offline_buf *buf; + struct rkvpss_output_ch out_ch[RKVPSS_OUTPUT_MAX] = { 0 }; + u32 in_ctrl, in_size, in_c_offs, update, mi_update; + u32 w, h, val, reg, mask, crop_en, flip_en; + bool ch_en = false; + ktime_t t = 0; + s64 us = 0; + int ret, i; + + if (rkvpss_debug >= 2) { + v4l2_info(&ofl->v4l2_dev, + "%s dev_id:%d seq:%d mirror:%d input:%dx%d buffd:%d format:%c%c%c%c\n", + __func__, cfg->dev_id, cfg->sequence, cfg->mirror, + cfg->input.width, cfg->input.height, cfg->input.buf_fd, + cfg->input.format, cfg->input.format >> 8, + cfg->input.format >> 16, cfg->input.format >> 24); + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (!cfg->output[i].enable) + continue; + v4l2_info(&ofl->v4l2_dev, + "\t\t\tch%d crop:(%d,%d)/%dx%d scl:%dx%d flip:%d buffd:%d format:%c%c%c%c\n", + i, cfg->output[i].crop_h_offs, cfg->output[i].crop_v_offs, + cfg->output[i].crop_width, cfg->output[i].crop_height, + cfg->output[i].scl_width, cfg->output[i].scl_height, + cfg->output[i].flip, cfg->output[i].buf_fd, + cfg->output[i].format, cfg->output[i].format >> 8, + cfg->output[i].format >> 16, cfg->output[i].format >> 24); + } + t = ktime_get(); + } + init_completion(&ofl->cmpl); + + in_c_offs = 0; + in_ctrl = 0; + switch (cfg->input.format) { + case V4L2_PIX_FMT_NV16: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = cfg->input.stride * cfg->input.height; + in_size = cfg->input.stride * cfg->input.height * 2; + in_ctrl |= RKVPSS_MI_RD_INPUT_422SP; + break; + case V4L2_PIX_FMT_NV12: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = cfg->input.stride * cfg->input.height; + in_size = cfg->input.stride * cfg->input.height * 3 / 2; + in_ctrl |= RKVPSS_MI_RD_INPUT_420SP; + break; + case V4L2_PIX_FMT_RGB565: + if (cfg->input.stride < ALIGN(cfg->input.width * 2, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 2, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_BGR565; + break; + case V4L2_PIX_FMT_RGB24: + if (cfg->input.stride < ALIGN(cfg->input.width * 3, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 3, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_BGR888; + break; + case V4L2_PIX_FMT_XBGR32: + if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 4, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 | RKVPSS_MI_RD_RB_SWAP; + break; + default: + v4l2_err(&ofl->v4l2_dev, "dev_id:%d no support input format:%c%c%c%c\n", + cfg->dev_id, cfg->input.format, cfg->input.format >> 8, + cfg->input.format >> 16, cfg->input.format >> 24); + return -EINVAL; + } + + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (!hw->is_ofl_ch[i] && cfg->output[i].enable) { + v4l2_err(&ofl->v4l2_dev, + "dev_id:%d ch%d no select for offline mode, set to disable\n", + cfg->dev_id, i); + cfg->output[i].enable = 0; + } + + if (!cfg->output[i].enable) + continue; + ch_en = true; + cfg->output[i].crop_h_offs = ALIGN(cfg->output[i].crop_h_offs, 2); + cfg->output[i].crop_v_offs = ALIGN(cfg->output[i].crop_v_offs, 2); + cfg->output[i].crop_width = ALIGN(cfg->output[i].crop_width, 2); + cfg->output[i].crop_height = ALIGN(cfg->output[i].crop_height, 2); + if (cfg->output[i].crop_width + cfg->output[i].crop_h_offs > cfg->input.width) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ech%d inval crop(offs:%d w:%d) input width:%d\n", + i, cfg->dev_id, cfg->output[i].crop_h_offs, + cfg->output[i].crop_width, cfg->input.width); + cfg->output[i].crop_h_offs = 0; + cfg->output[i].crop_width = cfg->input.width; + } + if (cfg->output[i].crop_height + cfg->output[i].crop_v_offs > cfg->input.height) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d inval crop(offs:%d h:%d) input height:%d\n", + cfg->dev_id, i, cfg->output[i].crop_v_offs, + cfg->output[i].crop_height, cfg->input.height); + cfg->output[i].crop_v_offs = 0; + cfg->output[i].crop_height = cfg->input.height; + } + + if (i == RKVPSS_OUTPUT_CH2 || i == RKVPSS_OUTPUT_CH3) { + if (cfg->output[i].crop_width != cfg->output[i].scl_width && + cfg->output[i].scl_width > 1920) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d scale max width 1920\n", + cfg->dev_id, i); + cfg->output[i].scl_width = 1920; + } + if (cfg->output[i].crop_height != cfg->output[i].scl_height && + cfg->output[i].scl_height > 1080) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d scale max height 1080\n", + cfg->dev_id, i); + cfg->output[i].scl_height = 1080; + } + } + + if (cfg->output[i].aspt.enable) { + w = cfg->output[i].aspt.width; + h = cfg->output[i].aspt.height; + } else { + w = cfg->output[i].scl_width; + h = cfg->output[i].scl_height; + } + if (i == RKVPSS_OUTPUT_CH1) { + bool is_fmt_find = true; + + switch (cfg->output[i].format) { + case V4L2_PIX_FMT_RGB565: + if (cfg->output[i].stride < ALIGN(w * 2, 16)) + cfg->output[i].stride = ALIGN(w * 3, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB565 | RKVPSS_MI_CHN_WR_RB_SWAP; + break; + case V4L2_PIX_FMT_RGB24: + if (cfg->output[i].stride < ALIGN(w * 3, 16)) + cfg->output[i].stride = ALIGN(w * 3, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB888 | RKVPSS_MI_CHN_WR_RB_SWAP; + break; + case V4L2_PIX_FMT_XBGR32: + if (cfg->output[i].stride < ALIGN(w * 4, 16)) + cfg->output[i].stride = ALIGN(w * 4, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_ARGB888; + break; + default: + is_fmt_find = false; + } + if (is_fmt_find) { + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD; + out_ch[i].size = cfg->output[i].stride * h; + continue; + } + } + switch (cfg->output[i].format) { + case V4L2_PIX_FMT_UYVY: + if (cfg->output[i].stride < ALIGN(w * 2, 16)) + cfg->output[i].stride = ALIGN(w * 2, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_422P | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; + out_ch[i].size = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_NV16: + if (cfg->output[i].stride < ALIGN(w, 16)) + cfg->output[i].stride = ALIGN(w, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; + out_ch[i].size = cfg->output[i].stride * h * 2; + out_ch[i].c_offs = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_NV12: + if (cfg->output[i].stride < ALIGN(w, 16)) + cfg->output[i].stride = ALIGN(w, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV420; + out_ch[i].size = cfg->output[i].stride * h * 3 / 2; + out_ch[i].c_offs = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_GREY: + if (cfg->output[i].stride < ALIGN(w, 16)) + cfg->output[i].stride = ALIGN(w, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV400; + out_ch[i].size = cfg->output[i].stride * h; + break; + default: + v4l2_err(&ofl->v4l2_dev, "dev_id:%d no support output ch%d format:%c%c%c%c\n", + cfg->dev_id, i, + cfg->output[i].format, cfg->output[i].format >> 8, + cfg->output[i].format >> 16, cfg->output[i].format >> 24); + return -EINVAL; + } + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD; + } + if (!ch_en) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d no output channel enable\n", cfg->dev_id); + return -EINVAL; + } + + buf = buf_add(file, cfg->dev_id, cfg->input.buf_fd, in_size); + if (!buf) + goto err; + + sg_tbl = mem_ops->cookie(&buf->vb, buf->mem); + val = sg_dma_address(sg_tbl->sgl); + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); + val += in_c_offs; + rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); + + val = cfg->input.width; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); + val = cfg->input.height; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_HEIGHT, val); + val = cfg->input.stride; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_STRIDE, val); + + mask = RKVPSS_MI_RD_GROUP_MODE(3) | RKVPSS_MI_RD_BURST16_LEN; + rkvpss_hw_set_bits(hw, RKVPSS_MI_RD_CTRL, ~mask, in_ctrl); + rkvpss_hw_write(hw, RKVPSS_MI_RD_INIT, RKVPSS_MI_RD_FORCE_UPD); + + cmsc_config(ofl, cfg); + + mi_update = 0; + crop_en = 0; + flip_en = 0; + mask = 0; + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (hw->is_ofl_ch[i]) + mask |= RKVPSS_MI_CHN_V_FLIP(i); + if (!cfg->output[i].enable) + continue; + buf = buf_add(file, cfg->dev_id, cfg->output[i].buf_fd, out_ch[i].size); + if (!buf) + goto free_buf; + + reg = RKVPSS_CROP0_0_H_OFFS; + val = cfg->output[i].crop_h_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_OFFS; + val = cfg->output[i].crop_v_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_H_SIZE; + val = cfg->output[i].crop_width; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_SIZE; + val = cfg->output[i].crop_height; + rkvpss_hw_write(hw, reg + i * 0x10, val); + crop_en |= RKVPSS_CROP_CHN_EN(i); + + scale_config(ofl, &cfg->output[i], i); + aspt_config(ofl, &cfg->output[i], i); + + if (cfg->output[i].aspt.enable) + h = cfg->output[i].aspt.height; + else + h = cfg->output[i].scl_height; + + sg_tbl = mem_ops->cookie(&buf->vb, buf->mem); + val = sg_dma_address(sg_tbl->sgl); + reg = RKVPSS_MI_CHN0_WR_Y_BASE; + rkvpss_hw_write(hw, reg + i * 0x100, val); + reg = RKVPSS_MI_CHN0_WR_CB_BASE; + val += out_ch[i].c_offs; + rkvpss_hw_write(hw, reg + i * 0x100, val); + + reg = RKVPSS_MI_CHN0_WR_Y_STRIDE; + val = cfg->output[i].stride; + rkvpss_hw_write(hw, reg + i * 0x100, val); + reg = RKVPSS_MI_CHN0_WR_Y_SIZE; + val = cfg->output[i].stride * h; + rkvpss_hw_write(hw, reg + i * 0x100, val); + reg = RKVPSS_MI_CHN0_WR_CB_SIZE; + val = out_ch[i].size - val; + rkvpss_hw_write(hw, reg + i * 0x100, val); + + reg = RKVPSS_MI_CHN0_WR_CTRL; + val = out_ch[i].ctrl; + rkvpss_hw_write(hw, reg + i * 0x100, val); + + if (cfg->output[i].flip) + flip_en |= RKVPSS_MI_CHN_V_FLIP(i); + mi_update |= (RKVPSS_MI_CHN0_FORCE_UPD << i); + } + rkvpss_hw_write(hw, RKVPSS_CROP0_CTRL, crop_en); + rkvpss_hw_write(hw, RKVPSS_CROP0_UPDATE, RKVPSS_CROP_FORCE_UPD); + if (flip_en) + rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_VFLIP_CTRL, mask, flip_en); + rkvpss_hw_write(hw, RKVPSS_MI_WR_INIT, mi_update); + + mask = 0; + val = 0; + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (!hw->is_ofl_ch[i]) + continue; + mask |= (RKVPSS_ISP2VPSS_CHN0_SEL(3) << i * 2); + if (hw->is_ofl_cmsc) + mask |= RKVPSS_ISP2VPSS_ONLINE2_CMSC_EN; + if (cfg->output[i].enable) + val |= (RKVPSS_ISP2VPSS_CHN0_SEL(1) << i * 2); + } + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_ONLINE, mask, val); + + update = 0; + mask = hw->is_ofl_cmsc ? RKVPSS_MIR_EN : 0; + val = (mask && cfg->mirror) ? RKVPSS_MIR_EN : 0; + if (mask) { + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CTRL, mask, val); + update |= RKVPSS_MIR_FORCE_UPD; + } + update |= RKVPSS_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD | RKVPSS_MIR_GEN_UPD; + rkvpss_hw_write(hw, RKVPSS_VPSS_UPDATE, update); + + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_IMSC, 0, RKVPSS_ALL_FRM_END); + + rkvpss_hw_write(hw, RKVPSS_MI_RD_START, RKVPSS_MI_RD_ST); + + ret = wait_for_completion_timeout(&ofl->cmpl, msecs_to_jiffies(500)); + if (!ret) { + v4l2_err(&ofl->v4l2_dev, "working timeout\n"); + ret = -EAGAIN; + } else { + ret = 0; + } + if (rkvpss_debug >= 2) { + us = ktime_us_delta(ktime_get(), t); + v4l2_info(&ofl->v4l2_dev, + "%s end, time:%lldus\n", __func__, us); + } + return ret; +free_buf: + for (i -= 1; i >= 0; i--) { + if (!cfg->output[i].enable) + continue; + buf_del(file, cfg->dev_id, cfg->output[i].buf_fd, false, true); + } + buf_del(file, cfg->dev_id, cfg->input.buf_fd, false, true); +err: + return -ENOMEM; +} + +static int rkvpss_module_sel(struct file *file, + struct rkvpss_module_sel *sel) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + struct rkvpss_device *vpss; + int i, ret = 0; + + mutex_lock(&hw->dev_lock); + for (i = 0; i < hw->dev_num; i++) { + vpss = hw->vpss[i]; + if (vpss && (vpss->vpss_sdev.state & VPSS_START)) { + v4l2_err(&ofl->v4l2_dev, "no support set mode when vpss working\n"); + ret = -EINVAL; + goto unlock; + } + } + + hw->is_ofl_cmsc = !!sel->mirror_cmsc_en; + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) + hw->is_ofl_ch[i] = !!sel->ch_en[i]; +unlock: + mutex_unlock(&hw->dev_lock); + return ret; +} + +static long rkvpss_ofl_ioctl(struct file *file, void *fh, + bool valid_prio, unsigned int cmd, void *arg) +{ + long ret = 0; + + if (!arg) + return -EINVAL; + + switch (cmd) { + case RKVPSS_CMD_MODULE_SEL: + ret = rkvpss_module_sel(file, arg); + break; + case RKVPSS_CMD_FRAME_HANDLE: + ret = rkvpss_ofl_run(file, arg); + break; + case RKVPSS_CMD_BUF_ADD: + ret = rkvpss_ofl_buf_add(file, arg); + break; + case RKVPSS_CMD_BUF_DEL: + rkvpss_ofl_buf_del(file, arg); + break; + default: + ret = -EFAULT; + } + + return ret; +} + +static const struct v4l2_ioctl_ops offline_ioctl_ops = { + .vidioc_default = rkvpss_ofl_ioctl, +}; + +static int ofl_open(struct file *file) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + int ret; + + ret = v4l2_fh_open(file); + if (ret) + goto end; + + mutex_lock(&ofl->hw->dev_lock); + ret = pm_runtime_get_sync(ofl->hw->dev); + mutex_unlock(&ofl->hw->dev_lock); + if (ret < 0) + v4l2_fh_release(file); +end: + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p ret:%d\n", __func__, file, ret); + return (ret > 0) ? 0 : ret; +} + +static int ofl_release(struct file *file) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p\n", __func__, file); + + v4l2_fh_release(file); + buf_del(file, 0, 0, true, false); + mutex_lock(&ofl->hw->dev_lock); + pm_runtime_put_sync(ofl->hw->dev); + mutex_unlock(&ofl->hw->dev_lock); + return 0; +} + +static const struct v4l2_file_operations offline_fops = { + .owner = THIS_MODULE, + .open = ofl_open, + .release = ofl_release, + .unlocked_ioctl = video_ioctl2, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = video_ioctl2, +#endif +}; + +static const struct video_device offline_videodev = { + .name = "rkvpss-offline", + .vfl_dir = VFL_DIR_RX, + .fops = &offline_fops, + .ioctl_ops = &offline_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, +}; + +void rkvpss_offline_irq(struct rkvpss_hw_dev *hw, u32 irq) +{ + struct rkvpss_offline_dev *ofl = &hw->ofl_dev; + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s 0x%x\n", __func__, irq); + + if (!completion_done(&ofl->cmpl)) + complete(&ofl->cmpl); +} + +int rkvpss_register_offline(struct rkvpss_hw_dev *hw) +{ + struct rkvpss_offline_dev *ofl = &hw->ofl_dev; + struct v4l2_device *v4l2_dev; + struct video_device *vfd; + int ret; + + ofl->hw = hw; + v4l2_dev = &ofl->v4l2_dev; + strscpy(v4l2_dev->name, offline_videodev.name, sizeof(v4l2_dev->name)); + ret = v4l2_device_register(hw->dev, v4l2_dev); + if (ret) + return ret; + + mutex_init(&ofl->apilock); + ofl->vfd = offline_videodev; + vfd = &ofl->vfd; + vfd->device_caps = V4L2_CAP_STREAMING; + vfd->lock = &ofl->apilock; + vfd->v4l2_dev = v4l2_dev; + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); + if (ret) { + v4l2_err(v4l2_dev, "Failed to register video device\n"); + goto unreg_v4l2; + } + video_set_drvdata(vfd, ofl); + INIT_LIST_HEAD(&ofl->list); + return 0; +unreg_v4l2: + mutex_destroy(&ofl->apilock); + v4l2_device_unregister(v4l2_dev); + return ret; +} + +void rkvpss_unregister_offline(struct rkvpss_hw_dev *hw) +{ + mutex_destroy(&hw->ofl_dev.apilock); + video_unregister_device(&hw->ofl_dev.vfd); + v4l2_device_unregister(&hw->ofl_dev.v4l2_dev); +} diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline.h b/drivers/media/platform/rockchip/vpss/vpss_offline.h new file mode 100644 index 000000000000..9c718efccca6 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_offline.h @@ -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 diff --git a/include/uapi/linux/rk-vpss-config.h b/include/uapi/linux/rk-vpss-config.h new file mode 100644 index 000000000000..37644f2f874f --- /dev/null +++ b/include/uapi/linux/rk-vpss-config.h @@ -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 +#include + +#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