media: rockchip: vpss: init driver for rk3576
Change-Id: I8fd1af91d055d0ebd30bd9898db6305a51fd7dda Signed-off-by: Cai YiWei <cyw@rock-chips.com>
This commit is contained in:
parent
2cd86cca5e
commit
aae52d34ed
21 changed files with 6726 additions and 0 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -6,3 +6,4 @@ obj-y += ispp/
|
|||
obj-y += hdmirx/
|
||||
obj-y += rga/
|
||||
obj-y += rkisp1/
|
||||
obj-y += vpss/
|
||||
|
|
|
|||
12
drivers/media/platform/rockchip/vpss/Kconfig
Normal file
12
drivers/media/platform/rockchip/vpss/Kconfig
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
config VIDEO_ROCKCHIP_VPSS
|
||||
tristate "Rockchip Video Processing Sub System driver"
|
||||
depends on V4L_PLATFORM_DRIVERS
|
||||
depends on VIDEO_DEV
|
||||
depends on ARCH_ROCKCHIP || COMPILE_TEST
|
||||
depends on CPU_RK3576
|
||||
select VIDEO_V4L2_SUBDEV_API
|
||||
select VIDEOBUF2_CMA_SG
|
||||
default n
|
||||
help
|
||||
Support for VPSS on the rockchip SoC.
|
||||
10
drivers/media/platform/rockchip/vpss/Makefile
Normal file
10
drivers/media/platform/rockchip/vpss/Makefile
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_VIDEO_ROCKCHIP_VPSS) += video_rkvpss.o
|
||||
|
||||
video_rkvpss-objs += hw.o \
|
||||
dev.o \
|
||||
common.o \
|
||||
vpss.o \
|
||||
stream.o \
|
||||
procfs.o \
|
||||
vpss_offline.o
|
||||
133
drivers/media/platform/rockchip/vpss/common.c
Normal file
133
drivers/media/platform/rockchip/vpss/common.c
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (C) 2023 Rockchip Electronics Co., Ltd */
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "dev.h"
|
||||
#include "regs.h"
|
||||
|
||||
void rkvpss_write(struct rkvpss_device *dev, u32 reg, u32 val)
|
||||
{
|
||||
u32 *mem = dev->sw_base_addr + reg;
|
||||
u32 *flag = dev->sw_base_addr + reg + RKVPSS_SW_REG_SIZE;
|
||||
|
||||
*mem = val;
|
||||
*flag = RKVPSS_REG_CACHE;
|
||||
if (dev->hw_dev->is_single)
|
||||
rkvpss_hw_write(dev->hw_dev, reg, val);
|
||||
}
|
||||
|
||||
u32 rkvpss_read(struct rkvpss_device *dev, u32 reg)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (dev->hw_dev->is_single)
|
||||
val = rkvpss_hw_read(dev->hw_dev, reg);
|
||||
else
|
||||
val = *(u32 *)(dev->sw_base_addr + reg);
|
||||
return val;
|
||||
}
|
||||
|
||||
void rkvpss_set_bits(struct rkvpss_device *dev, u32 reg, u32 mask, u32 val)
|
||||
{
|
||||
u32 *mem = dev->sw_base_addr + reg;
|
||||
u32 *flag = dev->sw_base_addr + reg + RKVPSS_SW_REG_SIZE;
|
||||
|
||||
*mem &= ~mask;
|
||||
*mem |= val;
|
||||
*flag = RKVPSS_REG_CACHE;
|
||||
if (dev->hw_dev->is_single)
|
||||
rkvpss_hw_set_bits(dev->hw_dev, reg, mask, val);
|
||||
}
|
||||
|
||||
void rkvpss_clear_bits(struct rkvpss_device *dev, u32 reg, u32 mask)
|
||||
{
|
||||
u32 *mem = dev->sw_base_addr + reg;
|
||||
u32 *flag = dev->sw_base_addr + reg + RKVPSS_SW_REG_SIZE;
|
||||
|
||||
*mem &= ~mask;
|
||||
*flag = RKVPSS_REG_CACHE;
|
||||
if (dev->hw_dev->is_single)
|
||||
rkvpss_hw_clear_bits(dev->hw_dev, reg, mask);
|
||||
}
|
||||
|
||||
void rkvpss_update_regs(struct rkvpss_device *dev, u32 start, u32 end)
|
||||
{
|
||||
struct rkvpss_hw_dev *hw = dev->hw_dev;
|
||||
void __iomem *base = hw->base_addr;
|
||||
unsigned long lock_flags = 0;
|
||||
u32 i, j;
|
||||
|
||||
if (end > RKVPSS_SW_REG_SIZE - 4) {
|
||||
dev_err(dev->dev, "%s out of range\n", __func__);
|
||||
return;
|
||||
}
|
||||
for (i = start; i <= end; i += 4) {
|
||||
u32 *val = dev->sw_base_addr + i;
|
||||
u32 *flag = dev->sw_base_addr + i + RKVPSS_SW_REG_SIZE;
|
||||
|
||||
if (*flag != RKVPSS_REG_CACHE)
|
||||
continue;
|
||||
|
||||
if (IS_SYNC_REG(i)) {
|
||||
spin_lock_irqsave(&hw->reg_lock, lock_flags);
|
||||
if (i == RKVPSS_VPSS_ONLINE) {
|
||||
u32 mask = 0;
|
||||
|
||||
for (j = 0; j < RKVPSS_OUTPUT_MAX; j++) {
|
||||
if (!hw->is_ofl_ch[j])
|
||||
continue;
|
||||
mask |= (RKVPSS_ISP2VPSS_CHN0_SEL(3) << j * 2);
|
||||
}
|
||||
*val |= (readl(base + i) & mask);
|
||||
}
|
||||
}
|
||||
writel(*val, base + i);
|
||||
if (IS_SYNC_REG(i))
|
||||
spin_unlock_irqrestore(&hw->reg_lock, lock_flags);
|
||||
}
|
||||
}
|
||||
|
||||
int rkvpss_attach_hw(struct rkvpss_device *vpss)
|
||||
{
|
||||
struct device_node *np;
|
||||
struct platform_device *pdev;
|
||||
struct rkvpss_hw_dev *hw;
|
||||
|
||||
np = of_parse_phandle(vpss->dev->of_node, "rockchip,hw", 0);
|
||||
if (!np || !of_device_is_available(np)) {
|
||||
dev_err(vpss->dev, "failed to get vpss hw node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pdev = of_find_device_by_node(np);
|
||||
of_node_put(np);
|
||||
if (!pdev) {
|
||||
dev_err(vpss->dev, "failed to get vpss hw from node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hw = platform_get_drvdata(pdev);
|
||||
if (!hw) {
|
||||
dev_err(vpss->dev, "failed attach vpss hw\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hw->dev_num >= VPSS_MAX_DEV) {
|
||||
dev_err(vpss->dev, "failed attach vpss hw, max dev:%d\n", VPSS_MAX_DEV);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hw->dev_num)
|
||||
hw->is_single = false;
|
||||
vpss->dev_id = hw->dev_num;
|
||||
hw->vpss[hw->dev_num] = vpss;
|
||||
hw->dev_num++;
|
||||
vpss->hw_dev = hw;
|
||||
vpss->vpss_ver = hw->vpss_ver;
|
||||
|
||||
return 0;
|
||||
}
|
||||
93
drivers/media/platform/rockchip/vpss/common.h
Normal file
93
drivers/media/platform/rockchip/vpss/common.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#ifndef _RKVPSS_COMMON_H
|
||||
#define _RKVPSS_COMMON_H
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <media/media-device.h>
|
||||
#include <media/media-entity.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/videobuf2-v4l2.h>
|
||||
|
||||
#include "../isp/isp_vpss.h"
|
||||
|
||||
#define RKVPSS_PLANE_Y 0
|
||||
#define RKVPSS_PLANE_UV 1
|
||||
|
||||
#define RKVPSS_DEFAULT_WIDTH 1920
|
||||
#define RKVPSS_DEFAULT_HEIGHT 1080
|
||||
|
||||
#define RKVPSS_MAX_WIDTH 4672
|
||||
#define RKVPSS_MAX_HEIGHT 3504
|
||||
#define RKVPSS_MIN_WIDTH 32
|
||||
#define RKVPSS_MIN_HEIGHT 32
|
||||
#define RKVPSS_VIDEO_NAME_LEN 16
|
||||
|
||||
#define RKVPSS_REG_CACHE_SYNC 0xeeeeeeee
|
||||
#define RKVPSS_REG_CACHE 0xffffffff
|
||||
#define RKVPSS_SW_REG_SIZE 0x35c0
|
||||
#define RKVPSS_SW_REG_SIZE_MAX (RKVPSS_SW_REG_SIZE * 2)
|
||||
|
||||
struct rkvpss_device;
|
||||
|
||||
enum rkvpss_ver {
|
||||
VPSS_V10 = 0x00,
|
||||
};
|
||||
|
||||
enum rkvpss_fmt_pix_type {
|
||||
FMT_YUV,
|
||||
FMT_RGB,
|
||||
};
|
||||
|
||||
/* One structure per video node */
|
||||
struct rkvpss_vdev_node {
|
||||
struct vb2_queue buf_queue;
|
||||
struct video_device vdev;
|
||||
struct media_pad pad;
|
||||
};
|
||||
|
||||
struct rkvpss_buffer {
|
||||
struct vb2_v4l2_buffer vb;
|
||||
struct list_head queue;
|
||||
u32 dma[VIDEO_MAX_PLANES];
|
||||
void *vaddr[VIDEO_MAX_PLANES];
|
||||
};
|
||||
|
||||
static inline struct rkvpss_vdev_node *vdev_to_node(struct video_device *vdev)
|
||||
{
|
||||
return container_of(vdev, struct rkvpss_vdev_node, vdev);
|
||||
}
|
||||
|
||||
static inline struct rkvpss_vdev_node *queue_to_node(struct vb2_queue *q)
|
||||
{
|
||||
return container_of(q, struct rkvpss_vdev_node, buf_queue);
|
||||
}
|
||||
|
||||
static inline struct rkvpss_buffer *to_rkvpss_buffer(struct vb2_v4l2_buffer *vb)
|
||||
{
|
||||
return container_of(vb, struct rkvpss_buffer, vb);
|
||||
}
|
||||
|
||||
static inline struct vb2_queue *to_vb2_queue(struct file *file)
|
||||
{
|
||||
struct rkvpss_vdev_node *vnode = video_drvdata(file);
|
||||
|
||||
return &vnode->buf_queue;
|
||||
}
|
||||
|
||||
extern int rkvpss_debug;
|
||||
extern struct platform_driver rkvpss_plat_drv;
|
||||
|
||||
void rkvpss_write(struct rkvpss_device *dev, u32 reg, u32 val);
|
||||
void rkvpss_set_bits(struct rkvpss_device *dev, u32 reg, u32 mask, u32 val);
|
||||
u32 rkvpss_read(struct rkvpss_device *dev, u32 reg);
|
||||
void rkvpss_clear_bits(struct rkvpss_device *dev, u32 reg, u32 mask);
|
||||
void rkvpss_update_regs(struct rkvpss_device *dev, u32 start, u32 end);
|
||||
|
||||
int rkvpss_attach_hw(struct rkvpss_device *vpss);
|
||||
void rkvpss_set_clk_rate(struct clk *clk, unsigned long rate);
|
||||
#endif
|
||||
386
drivers/media/platform/rockchip/vpss/dev.c
Normal file
386
drivers/media/platform/rockchip/vpss/dev.c
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (C) 2023 Rockchip Electronics Co., Ltd */
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <media/v4l2-fwnode.h>
|
||||
|
||||
#include "dev.h"
|
||||
#include "regs.h"
|
||||
#include "version.h"
|
||||
|
||||
#define RKVPSS_VERNO_LEN 10
|
||||
|
||||
int rkvpss_debug;
|
||||
module_param_named(debug, rkvpss_debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "Debug level (0-3)");
|
||||
|
||||
static bool rkvpss_clk_dbg;
|
||||
module_param_named(clk_dbg, rkvpss_clk_dbg, bool, 0644);
|
||||
MODULE_PARM_DESC(clk_dbg, "rkvpss clk set by user");
|
||||
|
||||
static char rkvpss_version[RKVPSS_VERNO_LEN];
|
||||
module_param_string(version, rkvpss_version, RKVPSS_VERNO_LEN, 0444);
|
||||
MODULE_PARM_DESC(version, "version number");
|
||||
|
||||
void rkvpss_set_clk_rate(struct clk *clk, unsigned long rate)
|
||||
{
|
||||
if (rkvpss_clk_dbg)
|
||||
return;
|
||||
|
||||
clk_set_rate(clk, rate);
|
||||
}
|
||||
|
||||
void rkvpss_pipeline_default_fmt(struct rkvpss_device *dev)
|
||||
{
|
||||
u32 i, w, h;
|
||||
|
||||
w = dev->vpss_sdev.out_fmt.width;
|
||||
h = dev->vpss_sdev.out_fmt.height;
|
||||
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++)
|
||||
rkvpss_stream_default_fmt(dev, i, w, h, V4L2_PIX_FMT_NV12);
|
||||
}
|
||||
|
||||
int rkvpss_pipeline_open(struct rkvpss_device *dev)
|
||||
{
|
||||
if (atomic_inc_return(&dev->pipe_power_cnt) > 1)
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rkvpss_pipeline_close(struct rkvpss_device *dev)
|
||||
{
|
||||
if (atomic_dec_return(&dev->pipe_power_cnt))
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rkvpss_pipeline_stream(struct rkvpss_device *dev, bool on)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((on && atomic_inc_return(&dev->pipe_stream_cnt) > 1) ||
|
||||
(!on && atomic_dec_return(&dev->pipe_stream_cnt) > 0))
|
||||
return 0;
|
||||
|
||||
if (on) {
|
||||
ret = v4l2_subdev_call(&dev->vpss_sdev.sd, video, s_stream, true);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
ret = v4l2_subdev_call(dev->remote_sd, video, s_stream, true);
|
||||
if (ret < 0) {
|
||||
v4l2_subdev_call(&dev->vpss_sdev.sd, video, s_stream, false);
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
v4l2_subdev_call(dev->remote_sd, video, s_stream, false);
|
||||
v4l2_subdev_call(&dev->vpss_sdev.sd, video, s_stream, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
atomic_dec(&dev->pipe_stream_cnt);
|
||||
v4l2_err(&dev->v4l2_dev, "%s on:%d failed:%d\n", __func__, on, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rkvpss_create_links(struct rkvpss_device *dev)
|
||||
{
|
||||
struct rkvpss_stream_vdev *stream_vdev;
|
||||
struct media_entity *source, *sink;
|
||||
struct rkvpss_stream *stream;
|
||||
unsigned int flags = 0;
|
||||
int ret;
|
||||
|
||||
if (!dev->remote_sd)
|
||||
return -EINVAL;
|
||||
|
||||
/* input links */
|
||||
sink = &dev->vpss_sdev.sd.entity;
|
||||
ret = media_create_pad_link(&dev->remote_sd->entity, 0,
|
||||
sink, RKVPSS_PAD_SINK, MEDIA_LNK_FL_ENABLED);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
dev->inp = INP_ISP;
|
||||
|
||||
stream_vdev = &dev->stream_vdev;
|
||||
/* output stream links */
|
||||
flags = MEDIA_LNK_FL_ENABLED;
|
||||
source = &dev->vpss_sdev.sd.entity;
|
||||
|
||||
stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH0];
|
||||
stream->linked = flags;
|
||||
sink = &stream->vnode.vdev.entity;
|
||||
ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
|
||||
stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH1];
|
||||
stream->linked = flags;
|
||||
sink = &stream->vnode.vdev.entity;
|
||||
ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
|
||||
stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH2];
|
||||
stream->linked = flags;
|
||||
sink = &stream->vnode.vdev.entity;
|
||||
ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
|
||||
stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH3];
|
||||
stream->linked = flags;
|
||||
sink = &stream->vnode.vdev.entity;
|
||||
ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
rkvpss_subdev_notifier_complete(struct v4l2_async_notifier *notifier)
|
||||
{
|
||||
struct rkvpss_device *dev;
|
||||
int ret;
|
||||
|
||||
dev = container_of(notifier, struct rkvpss_device, notifier);
|
||||
mutex_lock(&dev->media_dev.graph_mutex);
|
||||
|
||||
ret = rkvpss_create_links(dev);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
ret = v4l2_device_register_subdev_nodes(&dev->v4l2_dev);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
rkvpss_pipeline_default_fmt(dev);
|
||||
v4l2_info(&dev->v4l2_dev, "Async subdev notifier completed\n");
|
||||
unlock:
|
||||
mutex_unlock(&dev->media_dev.graph_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rkvpss_subdev_notifier_bound(struct v4l2_async_notifier *notifier,
|
||||
struct v4l2_subdev *subdev,
|
||||
struct v4l2_async_subdev *asd)
|
||||
{
|
||||
struct rkvpss_device *dev;
|
||||
|
||||
dev = container_of(notifier, struct rkvpss_device, notifier);
|
||||
dev->remote_sd = subdev;
|
||||
v4l2_set_subdev_hostdata(subdev, &dev->vpss_sdev.sd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rkvpss_subdev_notifier_unbind(struct v4l2_async_notifier *notifier,
|
||||
struct v4l2_subdev *subdev,
|
||||
struct v4l2_async_subdev *asd)
|
||||
{
|
||||
struct rkvpss_device *dev;
|
||||
|
||||
dev = container_of(notifier, struct rkvpss_device, notifier);
|
||||
if (dev->remote_sd) {
|
||||
v4l2_set_subdev_hostdata(dev->remote_sd, NULL);
|
||||
dev->remote_sd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct
|
||||
v4l2_async_notifier_operations rkvpss_subdev_notifier_ops = {
|
||||
.bound = rkvpss_subdev_notifier_bound,
|
||||
.unbind = rkvpss_subdev_notifier_unbind,
|
||||
.complete = rkvpss_subdev_notifier_complete,
|
||||
};
|
||||
|
||||
static int rkvpss_subdev_notifier(struct rkvpss_device *dev)
|
||||
{
|
||||
struct v4l2_async_notifier *ntf = &dev->notifier;
|
||||
int ret;
|
||||
|
||||
v4l2_async_nf_init(ntf);
|
||||
ret = v4l2_async_nf_parse_fwnode_endpoints(dev->dev,
|
||||
ntf, sizeof(struct v4l2_async_subdev), NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ntf->ops = &rkvpss_subdev_notifier_ops;
|
||||
return v4l2_async_nf_register(&dev->v4l2_dev, ntf);
|
||||
}
|
||||
|
||||
static int rkvpss_register_platform_subdevs(struct rkvpss_device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rkvpss_register_subdev(dev, &dev->v4l2_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rkvpss_register_stream_vdevs(dev);
|
||||
if (ret < 0)
|
||||
goto err_unreg_vpss_subdev;
|
||||
|
||||
ret = rkvpss_subdev_notifier(dev);
|
||||
if (ret < 0)
|
||||
goto err_unreg_stream_vdevs;
|
||||
|
||||
return ret;
|
||||
err_unreg_stream_vdevs:
|
||||
rkvpss_unregister_stream_vdevs(dev);
|
||||
err_unreg_vpss_subdev:
|
||||
rkvpss_unregister_subdev(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id rkvpss_plat_of_match[] = {
|
||||
{
|
||||
.compatible = "rockchip,rkvpss-vir",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static int rkvpss_plat_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct v4l2_device *v4l2_dev;
|
||||
struct rkvpss_device *vpss_dev;
|
||||
int ret;
|
||||
|
||||
sprintf(rkvpss_version, "v%02x.%02x.%02x",
|
||||
RKVPSS_DRIVER_VERSION >> 16,
|
||||
(RKVPSS_DRIVER_VERSION & 0xff00) >> 8,
|
||||
RKVPSS_DRIVER_VERSION & 0x00ff);
|
||||
|
||||
dev_info(dev, "rkvpss driver version: %s\n", rkvpss_version);
|
||||
|
||||
vpss_dev = devm_kzalloc(dev, sizeof(*vpss_dev), GFP_KERNEL);
|
||||
if (!vpss_dev)
|
||||
return -ENOMEM;
|
||||
vpss_dev->sw_base_addr = devm_kzalloc(dev, RKVPSS_SW_REG_SIZE_MAX, GFP_KERNEL);
|
||||
if (!vpss_dev->sw_base_addr)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, vpss_dev);
|
||||
vpss_dev->dev = dev;
|
||||
|
||||
ret = rkvpss_attach_hw(vpss_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_init(&vpss_dev->apilock);
|
||||
|
||||
strscpy(vpss_dev->name, dev_name(dev), sizeof(vpss_dev->name));
|
||||
snprintf(vpss_dev->media_dev.model, sizeof(vpss_dev->media_dev.model),
|
||||
"%s%d", DRIVER_NAME, vpss_dev->dev_id);
|
||||
strscpy(vpss_dev->media_dev.driver_name, vpss_dev->name,
|
||||
sizeof(vpss_dev->media_dev.driver_name));
|
||||
vpss_dev->media_dev.dev = &pdev->dev;
|
||||
v4l2_dev = &vpss_dev->v4l2_dev;
|
||||
v4l2_dev->mdev = &vpss_dev->media_dev;
|
||||
strscpy(v4l2_dev->name, vpss_dev->name, sizeof(v4l2_dev->name));
|
||||
v4l2_ctrl_handler_init(&vpss_dev->ctrl_handler, 5);
|
||||
v4l2_dev->ctrl_handler = &vpss_dev->ctrl_handler;
|
||||
|
||||
ret = v4l2_device_register(vpss_dev->dev, v4l2_dev);
|
||||
if (ret < 0) {
|
||||
v4l2_err(v4l2_dev, "register v4l2 device failed:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
media_device_init(&vpss_dev->media_dev);
|
||||
ret = media_device_register(&vpss_dev->media_dev);
|
||||
if (ret < 0) {
|
||||
v4l2_err(v4l2_dev, "register media device failed:%d\n", ret);
|
||||
goto err_unreg_v4l2_dev;
|
||||
}
|
||||
|
||||
ret = rkvpss_register_platform_subdevs(vpss_dev);
|
||||
if (ret < 0)
|
||||
goto err_unreg_media_dev;
|
||||
|
||||
atomic_set(&vpss_dev->pipe_power_cnt, 0);
|
||||
atomic_set(&vpss_dev->pipe_stream_cnt, 0);
|
||||
rkvpss_proc_init(vpss_dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
vpss_dev->is_probe_end = true;
|
||||
return 0;
|
||||
|
||||
err_unreg_media_dev:
|
||||
media_device_unregister(&vpss_dev->media_dev);
|
||||
err_unreg_v4l2_dev:
|
||||
v4l2_device_unregister(&vpss_dev->v4l2_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rkvpss_plat_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rkvpss_device *vpss_dev = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
rkvpss_proc_cleanup(vpss_dev);
|
||||
rkvpss_unregister_subdev(vpss_dev);
|
||||
rkvpss_unregister_stream_vdevs(vpss_dev);
|
||||
|
||||
v4l2_ctrl_handler_free(&vpss_dev->ctrl_handler);
|
||||
v4l2_async_nf_unregister(&vpss_dev->notifier);
|
||||
v4l2_async_nf_cleanup(&vpss_dev->notifier);
|
||||
media_device_unregister(&vpss_dev->media_dev);
|
||||
v4l2_device_unregister(&vpss_dev->v4l2_dev);
|
||||
mutex_destroy(&vpss_dev->apilock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused rkvpss_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct rkvpss_device *vpss_dev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&vpss_dev->hw_dev->dev_lock);
|
||||
ret = pm_runtime_put_sync(vpss_dev->hw_dev->dev);
|
||||
mutex_unlock(&vpss_dev->hw_dev->dev_lock);
|
||||
return (ret > 0) ? 0 : ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused rkvpss_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct rkvpss_device *vpss_dev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&vpss_dev->hw_dev->dev_lock);
|
||||
ret = pm_runtime_get_sync(vpss_dev->hw_dev->dev);
|
||||
mutex_unlock(&vpss_dev->hw_dev->dev_lock);
|
||||
return (ret > 0) ? 0 : ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops rkvpss_plat_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(rkvpss_runtime_suspend,
|
||||
rkvpss_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
struct platform_driver rkvpss_plat_drv = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(rkvpss_plat_of_match),
|
||||
.pm = &rkvpss_plat_pm_ops,
|
||||
},
|
||||
.probe = rkvpss_plat_probe,
|
||||
.remove = rkvpss_plat_remove,
|
||||
};
|
||||
EXPORT_SYMBOL(rkvpss_plat_drv);
|
||||
|
||||
MODULE_AUTHOR("Rockchip Camera/ISP team");
|
||||
MODULE_DESCRIPTION("Rockchip VPSS platform driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(DMA_BUF);
|
||||
76
drivers/media/platform/rockchip/vpss/dev.h
Normal file
76
drivers/media/platform/rockchip/vpss/dev.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2023 Fuzhou Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#ifndef _RKVPSS_DEV_H
|
||||
#define _RKVPSS_DEV_H
|
||||
|
||||
#include <linux/rk-vpss-config.h>
|
||||
|
||||
#include "hw.h"
|
||||
#include "procfs.h"
|
||||
#include "stream.h"
|
||||
#include "vpss.h"
|
||||
|
||||
#define DRIVER_NAME "rkvpss"
|
||||
#define S0_VDEV_NAME DRIVER_NAME "_scale0"
|
||||
#define S1_VDEV_NAME DRIVER_NAME "_scale1"
|
||||
#define S2_VDEV_NAME DRIVER_NAME "_scale2"
|
||||
#define S3_VDEV_NAME DRIVER_NAME "_scale3"
|
||||
|
||||
enum rkvpss_input {
|
||||
INP_INVAL = 0,
|
||||
INP_ISP,
|
||||
};
|
||||
|
||||
enum {
|
||||
T_CMD_QUEUE,
|
||||
T_CMD_DEQUEUE,
|
||||
T_CMD_LEN,
|
||||
T_CMD_END,
|
||||
};
|
||||
|
||||
struct rkvpss_rdbk_info {
|
||||
u64 timestamp;
|
||||
u64 seq;
|
||||
};
|
||||
|
||||
struct rkvpss_device {
|
||||
char name[128];
|
||||
struct device *dev;
|
||||
void *sw_base_addr;
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct media_device media_dev;
|
||||
struct v4l2_async_notifier notifier;
|
||||
struct v4l2_ctrl_handler ctrl_handler;
|
||||
struct v4l2_subdev *remote_sd;
|
||||
|
||||
struct rkvpss_hw_dev *hw_dev;
|
||||
struct rkvpss_subdev vpss_sdev;
|
||||
struct rkvpss_stream_vdev stream_vdev;
|
||||
struct proc_dir_entry *procfs;
|
||||
|
||||
atomic_t pipe_power_cnt;
|
||||
atomic_t pipe_stream_cnt;
|
||||
|
||||
spinlock_t cmsc_lock;
|
||||
struct rkvpss_cmsc_cfg cmsc_cfg;
|
||||
|
||||
enum rkvpss_ver vpss_ver;
|
||||
/* mutex to serialize the calls from user */
|
||||
struct mutex apilock;
|
||||
enum rkvpss_input inp;
|
||||
u32 dev_id;
|
||||
u32 isr_cnt;
|
||||
u32 isr_err_cnt;
|
||||
|
||||
bool mir_en;
|
||||
bool cmsc_upd;
|
||||
|
||||
bool is_probe_end;
|
||||
};
|
||||
|
||||
void rkvpss_pipeline_default_fmt(struct rkvpss_device *dev);
|
||||
int rkvpss_pipeline_open(struct rkvpss_device *dev);
|
||||
int rkvpss_pipeline_close(struct rkvpss_device *dev);
|
||||
int rkvpss_pipeline_stream(struct rkvpss_device *dev, bool on);
|
||||
#endif
|
||||
898
drivers/media/platform/rockchip/vpss/hw.c
Normal file
898
drivers/media/platform/rockchip/vpss/hw.c
Normal file
|
|
@ -0,0 +1,898 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (C) 2023 Rockchip Electronics Co., Ltd */
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <media/videobuf2-cma-sg.h>
|
||||
#include <media/videobuf2-dma-sg.h>
|
||||
#include <soc/rockchip/rockchip_iommu.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "dev.h"
|
||||
#include "hw.h"
|
||||
#include "regs.h"
|
||||
|
||||
struct irqs_data {
|
||||
const char *name;
|
||||
irqreturn_t (*irq_hdl)(int irq, void *ctx);
|
||||
};
|
||||
|
||||
const s16 rkvpss_zme_tap8_coe[11][17][8] = {
|
||||
{//>=2.667
|
||||
{4, -12, 20, 488, 20, -12, 4, 0},
|
||||
{4, -8, 8, 484, 36, -16, 4, 0},
|
||||
{4, -4, -4, 476, 52, -20, 8, 0},
|
||||
{0, 0, -16, 480, 68, -28, 8, 0},
|
||||
{0, 4, -24, 472, 84, -32, 8, 0},
|
||||
{0, 4, -36, 468, 100, -36, 12, 0},
|
||||
{0, 8, -44, 456, 120, -40, 12, 0},
|
||||
{0, 12, -52, 448, 136, -44, 12, 0},
|
||||
{0, 12, -56, 436, 156, -48, 16, -4},
|
||||
{-4, 16, -60, 424, 176, -52, 16, -4},
|
||||
{-4, 16, -64, 412, 196, -56, 16, -4},
|
||||
{-4, 16, -68, 400, 216, -60, 16, -4},
|
||||
{-4, 20, -72, 380, 236, -64, 20, -4},
|
||||
{-4, 20, -72, 364, 256, -68, 20, -4},
|
||||
{-4, 20, -72, 348, 272, -68, 20, -4},
|
||||
{-4, 20, -72, 332, 292, -72, 20, -4},
|
||||
{-4, 20, -72, 312, 312, -72, 20, -4},
|
||||
},
|
||||
{//>=2
|
||||
{8, -24, 44, 456, 44, -24, 8, 0},
|
||||
{8, -20, 28, 460, 56, -28, 8, 0},
|
||||
{8, -16, 16, 452, 72, -32, 12, 0},
|
||||
{4, -12, 8, 448, 88, -36, 12, 0},
|
||||
{4, -8, -4, 444, 104, -40, 12, 0},
|
||||
{4, -8, -16, 444, 120, -44, 12, 0},
|
||||
{4, -4, -24, 432, 136, -48, 16, 0},
|
||||
{4, 0, -32, 428, 152, -52, 16, -4},
|
||||
{0, 4, -40, 424, 168, -56, 16, -4},
|
||||
{0, 4, -44, 412, 188, -60, 16, -4},
|
||||
{0, 8, -52, 400, 204, -60, 16, -4},
|
||||
{0, 8, -56, 388, 224, -64, 16, -4},
|
||||
{0, 12, -60, 372, 240, -64, 16, -4},
|
||||
{0, 12, -64, 356, 264, -68, 16, -4},
|
||||
{0, 12, -64, 340, 280, -68, 16, -4},
|
||||
{0, 16, -68, 324, 296, -68, 16, -4},
|
||||
{0, 16, -68, 308, 308, -68, 16, 0},
|
||||
},
|
||||
{//>=1.5
|
||||
{12, -32, 64, 424, 64, -32, 12, 0},
|
||||
{8, -32, 52, 432, 76, -36, 12, 0},
|
||||
{8, -28, 40, 432, 88, -40, 12, 0},
|
||||
{8, -24, 28, 428, 104, -44, 12, 0},
|
||||
{8, -20, 16, 424, 120, -48, 12, 0},
|
||||
{8, -16, 8, 416, 132, -48, 12, 0},
|
||||
{4, -16, -4, 420, 148, -52, 12, 0},
|
||||
{4, -12, -12, 412, 164, -56, 12, 0},
|
||||
{4, -8, -20, 400, 180, -56, 12, 0},
|
||||
{4, -4, -28, 388, 196, -56, 12, 0},
|
||||
{4, -4, -32, 380, 212, -60, 12, 0},
|
||||
{4, 0, -40, 368, 228, -60, 12, 0},
|
||||
{4, 0, -44, 356, 244, -60, 12, 0},
|
||||
{0, 4, -48, 344, 260, -60, 12, 0},
|
||||
{0, 4, -52, 332, 276, -60, 12, 0},
|
||||
{0, 8, -56, 320, 292, -60, 8, 0},
|
||||
{0, 8, -56, 304, 304, -56, 8, 0},
|
||||
},
|
||||
{//>1
|
||||
{12, -40, 84, 400, 84, -40, 12, 0},
|
||||
{12, -40, 72, 404, 96, -44, 12, 0},
|
||||
{12, -36, 60, 404, 108, -48, 12, 0},
|
||||
{8, -32, 48, 404, 120, -48, 12, 0},
|
||||
{8, -32, 36, 404, 136, -52, 12, 0},
|
||||
{8, -28, 28, 396, 148, -52, 12, 0},
|
||||
{8, -24, 16, 392, 160, -52, 12, 0},
|
||||
{8, -20, 8, 384, 176, -56, 12, 0},
|
||||
{8, -20, 0, 384, 188, -56, 8, 0},
|
||||
{8, -16, -8, 372, 204, -56, 8, 0},
|
||||
{8, -12, -16, 364, 216, -56, 8, 0},
|
||||
{4, -12, -20, 356, 232, -56, 8, 0},
|
||||
{4, -8, -28, 348, 244, -56, 8, 0},
|
||||
{4, -8, -32, 332, 264, -52, 4, 0},
|
||||
{4, -4, -36, 324, 272, -52, 4, 0},
|
||||
{4, 0, -40, 312, 280, -48, 0, 4},
|
||||
{4, 0, -44, 296, 296, -44, 0, 4},
|
||||
},
|
||||
{//==1
|
||||
{0, 0, 0, 511, 0, 0, 0, 0},
|
||||
{-1, 3, -12, 511, 14, -4, 1, 0},
|
||||
{-2, 6, -23, 509, 28, -8, 2, 0},
|
||||
{-2, 9, -33, 503, 44, -12, 3, 0},
|
||||
{-3, 11, -41, 496, 61, -16, 4, 0},
|
||||
{-3, 13, -48, 488, 79, -21, 5, -1},
|
||||
{-3, 14, -54, 477, 98, -25, 7, -2},
|
||||
{-4, 16, -59, 465, 118, -30, 8, -2},
|
||||
{-4, 17, -63, 451, 138, -35, 9, -1},
|
||||
{-4, 18, -66, 437, 158, -39, 10, -2},
|
||||
{-4, 18, -68, 421, 180, -44, 11, -2},
|
||||
{-4, 18, -69, 404, 201, -48, 13, -3},
|
||||
{-4, 18, -70, 386, 222, -52, 14, -2},
|
||||
{-4, 18, -70, 368, 244, -56, 15, -3},
|
||||
{-4, 18, -69, 348, 265, -59, 16, -3},
|
||||
{-4, 18, -67, 329, 286, -63, 16, -3},
|
||||
{-3, 17, -65, 307, 307, -65, 17, -3},
|
||||
},
|
||||
{//>=0.833
|
||||
{-16, 0, 145, 254, 145, 0, -16, 0},
|
||||
{-16, -2, 140, 253, 151, 3, -17, 0},
|
||||
{-15, -5, 135, 253, 157, 5, -18, 0},
|
||||
{-14, -7, 129, 252, 162, 8, -18, 0},
|
||||
{-13, -9, 123, 252, 167, 11, -19, 0},
|
||||
{-13, -11, 118, 250, 172, 15, -19, 0},
|
||||
{-12, -12, 112, 250, 177, 18, -20, -1},
|
||||
{-11, -14, 107, 247, 183, 21, -20, -1},
|
||||
{-10, -15, 101, 245, 188, 25, -21, -1},
|
||||
{-9, -16, 96, 243, 192, 29, -21, -2},
|
||||
{-8, -18, 90, 242, 197, 33, -22, -2},
|
||||
{-8, -19, 85, 239, 202, 37, -22, -2},
|
||||
{-7, -19, 80, 236, 206, 41, -22, -3},
|
||||
{-7, -20, 75, 233, 210, 46, -22, -3},
|
||||
{-6, -21, 69, 230, 215, 50, -22, -3},
|
||||
{-5, -21, 65, 226, 219, 55, -22, -5},
|
||||
{-5, -21, 60, 222, 222, 60, -21, -5},
|
||||
},
|
||||
{//>=0.7
|
||||
{-16, 0, 145, 254, 145, 0, -16, 0},
|
||||
{-16, -2, 140, 253, 151, 3, -17, 0},
|
||||
{-15, -5, 135, 253, 157, 5, -18, 0},
|
||||
{-14, -7, 129, 252, 162, 8, -18, 0},
|
||||
{-13, -9, 123, 252, 167, 11, -19, 0},
|
||||
{-13, -11, 118, 250, 172, 15, -19, 0},
|
||||
{-12, -12, 112, 250, 177, 18, -20, -1},
|
||||
{-11, -14, 107, 247, 183, 21, -20, -1},
|
||||
{-10, -15, 101, 245, 188, 25, -21, -1},
|
||||
{-9, -16, 96, 243, 192, 29, -21, -2},
|
||||
{-8, -18, 90, 242, 197, 33, -22, -2},
|
||||
{-8, -19, 85, 239, 202, 37, -22, -2},
|
||||
{-7, -19, 80, 236, 206, 41, -22, -3},
|
||||
{-7, -20, 75, 233, 210, 46, -22, -3},
|
||||
{-6, -21, 69, 230, 215, 50, -22, -3},
|
||||
{-5, -21, 65, 226, 219, 55, -22, -5},
|
||||
{-5, -21, 60, 222, 222, 60, -21, -5},
|
||||
},
|
||||
{//>=0.5
|
||||
{-16, 0, 145, 254, 145, 0, -16, 0},
|
||||
{-16, -2, 140, 253, 151, 3, -17, 0},
|
||||
{-15, -5, 135, 253, 157, 5, -18, 0},
|
||||
{-14, -7, 129, 252, 162, 8, -18, 0},
|
||||
{-13, -9, 123, 252, 167, 11, -19, 0},
|
||||
{-13, -11, 118, 250, 172, 15, -19, 0},
|
||||
{-12, -12, 112, 250, 177, 18, -20, -1},
|
||||
{-11, -14, 107, 247, 183, 21, -20, -1},
|
||||
{-10, -15, 101, 245, 188, 25, -21, -1},
|
||||
{-9, -16, 96, 243, 192, 29, -21, -2},
|
||||
{-8, -18, 90, 242, 197, 33, -22, -2},
|
||||
{-8, -19, 85, 239, 202, 37, -22, -2},
|
||||
{-7, -19, 80, 236, 206, 41, -22, -3},
|
||||
{-7, -20, 75, 233, 210, 46, -22, -3},
|
||||
{-6, -21, 69, 230, 215, 50, -22, -3},
|
||||
{-5, -21, 65, 226, 219, 55, -22, -5},
|
||||
{-5, -21, 60, 222, 222, 60, -21, -5},
|
||||
},
|
||||
{//>=0.33
|
||||
{-18, 18, 144, 226, 144, 19, -17, -4},
|
||||
{-17, 16, 139, 226, 148, 21, -17, -4},
|
||||
{-17, 13, 135, 227, 153, 24, -18, -5},
|
||||
{-17, 11, 131, 226, 157, 27, -18, -5},
|
||||
{-17, 9, 126, 225, 161, 30, -17, -5},
|
||||
{-16, 6, 122, 225, 165, 33, -17, -6},
|
||||
{-16, 4, 118, 224, 169, 37, -17, -7},
|
||||
{-16, 2, 113, 224, 173, 40, -17, -7},
|
||||
{-15, 0, 109, 222, 177, 43, -17, -7},
|
||||
{-15, -1, 104, 220, 181, 47, -16, -8},
|
||||
{-14, -3, 100, 218, 185, 51, -16, -9},
|
||||
{-14, -5, 96, 217, 188, 54, -15, -9},
|
||||
{-14, -6, 91, 214, 192, 58, -14, -9},
|
||||
{-13, -7, 87, 212, 195, 62, -14, -10},
|
||||
{-13, -9, 83, 210, 198, 66, -13, -10},
|
||||
{-12, -10, 79, 207, 201, 70, -12, -11},
|
||||
{-12, -11, 74, 205, 205, 74, -11, -12},
|
||||
},
|
||||
{//>=0.25
|
||||
{14, 66, 113, 133, 113, 66, 14, -7},
|
||||
{12, 65, 112, 133, 114, 68, 15, -7},
|
||||
{11, 63, 111, 132, 115, 70, 17, -7},
|
||||
{10, 62, 110, 132, 116, 71, 18, -7},
|
||||
{8, 60, 108, 132, 118, 73, 20, -7},
|
||||
{7, 58, 107, 132, 119, 75, 21, -7},
|
||||
{6, 56, 106, 132, 120, 76, 23, -7},
|
||||
{5, 55, 105, 131, 121, 78, 24, -7},
|
||||
{4, 53, 103, 131, 122, 80, 26, -7},
|
||||
{3, 51, 102, 131, 122, 81, 28, -6},
|
||||
{2, 50, 101, 130, 123, 83, 29, -6},
|
||||
{1, 48, 99, 131, 124, 84, 31, -6},
|
||||
{0, 46, 98, 129, 125, 86, 33, -5},
|
||||
{-1, 45, 97, 128, 126, 88, 34, -5},
|
||||
{-2, 43, 95, 130, 126, 89, 36, -5},
|
||||
{-3, 41, 94, 128, 127, 91, 38, -4},
|
||||
{-3, 39, 92, 128, 128, 92, 39, -3},
|
||||
},
|
||||
{//others
|
||||
{39, 69, 93, 102, 93, 69, 39, 8},
|
||||
{38, 68, 92, 102, 93, 70, 40, 9},
|
||||
{37, 67, 91, 102, 93, 71, 41, 10},
|
||||
{36, 66, 91, 101, 94, 71, 42, 11},
|
||||
{35, 65, 90, 102, 94, 72, 43, 11},
|
||||
{34, 64, 89, 102, 94, 73, 44, 12},
|
||||
{33, 63, 88, 101, 95, 74, 45, 13},
|
||||
{32, 62, 88, 100, 95, 75, 46, 14},
|
||||
{31, 62, 87, 100, 95, 75, 47, 15},
|
||||
{30, 61, 86, 99, 96, 76, 48, 16},
|
||||
{29, 60, 86, 98, 96, 77, 49, 17},
|
||||
{28, 59, 85, 98, 96, 78, 50, 18},
|
||||
{27, 58, 84, 99, 97, 78, 50, 19},
|
||||
{26, 57, 83, 99, 97, 79, 51, 20},
|
||||
{25, 56, 83, 98, 97, 80, 52, 21},
|
||||
{24, 55, 82, 97, 98, 81, 53, 22},
|
||||
{23, 54, 81, 98, 98, 81, 54, 23},
|
||||
}
|
||||
};
|
||||
|
||||
const s16 rkvpss_zme_tap6_coe[11][17][8] = {
|
||||
{//>=2.667
|
||||
{-12, 20, 492, 20, -12, 4, 0, 0},
|
||||
{-8, 8, 488, 36, -16, 4, 0, 0},
|
||||
{-4, -4, 488, 48, -20, 4, 0, 0},
|
||||
{0, -16, 484, 64, -24, 4, 0, 0},
|
||||
{0, -24, 476, 80, -28, 8, 0, 0},
|
||||
{4, -32, 464, 100, -32, 8, 0, 0},
|
||||
{8, -40, 456, 116, -36, 8, 0, 0},
|
||||
{8, -48, 448, 136, -40, 8, 0, 0},
|
||||
{12, -52, 436, 152, -44, 8, 0, 0},
|
||||
{12, -60, 424, 172, -48, 12, 0, 0},
|
||||
{12, -64, 412, 192, -52, 12, 0, 0},
|
||||
{16, -64, 392, 212, -56, 12, 0, 0},
|
||||
{16, -68, 380, 232, -60, 12, 0, 0},
|
||||
{16, -68, 360, 248, -60, 16, 0, 0},
|
||||
{16, -68, 344, 268, -64, 16, 0, 0},
|
||||
{16, -68, 328, 288, -68, 16, 0, 0},
|
||||
{16, -68, 308, 308, -68, 16, 0, 0},
|
||||
},
|
||||
{//>=2
|
||||
{-20, 40, 468, 40, -20, 4, 0, 0},
|
||||
{-16, 28, 464, 56, -24, 4, 0, 0},
|
||||
{-16, 16, 464, 68, -28, 8, 0, 0},
|
||||
{-12, 4, 460, 84, -32, 8, 0, 0},
|
||||
{-8, -4, 452, 100, -36, 8, 0, 0},
|
||||
{-4, -12, 444, 116, -40, 8, 0, 0},
|
||||
{-4, -24, 440, 136, -44, 8, 0, 0},
|
||||
{0, -32, 432, 152, -48, 8, 0, 0},
|
||||
{0, -36, 416, 168, -48, 12, 0, 0},
|
||||
{4, -44, 408, 184, -52, 12, 0, 0},
|
||||
{4, -48, 400, 200, -56, 12, 0, 0},
|
||||
{8, -52, 380, 220, -56, 12, 0, 0},
|
||||
{8, -56, 372, 236, -60, 12, 0, 0},
|
||||
{8, -60, 356, 256, -60, 12, 0, 0},
|
||||
{12, -60, 340, 268, -60, 12, 0, 0},
|
||||
{12, -60, 324, 288, -64, 12, 0, 0},
|
||||
{12, -64, 308, 308, -64, 12, 0, 0},
|
||||
},
|
||||
{//>=1.5
|
||||
{-28, 60, 440, 60, -28, 8, 0, 0},
|
||||
{-28, 48, 440, 76, -32, 8, 0, 0},
|
||||
{-24, 36, 440, 88, -36, 8, 0, 0},
|
||||
{-20, 28, 432, 104, -40, 8, 0, 0},
|
||||
{-16, 16, 428, 116, -40, 8, 0, 0},
|
||||
{-16, 4, 428, 132, -44, 8, 0, 0},
|
||||
{-12, -4, 420, 148, -48, 8, 0, 0},
|
||||
{-8, -12, 408, 164, -48, 8, 0, 0},
|
||||
{-8, -20, 404, 180, -52, 8, 0, 0},
|
||||
{-4, -24, 388, 196, -52, 8, 0, 0},
|
||||
{-4, -32, 384, 212, -56, 8, 0, 0},
|
||||
{0, -36, 372, 224, -56, 8, 0, 0},
|
||||
{0, -40, 360, 240, -56, 8, 0, 0},
|
||||
{4, -44, 344, 256, -56, 8, 0, 0},
|
||||
{4, -48, 332, 272, -56, 8, 0, 0},
|
||||
{4, -52, 316, 292, -56, 8, 0, 0},
|
||||
{8, -52, 300, 300, -52, 8, 0, 0},
|
||||
},
|
||||
{//>1
|
||||
{-36, 80, 420, 80, -36, 4, 0, 0},
|
||||
{-32, 68, 412, 92, -36, 8, 0, 0},
|
||||
{-28, 56, 412, 104, -40, 8, 0, 0},
|
||||
{-28, 44, 412, 116, -40, 8, 0, 0},
|
||||
{-24, 36, 404, 132, -44, 8, 0, 0},
|
||||
{-24, 24, 404, 144, -44, 8, 0, 0},
|
||||
{-20, 16, 396, 160, -48, 8, 0, 0},
|
||||
{-16, 8, 388, 172, -48, 8, 0, 0},
|
||||
{-16, 0, 380, 188, -48, 8, 0, 0},
|
||||
{-12, -8, 376, 200, -48, 4, 0, 0},
|
||||
{-12, -12, 364, 216, -48, 4, 0, 0},
|
||||
{-8, -20, 356, 228, -48, 4, 0, 0},
|
||||
{-8, -24, 344, 244, -48, 4, 0, 0},
|
||||
{-4, -32, 332, 260, -48, 4, 0, 0},
|
||||
{-4, -36, 320, 272, -44, 4, 0, 0},
|
||||
{0, -40, 308, 288, -44, 0, 0, 0},
|
||||
{0, -40, 296, 296, -40, 0, 0, 0},
|
||||
},
|
||||
{//==1
|
||||
{0, 0, 511, 0, 0, 0, 0, 0},
|
||||
{3, -12, 511, 13, -3, 0, 0, 0},
|
||||
{6, -22, 507, 28, -7, 0, 0, 0},
|
||||
{8, -32, 502, 44, -11, 1, 0, 0},
|
||||
{10, -40, 495, 61, -15, 1, 0, 0},
|
||||
{11, -47, 486, 79, -19, 2, 0, 0},
|
||||
{12, -53, 476, 98, -24, 3, 0, 0},
|
||||
{13, -58, 464, 117, -28, 4, 0, 0},
|
||||
{14, -62, 451, 137, -33, 5, 0, 0},
|
||||
{15, -65, 437, 157, -38, 6, 0, 0},
|
||||
{15, -67, 420, 179, -42, 7, 0, 0},
|
||||
{15, -68, 404, 200, -46, 7, 0, 0},
|
||||
{14, -68, 386, 221, -50, 9, 0, 0},
|
||||
{14, -68, 367, 243, -54, 10, 0, 0},
|
||||
{14, -67, 348, 264, -58, 11, 0, 0},
|
||||
{13, -66, 328, 286, -61, 12, 0, 0},
|
||||
{13, -63, 306, 306, -63, 13, 0, 0},
|
||||
},
|
||||
{//>=0.833
|
||||
{-31, 104, 362, 104, -31, 4, 0, 0},
|
||||
{-30, 94, 362, 114, -32, 4, 0, 0},
|
||||
{-29, 84, 361, 125, -32, 3, 0, 0},
|
||||
{-28, 75, 359, 136, -33, 3, 0, 0},
|
||||
{-27, 66, 356, 147, -33, 3, 0, 0},
|
||||
{-25, 57, 353, 158, -33, 2, 0, 0},
|
||||
{-24, 49, 349, 169, -33, 2, 0, 0},
|
||||
{-22, 41, 344, 180, -32, 1, 0, 0},
|
||||
{-20, 33, 339, 191, -31, 0, 0, 0},
|
||||
{-19, 26, 333, 203, -30, -1, 0, 0},
|
||||
{-17, 19, 327, 214, -29, -2, 0, 0},
|
||||
{-16, 13, 320, 225, -27, -3, 0, 0},
|
||||
{-14, 7, 312, 236, -25, -4, 0, 0},
|
||||
{-13, 1, 305, 246, -22, -5, 0, 0},
|
||||
{-11, -4, 295, 257, -19, -6, 0, 0},
|
||||
{-10, -8, 286, 267, -16, -7, 0, 0},
|
||||
{-9, -12, 277, 277, -12, -9, 0, 0},
|
||||
},
|
||||
{//>=0.7
|
||||
{-31, 104, 362, 104, -31, 4, 0, 0},
|
||||
{-30, 94, 362, 114, -32, 4, 0, 0},
|
||||
{-29, 84, 361, 125, -32, 3, 0, 0},
|
||||
{-28, 75, 359, 136, -33, 3, 0, 0},
|
||||
{-27, 66, 356, 147, -33, 3, 0, 0},
|
||||
{-25, 57, 353, 158, -33, 2, 0, 0},
|
||||
{-24, 49, 349, 169, -33, 2, 0, 0},
|
||||
{-22, 41, 344, 180, -32, 1, 0, 0},
|
||||
{-20, 33, 339, 191, -31, 0, 0, 0},
|
||||
{-19, 26, 333, 203, -30, -1, 0, 0},
|
||||
{-17, 19, 327, 214, -29, -2, 0, 0},
|
||||
{-16, 13, 320, 225, -27, -3, 0, 0},
|
||||
{-14, 7, 312, 236, -25, -4, 0, 0},
|
||||
{-13, 1, 305, 246, -22, -5, 0, 0},
|
||||
{-11, -4, 295, 257, -19, -6, 0, 0},
|
||||
{-10, -8, 286, 267, -16, -7, 0, 0},
|
||||
{-9, -12, 277, 277, -12, -9, 0, 0},
|
||||
},
|
||||
{//>=0.5
|
||||
{ -20, 130, 297, 130, -20, -5, 0, 0},
|
||||
{ -21, 122, 298, 138, -19, -6, 0, 0},
|
||||
{ -22, 115, 297, 146, -17, -7, 0, 0},
|
||||
{ -22, 108, 296, 153, -16, -7, 0, 0},
|
||||
{ -23, 101, 295, 161, -14, -8, 0, 0},
|
||||
{ -23, 93, 294, 169, -12, -9, 0, 0},
|
||||
{ -24, 87, 292, 177, -10, -10, 0, 0},
|
||||
{ -24, 80, 289, 185, -7, -11, 0, 0},
|
||||
{ -24, 73, 286, 193, -4, -12, 0, 0},
|
||||
{ -23, 66, 283, 200, -1, -13, 0, 0},
|
||||
{ -23, 60, 279, 208, 2, -14, 0, 0},
|
||||
{ -23, 54, 276, 215, 5, -15, 0, 0},
|
||||
{ -22, 48, 271, 222, 9, -16, 0, 0},
|
||||
{ -21, 42, 266, 229, 13, -17, 0, 0},
|
||||
{ -21, 37, 261, 236, 17, -18, 0, 0},
|
||||
{ -21, 32, 255, 242, 22, -18, 0, 0},
|
||||
{ -20, 27, 249, 249, 27, -20, 0, 0},
|
||||
},
|
||||
{//>=0.33
|
||||
{16, 136, 217, 136, 16, -9, 0, 0},
|
||||
{13, 132, 217, 141, 18, -9, 0, 0},
|
||||
{11, 128, 217, 145, 21, -10, 0, 0},
|
||||
{9, 124, 216, 149, 24, -10, 0, 0},
|
||||
{7, 119, 216, 153, 27, -10, 0, 0},
|
||||
{5, 115, 216, 157, 30, -11, 0, 0},
|
||||
{3, 111, 215, 161, 33, -11, 0, 0},
|
||||
{1, 107, 214, 165, 36, -11, 0, 0},
|
||||
{0, 102, 213, 169, 39, -11, 0, 0},
|
||||
{-2, 98, 211, 173, 43, -11, 0, 0},
|
||||
{-3, 94, 209, 177, 46, -11, 0, 0},
|
||||
{-4, 90, 207, 180, 50, -11, 0, 0},
|
||||
{-5, 85, 206, 184, 53, -11, 0, 0},
|
||||
{-6, 81, 203, 187, 57, -10, 0, 0},
|
||||
{-7, 77, 201, 190, 61, -10, 0, 0},
|
||||
{-8, 73, 198, 193, 65, -9, 0, 0},
|
||||
{-9, 69, 196, 196, 69, -9, 0, 0},
|
||||
},
|
||||
{//>=0.25
|
||||
{66, 115, 138, 115, 66, 12, 0, 0},
|
||||
{64, 114, 136, 116, 68, 14, 0, 0},
|
||||
{63, 113, 134, 117, 70, 15, 0, 0},
|
||||
{61, 111, 135, 118, 71, 16, 0, 0},
|
||||
{59, 110, 133, 119, 73, 18, 0, 0},
|
||||
{57, 108, 134, 120, 74, 19, 0, 0},
|
||||
{55, 107, 133, 121, 76, 20, 0, 0},
|
||||
{53, 105, 133, 121, 78, 22, 0, 0},
|
||||
{51, 104, 133, 122, 79, 23, 0, 0},
|
||||
{49, 102, 132, 123, 81, 25, 0, 0},
|
||||
{47, 101, 132, 124, 82, 26, 0, 0},
|
||||
{45, 99, 131, 125, 84, 28, 0, 0},
|
||||
{44, 98, 130, 125, 85, 30, 0, 0},
|
||||
{42, 96, 130, 126, 87, 31, 0, 0},
|
||||
{40, 95, 128, 127, 89, 33, 0, 0},
|
||||
{38, 93, 129, 127, 90, 35, 0, 0},
|
||||
{36, 92, 128, 128, 92, 36, 0, 0},
|
||||
},
|
||||
{//others
|
||||
{80, 105, 116, 105, 80, 26, 0, 0},
|
||||
{79, 104, 115, 105, 81, 28, 0, 0},
|
||||
{77, 103, 116, 106, 81, 29, 0, 0},
|
||||
{76, 102, 115, 106, 82, 31, 0, 0},
|
||||
{74, 101, 115, 106, 83, 33, 0, 0},
|
||||
{73, 100, 114, 106, 84, 35, 0, 0},
|
||||
{71, 99, 114, 107, 84, 37, 0, 0},
|
||||
{70, 98, 113, 107, 85, 39, 0, 0},
|
||||
{68, 98, 113, 107, 86, 40, 0, 0},
|
||||
{67, 97, 112, 108, 86, 42, 0, 0},
|
||||
{65, 96, 112, 108, 87, 44, 0, 0},
|
||||
{63, 95, 112, 108, 88, 46, 0, 0},
|
||||
{62, 94, 112, 108, 88, 48, 0, 0},
|
||||
{60, 93, 111, 109, 89, 50, 0, 0},
|
||||
{58, 93, 111, 109, 90, 51, 0, 0},
|
||||
{57, 92, 110, 110, 90, 53, 0, 0},
|
||||
{55, 91, 110, 110, 91, 55, 0, 0},
|
||||
}
|
||||
};
|
||||
|
||||
void rkvpss_hw_write(struct rkvpss_hw_dev *hw, u32 reg, u32 val)
|
||||
{
|
||||
unsigned long lock_flags = 0;
|
||||
|
||||
if (IS_SYNC_REG(reg))
|
||||
spin_lock_irqsave(&hw->reg_lock, lock_flags);
|
||||
writel(val, hw->base_addr + reg);
|
||||
if (IS_SYNC_REG(reg))
|
||||
spin_unlock_irqrestore(&hw->reg_lock, lock_flags);
|
||||
}
|
||||
|
||||
u32 rkvpss_hw_read(struct rkvpss_hw_dev *hw, u32 reg)
|
||||
{
|
||||
unsigned long lock_flags = 0;
|
||||
u32 val;
|
||||
|
||||
if (IS_SYNC_REG(reg))
|
||||
spin_lock_irqsave(&hw->reg_lock, lock_flags);
|
||||
val = readl(hw->base_addr + reg);
|
||||
if (IS_SYNC_REG(reg))
|
||||
spin_unlock_irqrestore(&hw->reg_lock, lock_flags);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void rkvpss_hw_set_bits(struct rkvpss_hw_dev *hw, u32 reg, u32 mask, u32 val)
|
||||
{
|
||||
unsigned long lock_flags = 0;
|
||||
u32 tmp;
|
||||
|
||||
if (IS_SYNC_REG(reg))
|
||||
spin_lock_irqsave(&hw->reg_lock, lock_flags);
|
||||
tmp = readl(hw->base_addr + reg) & ~mask;
|
||||
writel(val | tmp, hw->base_addr + reg);
|
||||
if (IS_SYNC_REG(reg))
|
||||
spin_unlock_irqrestore(&hw->reg_lock, lock_flags);
|
||||
}
|
||||
|
||||
void rkvpss_hw_clear_bits(struct rkvpss_hw_dev *hw, u32 reg, u32 mask)
|
||||
{
|
||||
unsigned long lock_flags = 0;
|
||||
u32 val;
|
||||
|
||||
if (IS_SYNC_REG(reg))
|
||||
spin_lock_irqsave(&hw->reg_lock, lock_flags);
|
||||
val = readl(hw->base_addr + reg) & ~mask;
|
||||
writel(val, hw->base_addr + reg);
|
||||
if (IS_SYNC_REG(reg))
|
||||
spin_unlock_irqrestore(&hw->reg_lock, lock_flags);
|
||||
}
|
||||
|
||||
void rkvpss_soft_reset(struct rkvpss_hw_dev *hw)
|
||||
{
|
||||
writel(RKVPSS_SOFT_RST, hw->base_addr + RKVPSS_VPSS_RESET);
|
||||
if (hw->reset) {
|
||||
reset_control_assert(hw->reset);
|
||||
udelay(20);
|
||||
reset_control_deassert(hw->reset);
|
||||
udelay(20);
|
||||
}
|
||||
|
||||
/* refresh iommu after reset */
|
||||
if (hw->is_mmu) {
|
||||
rockchip_iommu_disable(hw->dev);
|
||||
rockchip_iommu_enable(hw->dev);
|
||||
}
|
||||
|
||||
rkvpss_hw_write(hw, RKVPSS_VPSS_CTRL, RKVPSS_ACK_FRM_PRO_DIS);
|
||||
rkvpss_hw_write(hw, RKVPSS_VPSS_IRQ_CFG, 0x3fff);
|
||||
rkvpss_hw_write(hw, RKVPSS_MI_IMSC, 0xd0000000);
|
||||
}
|
||||
|
||||
int rkvpss_get_zme_tap_coe_index(int ratio)
|
||||
{
|
||||
int idx;
|
||||
|
||||
if (ratio >= 26670)
|
||||
idx = 0;
|
||||
else if (ratio >= 20000)
|
||||
idx = 1;
|
||||
else if (ratio >= 15000)
|
||||
idx = 2;
|
||||
else if (ratio > 10000)
|
||||
idx = 3;
|
||||
else if (ratio == 10000)
|
||||
idx = 4;
|
||||
else if (ratio >= 8333)
|
||||
idx = 5;
|
||||
else if (ratio >= 7000)
|
||||
idx = 6;
|
||||
else if (ratio >= 5000)
|
||||
idx = 7;
|
||||
else if (ratio >= 3300)
|
||||
idx = 8;
|
||||
else if (ratio >= 2500)
|
||||
idx = 9;
|
||||
else
|
||||
idx = 10;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
void rkvpss_cmsc_slop(struct rkvpss_cmsc_point *p0,
|
||||
struct rkvpss_cmsc_point *p1,
|
||||
int *k, int *hor)
|
||||
{
|
||||
int x0 = p0->x, y0 = p0->y;
|
||||
int x1 = p1->x, y1 = p1->y;
|
||||
int i, slp, sign_flag = 0, slp_bit = 13;
|
||||
int highest1 = 0, k_fix, pow;
|
||||
|
||||
if (y0 == y1) {
|
||||
/* horizontal line */
|
||||
*hor = 1;
|
||||
slp = 0;
|
||||
} else {
|
||||
*hor = 0;
|
||||
slp = (int)((x1 - x0) * (1 << slp_bit) / (y1 - y0));
|
||||
if (slp < 0) {
|
||||
sign_flag = 1;
|
||||
slp = -slp;
|
||||
}
|
||||
slp = min_t(int, slp, (1 << slp_bit * 2) - 1);
|
||||
}
|
||||
|
||||
for (i = slp_bit * 2 - 1; i >= 0; i--) {
|
||||
if (slp >> i) {
|
||||
highest1 = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (highest1 >= slp_bit) {
|
||||
pow = highest1 - slp_bit + 1;
|
||||
k_fix = slp >> pow;
|
||||
} else {
|
||||
pow = 0;
|
||||
k_fix = slp;
|
||||
}
|
||||
*k = (sign_flag << (slp_bit + 4)) + (pow << slp_bit) + k_fix;
|
||||
}
|
||||
|
||||
static inline bool is_iommu_enable(struct device *dev)
|
||||
{
|
||||
struct device_node *iommu;
|
||||
|
||||
iommu = of_parse_phandle(dev->of_node, "iommus", 0);
|
||||
if (!iommu) {
|
||||
dev_info(dev, "no iommu attached, using non-iommu buffers\n");
|
||||
return false;
|
||||
} else if (!of_device_is_available(iommu)) {
|
||||
dev_info(dev, "iommu is disabled, using non-iommu buffers\n");
|
||||
of_node_put(iommu);
|
||||
return false;
|
||||
}
|
||||
of_node_put(iommu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void disable_sys_clk(struct rkvpss_hw_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->clks_num; i++)
|
||||
clk_disable_unprepare(dev->clks[i]);
|
||||
}
|
||||
|
||||
static int enable_sys_clk(struct rkvpss_hw_dev *dev)
|
||||
{
|
||||
int i, ret = -EINVAL;
|
||||
|
||||
for (i = 0; i < dev->clks_num; i++) {
|
||||
ret = clk_prepare_enable(dev->clks[i]);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
return 0;
|
||||
err:
|
||||
for (--i; i >= 0; --i)
|
||||
clk_disable_unprepare(dev->clks[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t vpss_irq_hdl(int irq, void *ctx)
|
||||
{
|
||||
struct device *dev = ctx;
|
||||
struct rkvpss_hw_dev *hw_dev = dev_get_drvdata(dev);
|
||||
struct rkvpss_device *vpss = hw_dev->vpss[hw_dev->cur_dev_id];
|
||||
unsigned int mis_val;
|
||||
|
||||
mis_val = rkvpss_hw_read(hw_dev, RKVPSS_VPSS_MIS);
|
||||
if (mis_val) {
|
||||
rkvpss_hw_write(hw_dev, RKVPSS_VPSS_ICR, mis_val);
|
||||
if (mis_val & RKVPSS_ISP_ALL_FRM_END)
|
||||
rkvpss_isr(vpss, mis_val);
|
||||
if (mis_val & RKVPSS_ALL_FRM_END)
|
||||
rkvpss_offline_irq(hw_dev, mis_val);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t mi_irq_hdl(int irq, void *ctx)
|
||||
{
|
||||
struct device *dev = ctx;
|
||||
struct rkvpss_hw_dev *hw_dev = dev_get_drvdata(dev);
|
||||
struct rkvpss_device *vpss = hw_dev->vpss[hw_dev->cur_dev_id];
|
||||
unsigned int mis_val;
|
||||
|
||||
mis_val = rkvpss_hw_read(hw_dev, RKVPSS_MI_MIS);
|
||||
if (mis_val) {
|
||||
rkvpss_hw_write(hw_dev, RKVPSS_MI_ICR, mis_val);
|
||||
rkvpss_mi_isr(vpss, mis_val);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct irqs_data vpss_irqs[] = {
|
||||
{"vpss_irq", vpss_irq_hdl},
|
||||
{"mi_irq", mi_irq_hdl},
|
||||
};
|
||||
|
||||
static const char * const vpss_clks[] = {
|
||||
"clk_vpss",
|
||||
"aclk_vpss",
|
||||
"hclk_vpss",
|
||||
};
|
||||
|
||||
static const struct vpss_match_data rk3576_vpss_match_data = {
|
||||
.irqs = vpss_irqs,
|
||||
.num_irqs = ARRAY_SIZE(vpss_irqs),
|
||||
.clks = vpss_clks,
|
||||
.clks_num = ARRAY_SIZE(vpss_clks),
|
||||
.vpss_ver = VPSS_V10,
|
||||
};
|
||||
|
||||
static const struct of_device_id rkvpss_hw_of_match[] = {
|
||||
{
|
||||
.compatible = "rockchip,rk3576-rkvpss",
|
||||
.data = &rk3576_vpss_match_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static int rkvpss_hw_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
const struct vpss_match_data *match_data;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rkvpss_hw_dev *hw_dev;
|
||||
struct resource *res;
|
||||
int i, ret, irq;
|
||||
bool is_mem_reserved = true;
|
||||
|
||||
match = of_match_node(rkvpss_hw_of_match, node);
|
||||
if (IS_ERR(match))
|
||||
return PTR_ERR(match);
|
||||
|
||||
hw_dev = devm_kzalloc(dev, sizeof(*hw_dev), GFP_KERNEL);
|
||||
if (!hw_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, hw_dev);
|
||||
hw_dev->dev = dev;
|
||||
match_data = match->data;
|
||||
hw_dev->match_data = match->data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "get resource failed\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
hw_dev->base_addr = devm_ioremap_resource(dev, res);
|
||||
if (PTR_ERR(hw_dev->base_addr) == -EBUSY) {
|
||||
resource_size_t offset = res->start;
|
||||
resource_size_t size = resource_size(res);
|
||||
|
||||
hw_dev->base_addr = devm_ioremap(dev, offset, size);
|
||||
}
|
||||
if (IS_ERR(hw_dev->base_addr)) {
|
||||
dev_err(dev, "ioremap failed\n");
|
||||
ret = PTR_ERR(hw_dev->base_addr);
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; i < match_data->num_irqs; i++) {
|
||||
irq = platform_get_irq_byname(pdev, match_data->irqs[i].name);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "no irq %s in dts\n", match_data->irqs[i].name);
|
||||
ret = irq;
|
||||
goto err;
|
||||
}
|
||||
ret = devm_request_irq(dev, irq,
|
||||
match_data->irqs[i].irq_hdl,
|
||||
IRQF_SHARED,
|
||||
dev_driver_string(dev),
|
||||
dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "request %s failed: %d\n",
|
||||
match_data->irqs[i].name, ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < match_data->clks_num; i++) {
|
||||
struct clk *clk = devm_clk_get(dev, match_data->clks[i]);
|
||||
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "failed to get %s\n",
|
||||
match_data->clks[i]);
|
||||
ret = PTR_ERR(clk);
|
||||
goto err;
|
||||
}
|
||||
hw_dev->clks[i] = clk;
|
||||
}
|
||||
hw_dev->clks_num = match_data->clks_num;
|
||||
|
||||
hw_dev->reset = devm_reset_control_array_get(dev, false, false);
|
||||
if (IS_ERR(hw_dev->reset)) {
|
||||
dev_info(dev, "failed to get cru reset\n");
|
||||
hw_dev->reset = NULL;
|
||||
}
|
||||
|
||||
hw_dev->dev_num = 0;
|
||||
hw_dev->cur_dev_id = 0;
|
||||
hw_dev->pre_dev_id = -1;
|
||||
hw_dev->vpss_ver = match_data->vpss_ver;
|
||||
mutex_init(&hw_dev->dev_lock);
|
||||
spin_lock_init(&hw_dev->reg_lock);
|
||||
atomic_set(&hw_dev->refcnt, 0);
|
||||
INIT_LIST_HEAD(&hw_dev->list);
|
||||
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++)
|
||||
hw_dev->is_ofl_ch[i] = false;
|
||||
hw_dev->is_ofl_cmsc = false;
|
||||
hw_dev->is_single = true;
|
||||
hw_dev->is_dma_contig = true;
|
||||
hw_dev->is_shutdown = false;
|
||||
hw_dev->is_mmu = is_iommu_enable(dev);
|
||||
ret = of_reserved_mem_device_init(dev);
|
||||
if (ret) {
|
||||
is_mem_reserved = false;
|
||||
if (!hw_dev->is_mmu)
|
||||
dev_info(dev, "No reserved memory region. default cma area!\n");
|
||||
}
|
||||
if (hw_dev->is_mmu && !is_mem_reserved)
|
||||
hw_dev->is_dma_contig = false;
|
||||
hw_dev->mem_ops = &vb2_cma_sg_memops;
|
||||
|
||||
rkvpss_register_offline(hw_dev);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rkvpss_hw_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rkvpss_hw_dev *hw_dev = platform_get_drvdata(pdev);
|
||||
|
||||
rkvpss_unregister_offline(hw_dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
mutex_destroy(&hw_dev->dev_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rkvpss_hw_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct rkvpss_hw_dev *hw_dev = platform_get_drvdata(pdev);
|
||||
|
||||
hw_dev->is_shutdown = true;
|
||||
if (pm_runtime_active(&pdev->dev)) {
|
||||
rkvpss_hw_write(hw_dev, RKVPSS_VPSS_IMSC, 0);
|
||||
rkvpss_hw_write(hw_dev, RKVPSS_MI_IMSC, 0);
|
||||
rkvpss_hw_write(hw_dev, RKVPSS_VPSS_RESET, RKVPSS_SOFT_RST);
|
||||
}
|
||||
}
|
||||
|
||||
static int __maybe_unused rkvpss_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct rkvpss_hw_dev *hw_dev = dev_get_drvdata(dev);
|
||||
|
||||
rkvpss_hw_write(hw_dev, RKVPSS_MI_IMSC, 0);
|
||||
rkvpss_hw_write(hw_dev, RKVPSS_VPSS_IMSC, 0);
|
||||
disable_sys_clk(hw_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused rkvpss_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct rkvpss_hw_dev *hw_dev = dev_get_drvdata(dev);
|
||||
void __iomem *base = hw_dev->base_addr;
|
||||
int i;
|
||||
|
||||
enable_sys_clk(hw_dev);
|
||||
rkvpss_soft_reset(hw_dev);
|
||||
|
||||
for (i = 0; i < hw_dev->dev_num; i++) {
|
||||
void *buf = hw_dev->vpss[i]->sw_base_addr;
|
||||
|
||||
memset(buf, 0, RKVPSS_SW_REG_SIZE_MAX);
|
||||
memcpy_fromio(buf, base, RKVPSS_SW_REG_SIZE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops rkvpss_hw_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(rkvpss_runtime_suspend,
|
||||
rkvpss_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver rkvpss_hw_drv = {
|
||||
.driver = {
|
||||
.name = "rkvpss_hw",
|
||||
.of_match_table = of_match_ptr(rkvpss_hw_of_match),
|
||||
.pm = &rkvpss_hw_pm_ops,
|
||||
},
|
||||
.probe = rkvpss_hw_probe,
|
||||
.remove = rkvpss_hw_remove,
|
||||
.shutdown = rkvpss_hw_shutdown,
|
||||
};
|
||||
|
||||
static int __init rkvpss_hw_drv_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&rkvpss_hw_drv);
|
||||
if (!ret)
|
||||
ret = platform_driver_register(&rkvpss_plat_drv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit rkvpss_hw_drv_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&rkvpss_plat_drv);
|
||||
platform_driver_unregister(&rkvpss_hw_drv);
|
||||
}
|
||||
|
||||
module_init(rkvpss_hw_drv_init);
|
||||
module_exit(rkvpss_hw_drv_exit);
|
||||
70
drivers/media/platform/rockchip/vpss/hw.h
Normal file
70
drivers/media/platform/rockchip/vpss/hw.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (C) 2023 Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#ifndef _RKVPSS_HW_H
|
||||
#define _RKVPSS_HW_H
|
||||
|
||||
#include "common.h"
|
||||
#include "vpss_offline.h"
|
||||
|
||||
#define VPSS_MAX_BUS_CLK 4
|
||||
#define VPSS_MAX_DEV 8
|
||||
|
||||
struct vpss_clk_info {
|
||||
u32 clk_rate;
|
||||
u32 refer_data;
|
||||
};
|
||||
|
||||
struct vpss_match_data {
|
||||
int clks_num;
|
||||
const char * const *clks;
|
||||
enum rkvpss_ver vpss_ver;
|
||||
struct irqs_data *irqs;
|
||||
int num_irqs;
|
||||
};
|
||||
|
||||
struct rkvpss_hw_dev {
|
||||
struct device *dev;
|
||||
void __iomem *base_addr;
|
||||
const struct vpss_match_data *match_data;
|
||||
const struct vpss_clk_info *clk_rate_tbl;
|
||||
struct reset_control *reset;
|
||||
struct clk *clks[VPSS_MAX_BUS_CLK];
|
||||
struct rkvpss_device *vpss[VPSS_MAX_DEV];
|
||||
struct rkvpss_offline_dev ofl_dev;
|
||||
struct list_head list;
|
||||
int clk_rate_tbl_num;
|
||||
int clks_num;
|
||||
int dev_num;
|
||||
int cur_dev_id;
|
||||
int pre_dev_id;
|
||||
unsigned long core_clk_min;
|
||||
unsigned long core_clk_max;
|
||||
enum rkvpss_ver vpss_ver;
|
||||
/* lock for multi dev */
|
||||
struct mutex dev_lock;
|
||||
spinlock_t reg_lock;
|
||||
atomic_t refcnt;
|
||||
const struct vb2_mem_ops *mem_ops;
|
||||
bool is_ofl_ch[RKVPSS_OUTPUT_MAX];
|
||||
bool is_ofl_cmsc;
|
||||
bool is_mmu;
|
||||
bool is_single;
|
||||
bool is_dma_contig;
|
||||
bool is_shutdown;
|
||||
};
|
||||
|
||||
#define RKVPSS_ZME_TAP_COE(x, y) (((x) & 0x3ff) | (((y) & 0x3ff) << 16))
|
||||
extern const s16 rkvpss_zme_tap8_coe[11][17][8];
|
||||
extern const s16 rkvpss_zme_tap6_coe[11][17][8];
|
||||
int rkvpss_get_zme_tap_coe_index(int ratio);
|
||||
void rkvpss_cmsc_slop(struct rkvpss_cmsc_point *p0,
|
||||
struct rkvpss_cmsc_point *p1,
|
||||
int *k, int *hor);
|
||||
|
||||
void rkvpss_soft_reset(struct rkvpss_hw_dev *hw_dev);
|
||||
void rkvpss_hw_write(struct rkvpss_hw_dev *hw_dev, u32 reg, u32 val);
|
||||
u32 rkvpss_hw_read(struct rkvpss_hw_dev *hw_dev, u32 reg);
|
||||
void rkvpss_hw_set_bits(struct rkvpss_hw_dev *hw, u32 reg, u32 mask, u32 val);
|
||||
void rkvpss_hw_clear_bits(struct rkvpss_hw_dev *hw, u32 reg, u32 mask);
|
||||
#endif
|
||||
69
drivers/media/platform/rockchip/vpss/procfs.c
Normal file
69
drivers/media/platform/rockchip/vpss/procfs.c
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) Rockchip Electronics Co., Ltd. */
|
||||
#include <linux/clk.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/sem.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <media/v4l2-common.h>
|
||||
|
||||
#include "dev.h"
|
||||
#include "procfs.h"
|
||||
#include "regs.h"
|
||||
#include "version.h"
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static int vpss_show(struct seq_file *p, void *v)
|
||||
{
|
||||
struct rkvpss_device *dev = p->private;
|
||||
enum rkvpss_state state = dev->vpss_sdev.state;
|
||||
u32 val;
|
||||
|
||||
seq_printf(p, "%-10s Version:v%02x.%02x.%02x\n",
|
||||
dev->name,
|
||||
RKVPSS_DRIVER_VERSION >> 16,
|
||||
(RKVPSS_DRIVER_VERSION & 0xff00) >> 8,
|
||||
RKVPSS_DRIVER_VERSION & 0x00ff);
|
||||
for (val = 0; val < dev->hw_dev->clks_num; val++) {
|
||||
seq_printf(p, "%-10s %ld\n",
|
||||
dev->hw_dev->match_data->clks[val],
|
||||
clk_get_rate(dev->hw_dev->clks[val]));
|
||||
}
|
||||
if (state != VPSS_START)
|
||||
return 0;
|
||||
|
||||
seq_printf(p, "%-10s Cnt:%d ErrCnt:%d\n",
|
||||
"Interrupt",
|
||||
dev->isr_cnt,
|
||||
dev->isr_err_cnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpss_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct rkvpss_device *data = pde_data(inode);
|
||||
|
||||
return single_open(file, vpss_show, data);
|
||||
}
|
||||
|
||||
static const struct proc_ops ops = {
|
||||
.proc_open = vpss_open,
|
||||
.proc_read = seq_read,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = single_release,
|
||||
};
|
||||
|
||||
int rkvpss_proc_init(struct rkvpss_device *dev)
|
||||
{
|
||||
dev->procfs = proc_create_data(dev->name, 0, NULL, &ops, dev);
|
||||
if (!dev->procfs)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rkvpss_proc_cleanup(struct rkvpss_device *dev)
|
||||
{
|
||||
if (dev->procfs)
|
||||
remove_proc_entry(dev->name, NULL);
|
||||
dev->procfs = NULL;
|
||||
}
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
15
drivers/media/platform/rockchip/vpss/procfs.h
Normal file
15
drivers/media/platform/rockchip/vpss/procfs.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#ifndef _RKVPSS_PROCFS_H
|
||||
#define _RKVPSS_PROCFS_H
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
int rkvpss_proc_init(struct rkvpss_device *dev);
|
||||
void rkvpss_proc_cleanup(struct rkvpss_device *dev);
|
||||
#else
|
||||
static inline int rkvpss_proc_init(struct rkvpss_device *dev) { return 0; }
|
||||
static inline void rkvpss_proc_cleanup(struct rkvpss_device *dev) {}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1184
drivers/media/platform/rockchip/vpss/regs.h
Normal file
1184
drivers/media/platform/rockchip/vpss/regs.h
Normal file
File diff suppressed because it is too large
Load diff
1785
drivers/media/platform/rockchip/vpss/stream.c
Normal file
1785
drivers/media/platform/rockchip/vpss/stream.c
Normal file
File diff suppressed because it is too large
Load diff
172
drivers/media/platform/rockchip/vpss/stream.h
Normal file
172
drivers/media/platform/rockchip/vpss/stream.h
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#ifndef _RKVPSS_STREAM_H
|
||||
#define _RKVPSS_STREAM_H
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include "common.h"
|
||||
|
||||
struct rkvpss_stream;
|
||||
|
||||
/*
|
||||
* fourcc: pixel format
|
||||
* fmt_type: helper filed for pixel format
|
||||
* cplanes: number of colour planes
|
||||
* mplanes: number of stored memory planes
|
||||
* wr_fmt: defines format for reg
|
||||
* bpp: bits per pixel
|
||||
*/
|
||||
struct capture_fmt {
|
||||
u32 fourcc;
|
||||
u8 fmt_type;
|
||||
u8 cplanes;
|
||||
u8 mplanes;
|
||||
u8 swap;
|
||||
u32 wr_fmt;
|
||||
u32 output_fmt;
|
||||
u8 bpp[VIDEO_MAX_PLANES];
|
||||
};
|
||||
|
||||
/* Different config for stream */
|
||||
struct stream_config {
|
||||
const struct capture_fmt *fmts;
|
||||
unsigned int fmt_size;
|
||||
u32 frame_end_id;
|
||||
/* registers */
|
||||
struct {
|
||||
u32 ctrl;
|
||||
u32 update;
|
||||
u32 src_size;
|
||||
u32 dst_size;
|
||||
u32 hy_fac;
|
||||
u32 hc_fac;
|
||||
u32 vy_fac;
|
||||
u32 vc_fac;
|
||||
u32 hy_offs;
|
||||
u32 hc_offs;
|
||||
u32 vy_offs;
|
||||
u32 vc_offs;
|
||||
u32 hy_size;
|
||||
u32 hc_size;
|
||||
u32 hy_offs_mi;
|
||||
u32 hc_offs_mi;
|
||||
u32 in_crop_offs;
|
||||
u32 ctrl_shd;
|
||||
u32 src_size_shd;
|
||||
u32 dst_size_shd;
|
||||
u32 hy_fac_shd;
|
||||
u32 hc_fac_shd;
|
||||
u32 vy_fac_shd;
|
||||
u32 vc_fac_shd;
|
||||
u32 hy_offs_shd;
|
||||
u32 hc_offs_shd;
|
||||
u32 vy_offs_shd;
|
||||
u32 vc_offs_shd;
|
||||
u32 hy_size_shd;
|
||||
u32 hc_size_shd;
|
||||
u32 hy_offs_mi_shd;
|
||||
u32 hc_offs_mi_shd;
|
||||
u32 in_crop_offs_shd;
|
||||
} scale;
|
||||
struct {
|
||||
u32 ctrl;
|
||||
u32 update;
|
||||
u32 h_offs;
|
||||
u32 v_offs;
|
||||
u32 h_size;
|
||||
u32 v_size;
|
||||
u32 ctrl_shd;
|
||||
u32 h_offs_shd;
|
||||
u32 v_offs_shd;
|
||||
u32 h_size_shd;
|
||||
u32 v_size_shd;
|
||||
} crop;
|
||||
struct {
|
||||
u32 ctrl;
|
||||
u32 stride;
|
||||
u32 y_base;
|
||||
u32 uv_base;
|
||||
u32 y_size;
|
||||
u32 uv_size;
|
||||
u32 y_offs_cnt;
|
||||
u32 uv_offs_cnt;
|
||||
u32 y_pic_width;
|
||||
u32 y_pic_size;
|
||||
|
||||
u32 ctrl_shd;
|
||||
u32 y_shd;
|
||||
u32 uv_shd;
|
||||
} mi;
|
||||
};
|
||||
|
||||
/* Different reg ops for stream */
|
||||
struct streams_ops {
|
||||
void (*config_mi)(struct rkvpss_stream *stream);
|
||||
void (*enable_mi)(struct rkvpss_stream *stream);
|
||||
void (*disable_mi)(struct rkvpss_stream *stream);
|
||||
void (*update_mi)(struct rkvpss_stream *stream);
|
||||
void (*frame_start)(struct rkvpss_stream *stream, u32 irq);
|
||||
int (*is_stopped)(struct rkvpss_stream *stream);
|
||||
int (*limit_check)(struct rkvpss_stream *stream,
|
||||
struct v4l2_pix_format_mplane *try_fmt);
|
||||
};
|
||||
|
||||
/* struct rkvpss_stream - VPSS stream video device
|
||||
* id: stream video identify
|
||||
* buf_queue: queued buffer list
|
||||
* curr_buf: the buffer used for current frame
|
||||
* next_buf: the buffer used for next frame
|
||||
* done: wait frame end event queue
|
||||
* vbq_lock: lock to protect buf_queue
|
||||
* out_cap_fmt: the output of vpss
|
||||
* out_fmt: the output of v4l2 pix format
|
||||
* last_module: last function module
|
||||
* streaming: stream start flag
|
||||
* stopping: stream stop flag
|
||||
* linked: link enable flag
|
||||
*/
|
||||
struct rkvpss_stream {
|
||||
struct rkvpss_device *dev;
|
||||
struct rkvpss_vdev_node vnode;
|
||||
|
||||
struct list_head buf_queue;
|
||||
struct list_head buf_done_list;
|
||||
struct tasklet_struct buf_done_tasklet;
|
||||
|
||||
struct rkvpss_buffer *curr_buf;
|
||||
struct rkvpss_buffer *next_buf;
|
||||
wait_queue_head_t done;
|
||||
spinlock_t vbq_lock;
|
||||
|
||||
struct streams_ops *ops;
|
||||
struct stream_config *config;
|
||||
struct capture_fmt out_cap_fmt;
|
||||
|
||||
struct v4l2_rect crop;
|
||||
struct v4l2_pix_format_mplane out_fmt;
|
||||
|
||||
int id;
|
||||
bool streaming;
|
||||
bool stopping;
|
||||
bool linked;
|
||||
bool flip_en;
|
||||
bool is_crop_upd;
|
||||
bool is_mf_upd;
|
||||
bool is_pause;
|
||||
};
|
||||
|
||||
/* rkvpss stream device */
|
||||
struct rkvpss_stream_vdev {
|
||||
struct rkvpss_stream stream[RKVPSS_OUTPUT_MAX];
|
||||
atomic_t refcnt;
|
||||
};
|
||||
|
||||
void rkvpss_cmsc_config(struct rkvpss_device *dev, bool sync);
|
||||
void rkvpss_stream_default_fmt(struct rkvpss_device *dev, u32 id,
|
||||
u32 width, u32 height, u32 pixelformat);
|
||||
void rkvpss_isr(struct rkvpss_device *dev, u32 mis_val);
|
||||
void rkvpss_mi_isr(struct rkvpss_device *dev, u32 mis_val);
|
||||
void rkvpss_unregister_stream_vdevs(struct rkvpss_device *dev);
|
||||
int rkvpss_register_stream_vdevs(struct rkvpss_device *dev);
|
||||
#endif
|
||||
19
drivers/media/platform/rockchip/vpss/version.h
Normal file
19
drivers/media/platform/rockchip/vpss/version.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#ifndef _RKVPSS_VERSION_H
|
||||
#define _RKVPSS_VERSION_H
|
||||
#include <linux/version.h>
|
||||
#include <linux/rk-vpss-config.h>
|
||||
|
||||
/*
|
||||
* RKVPSS DRIVER VERSION NOTE
|
||||
*
|
||||
* v0.1.0:
|
||||
* 1. First version;
|
||||
*
|
||||
*/
|
||||
|
||||
#define RKVPSS_DRIVER_VERSION VPSS_API_VERSION
|
||||
|
||||
#endif
|
||||
387
drivers/media/platform/rockchip/vpss/vpss.c
Normal file
387
drivers/media/platform/rockchip/vpss/vpss.c
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/media-entity.h>
|
||||
#include <media/v4l2-event.h>
|
||||
|
||||
#include "dev.h"
|
||||
#include "regs.h"
|
||||
|
||||
static const struct vpsssd_fmt rkvpss_formats[] = {
|
||||
{
|
||||
.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
|
||||
.fourcc = V4L2_PIX_FMT_NV16,
|
||||
},
|
||||
};
|
||||
|
||||
static int rkvpss_subdev_link_setup(struct media_entity *entity,
|
||||
const struct media_pad *local,
|
||||
const struct media_pad *remote,
|
||||
u32 flags)
|
||||
{
|
||||
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
|
||||
struct rkvpss_subdev *vpss_sdev;
|
||||
struct rkvpss_device *dev;
|
||||
struct rkvpss_stream_vdev *vdev;
|
||||
struct rkvpss_stream *stream = NULL;
|
||||
|
||||
if (local->index != RKVPSS_PAD_SINK &&
|
||||
local->index != RKVPSS_PAD_SOURCE)
|
||||
return 0;
|
||||
|
||||
if (!sd)
|
||||
return -ENODEV;
|
||||
vpss_sdev = v4l2_get_subdevdata(sd);
|
||||
if (!vpss_sdev)
|
||||
return -ENODEV;
|
||||
dev = vpss_sdev->dev;
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
vdev = &dev->stream_vdev;
|
||||
if (vpss_sdev->state & VPSS_START)
|
||||
return -EBUSY;
|
||||
|
||||
if (!strcmp(remote->entity->name, S0_VDEV_NAME)) {
|
||||
stream = &vdev->stream[RKVPSS_OUTPUT_CH0];
|
||||
} else if (!strcmp(remote->entity->name, S1_VDEV_NAME)) {
|
||||
stream = &vdev->stream[RKVPSS_OUTPUT_CH1];
|
||||
} else if (!strcmp(remote->entity->name, S2_VDEV_NAME)) {
|
||||
stream = &vdev->stream[RKVPSS_OUTPUT_CH2];
|
||||
} else if (!strcmp(remote->entity->name, S3_VDEV_NAME)) {
|
||||
stream = &vdev->stream[RKVPSS_OUTPUT_CH3];
|
||||
} else if (strstr(remote->entity->name, "rkisp")) {
|
||||
if (flags & MEDIA_LNK_FL_ENABLED)
|
||||
dev->inp = INP_ISP;
|
||||
else
|
||||
dev->inp = INP_INVAL;
|
||||
}
|
||||
if (stream)
|
||||
stream->linked = flags & MEDIA_LNK_FL_ENABLED;
|
||||
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, "input:%d\n", dev->inp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rkvpss_sd_get_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_format *fmt)
|
||||
{
|
||||
struct rkvpss_subdev *sdev = v4l2_get_subdevdata(sd);
|
||||
struct rkvpss_device *dev = sdev->dev;
|
||||
struct v4l2_mbus_framefmt *mf;
|
||||
int ret = 0;
|
||||
|
||||
if (!fmt)
|
||||
return -EINVAL;
|
||||
|
||||
if (fmt->pad != RKVPSS_PAD_SINK &&
|
||||
fmt->pad != RKVPSS_PAD_SOURCE)
|
||||
return -EINVAL;
|
||||
|
||||
mf = &fmt->format;
|
||||
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
|
||||
if (!sd_state)
|
||||
return -EINVAL;
|
||||
mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
|
||||
}
|
||||
|
||||
*mf = sdev->in_fmt;
|
||||
if (fmt->pad == RKVPSS_PAD_SINK && sdev->dev->inp == INP_ISP) {
|
||||
ret = v4l2_subdev_call(dev->remote_sd, pad, get_fmt, sd_state, fmt);
|
||||
if (!ret) {
|
||||
if (sdev->in_fmt.width != mf->width ||
|
||||
sdev->in_fmt.height != mf->height) {
|
||||
sdev->out_fmt.width = mf->width;
|
||||
sdev->out_fmt.height = mf->height;
|
||||
rkvpss_pipeline_default_fmt(dev);
|
||||
}
|
||||
sdev->in_fmt = *mf;
|
||||
}
|
||||
} else {
|
||||
*mf = sdev->in_fmt;
|
||||
mf->width = sdev->out_fmt.width;
|
||||
mf->height = sdev->out_fmt.height;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rkvpss_sd_set_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_format *fmt)
|
||||
{
|
||||
struct rkvpss_subdev *sdev = v4l2_get_subdevdata(sd);
|
||||
struct v4l2_mbus_framefmt *mf;
|
||||
|
||||
if (!fmt)
|
||||
return -EINVAL;
|
||||
/* format from isp output */
|
||||
if (fmt->pad == RKVPSS_PAD_SINK && sdev->dev->inp == INP_ISP)
|
||||
return rkvpss_sd_get_fmt(sd, sd_state, fmt);
|
||||
|
||||
mf = &fmt->format;
|
||||
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
|
||||
if (!sd_state)
|
||||
return -EINVAL;
|
||||
mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
|
||||
}
|
||||
|
||||
if (fmt->pad == RKVPSS_PAD_SINK) {
|
||||
sdev->in_fmt = *mf;
|
||||
} else {
|
||||
sdev->out_fmt.width = mf->width;
|
||||
sdev->out_fmt.height = mf->height;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rkvpss_sd_s_stream(struct v4l2_subdev *sd, int on)
|
||||
{
|
||||
struct rkvpss_subdev *sdev = v4l2_get_subdevdata(sd);
|
||||
struct rkvpss_device *dev = sdev->dev;
|
||||
u32 val, w = sdev->in_fmt.width, h = sdev->in_fmt.height;
|
||||
|
||||
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev,
|
||||
"s_stream on:%d %dx%d\n", on, w, h);
|
||||
|
||||
if (!on) {
|
||||
rkvpss_clear_bits(dev, RKVPSS_VPSS_ONLINE, RKVPSS_ONLINE_MODE_MASK);
|
||||
rkvpss_clear_bits(dev, RKVPSS_VPSS_IMSC, RKVPSS_ISP_ALL_FRM_END);
|
||||
sdev->state = VPSS_STOP;
|
||||
atomic_dec(&dev->hw_dev->refcnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sdev->frame_seq = -1;
|
||||
sdev->frame_timestamp = 0;
|
||||
atomic_inc(&dev->hw_dev->refcnt);
|
||||
dev->cmsc_upd = true;
|
||||
rkvpss_cmsc_config(dev, true);
|
||||
rkvpss_write(dev, RKVPSS_VPSS_ONLINE2_SIZE, h << 16 | w);
|
||||
|
||||
val = RKVPSS_CFG_FORCE_UPD | RKVPSS_CFG_GEN_UPD | RKVPSS_MIR_GEN_UPD;
|
||||
if (!dev->hw_dev->is_ofl_cmsc)
|
||||
val |= RKVPSS_MIR_FORCE_UPD;
|
||||
rkvpss_write(dev, RKVPSS_VPSS_UPDATE, val);
|
||||
|
||||
rkvpss_set_bits(dev, RKVPSS_VPSS_IMSC, 0, RKVPSS_ISP_ALL_FRM_END);
|
||||
sdev->state |= VPSS_START;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rkvpss_sd_s_rx_buffer(struct v4l2_subdev *sd,
|
||||
void *buf, unsigned int *size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rkvpss_sd_s_power(struct v4l2_subdev *sd, int on)
|
||||
{
|
||||
struct rkvpss_subdev *sdev = v4l2_get_subdevdata(sd);
|
||||
struct rkvpss_device *dev = sdev->dev;
|
||||
int ret;
|
||||
|
||||
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev,
|
||||
"power on:%d\n", on);
|
||||
|
||||
if (on) {
|
||||
if (dev->inp == INP_ISP) {
|
||||
struct v4l2_subdev_format fmt = {
|
||||
.pad = RKVPSS_PAD_SINK,
|
||||
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
||||
};
|
||||
|
||||
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
|
||||
if (ret) {
|
||||
v4l2_err(&dev->v4l2_dev,
|
||||
"%s get isp format fail:%d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = v4l2_subdev_call(dev->remote_sd, core, s_power, 1);
|
||||
if (ret < 0) {
|
||||
v4l2_err(&dev->v4l2_dev,
|
||||
"%s set isp power on fail:%d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = pm_runtime_get_sync(dev->dev);
|
||||
if (ret < 0) {
|
||||
v4l2_err(&dev->v4l2_dev,
|
||||
"%s runtime get fail:%d\n", __func__, ret);
|
||||
if (dev->inp == INP_ISP)
|
||||
v4l2_subdev_call(dev->remote_sd, core, s_power, 0);
|
||||
}
|
||||
} else {
|
||||
if (dev->inp == INP_ISP)
|
||||
v4l2_subdev_call(dev->remote_sd, core, s_power, 0);
|
||||
ret = pm_runtime_put_sync(dev->dev);
|
||||
}
|
||||
if (ret < 0)
|
||||
v4l2_err(sd, "%s on:%d failed:%d\n", __func__, on, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rkvpss_sof(struct rkvpss_subdev *sdev, struct rkisp_vpss_sof *info)
|
||||
{
|
||||
struct rkvpss_device *dev = sdev->dev;
|
||||
struct rkvpss_hw_dev *hw = dev->hw_dev;
|
||||
struct rkvpss_stream *stream;
|
||||
int i;
|
||||
|
||||
if (!info)
|
||||
return -EINVAL;
|
||||
sdev->frame_seq = info->seq;
|
||||
sdev->frame_timestamp = info->timestamp;
|
||||
|
||||
rkvpss_cmsc_config(dev, !info->irq);
|
||||
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) {
|
||||
stream = &dev->stream_vdev.stream[i];
|
||||
if (!stream->streaming)
|
||||
continue;
|
||||
if (stream->ops->frame_start)
|
||||
stream->ops->frame_start(stream, info->irq);
|
||||
}
|
||||
|
||||
if (!info->irq && !hw->is_single) {
|
||||
hw->cur_dev_id = dev->dev_id;
|
||||
rkvpss_update_regs(dev, RKVPSS_VPSS_CTRL, RKVPSS_VPSS_ONLINE2_SIZE);
|
||||
rkvpss_update_regs(dev, RKVPSS_VPSS_IRQ_CFG, RKVPSS_VPSS_IMSC);
|
||||
rkvpss_update_regs(dev, RKVPSS_VPSS_Y2R_COE00, RKVPSS_VPSS_Y2R_OFF2);
|
||||
rkvpss_update_regs(dev, RKVPSS_CMSC_INTSCT_CORR, RKVPSS_CMSC_WIN7_L3_SLP);
|
||||
rkvpss_update_regs(dev, RKVPSS_CROP1_0_H_OFFS, RKVPSS_CROP1_3_V_SIZE);
|
||||
rkvpss_update_regs(dev, RKVPSS_ZME_Y_HOR_COE0_10, RKVPSS_ZME_UV_VER_COE16_76);
|
||||
rkvpss_update_regs(dev, RKVPSS_ZME_H_SIZE, RKVPSS_ZME_UV_YSCL_FACTOR);
|
||||
rkvpss_update_regs(dev, RKVPSS_SCALE1_SRC_SIZE, RKVPSS_SCALE1_IN_CROP_OFFSET);
|
||||
rkvpss_update_regs(dev, RKVPSS_SCALE2_SRC_SIZE, RKVPSS_SCALE2_IN_CROP_OFFSET);
|
||||
rkvpss_update_regs(dev, RKVPSS_SCALE3_SRC_SIZE, RKVPSS_SCALE3_IN_CROP_OFFSET);
|
||||
rkvpss_update_regs(dev, RKVPSS_MI_WR_WRAP_CTRL, RKVPSS_MI_IMSC);
|
||||
rkvpss_update_regs(dev, RKVPSS_MI_CHN0_WR_CTRL, RKVPSS_MI_CHN3_WR_LINE_CNT);
|
||||
|
||||
rkvpss_update_regs(dev, RKVPSS_MI_WR_CTRL, RKVPSS_MI_WR_INIT);
|
||||
rkvpss_update_regs(dev, RKVPSS_SCALE3_CTRL, RKVPSS_SCALE3_UPDATE);
|
||||
rkvpss_update_regs(dev, RKVPSS_SCALE2_CTRL, RKVPSS_SCALE2_UPDATE);
|
||||
rkvpss_update_regs(dev, RKVPSS_SCALE1_CTRL, RKVPSS_SCALE1_UPDATE);
|
||||
rkvpss_update_regs(dev, RKVPSS_ZME_CTRL, RKVPSS_ZME_UPDATE);
|
||||
rkvpss_update_regs(dev, RKVPSS_CROP1_CTRL, RKVPSS_CROP1_UPDATE);
|
||||
rkvpss_update_regs(dev, RKVPSS_CMSC_CTRL, RKVPSS_CMSC_UPDATE);
|
||||
rkvpss_update_regs(dev, RKVPSS_VPSS_UPDATE, RKVPSS_VPSS_UPDATE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long rkvpss_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||||
{
|
||||
struct rkvpss_subdev *sdev = v4l2_get_subdevdata(sd);
|
||||
long ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case RKISP_VPSS_CMD_SOF:
|
||||
ret = rkvpss_sof(sdev, arg);
|
||||
break;
|
||||
default:
|
||||
ret = -ENOIOCTLCMD;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long rkvpss_compat_ioctl32(struct v4l2_subdev *sd,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
long ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct media_entity_operations rkvpss_sd_media_ops = {
|
||||
.link_setup = rkvpss_subdev_link_setup,
|
||||
.link_validate = v4l2_subdev_link_validate,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_pad_ops rkvpss_sd_pad_ops = {
|
||||
.get_fmt = rkvpss_sd_get_fmt,
|
||||
.set_fmt = rkvpss_sd_set_fmt,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_video_ops rkvpss_sd_video_ops = {
|
||||
.s_stream = rkvpss_sd_s_stream,
|
||||
.s_rx_buffer = rkvpss_sd_s_rx_buffer,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_core_ops rkvpss_sd_core_ops = {
|
||||
.s_power = rkvpss_sd_s_power,
|
||||
.ioctl = rkvpss_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl32 = rkvpss_compat_ioctl32,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops rkvpss_sd_ops = {
|
||||
.core = &rkvpss_sd_core_ops,
|
||||
.video = &rkvpss_sd_video_ops,
|
||||
.pad = &rkvpss_sd_pad_ops,
|
||||
};
|
||||
|
||||
static void rkvpss_subdev_default_fmt(struct rkvpss_subdev *sdev)
|
||||
{
|
||||
struct v4l2_mbus_framefmt *in_fmt = &sdev->in_fmt;
|
||||
struct vpsssd_fmt *out_fmt = &sdev->out_fmt;
|
||||
|
||||
in_fmt->width = RKVPSS_DEFAULT_WIDTH;
|
||||
in_fmt->height = RKVPSS_DEFAULT_HEIGHT;
|
||||
in_fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
|
||||
|
||||
*out_fmt = rkvpss_formats[0];
|
||||
out_fmt->width = RKVPSS_DEFAULT_WIDTH;
|
||||
out_fmt->height = RKVPSS_DEFAULT_HEIGHT;
|
||||
}
|
||||
|
||||
int rkvpss_register_subdev(struct rkvpss_device *dev,
|
||||
struct v4l2_device *v4l2_dev)
|
||||
{
|
||||
struct rkvpss_subdev *vpss_sdev = &dev->vpss_sdev;
|
||||
struct v4l2_subdev *sd;
|
||||
int ret;
|
||||
|
||||
spin_lock_init(&dev->cmsc_lock);
|
||||
memset(vpss_sdev, 0, sizeof(*vpss_sdev));
|
||||
vpss_sdev->dev = dev;
|
||||
sd = &vpss_sdev->sd;
|
||||
vpss_sdev->state = VPSS_STOP;
|
||||
v4l2_subdev_init(sd, &rkvpss_sd_ops);
|
||||
//sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||
sd->entity.ops = &rkvpss_sd_media_ops;
|
||||
snprintf(sd->name, sizeof(sd->name), "rkvpss-subdev");
|
||||
sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_COMPOSER;
|
||||
vpss_sdev->pads[RKVPSS_PAD_SINK].flags =
|
||||
MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
|
||||
vpss_sdev->pads[RKVPSS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
|
||||
|
||||
ret = media_entity_pads_init(&sd->entity, RKVPSS_PAD_MAX, vpss_sdev->pads);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
sd->owner = THIS_MODULE;
|
||||
v4l2_set_subdevdata(sd, vpss_sdev);
|
||||
sd->grp_id = GRP_ID_VPSS;
|
||||
ret = v4l2_device_register_subdev(v4l2_dev, sd);
|
||||
if (ret < 0)
|
||||
goto free_media;
|
||||
rkvpss_subdev_default_fmt(vpss_sdev);
|
||||
return ret;
|
||||
free_media:
|
||||
media_entity_cleanup(&sd->entity);
|
||||
v4l2_err(sd, "Failed to register subdev, ret:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rkvpss_unregister_subdev(struct rkvpss_device *dev)
|
||||
{
|
||||
struct v4l2_subdev *sd = &dev->vpss_sdev.sd;
|
||||
|
||||
v4l2_device_unregister_subdev(sd);
|
||||
media_entity_cleanup(&sd->entity);
|
||||
}
|
||||
49
drivers/media/platform/rockchip/vpss/vpss.h
Normal file
49
drivers/media/platform/rockchip/vpss/vpss.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2023 Fuzhou Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#ifndef _RKVPSS_VPSS_H
|
||||
#define _RKVPSS_VPSS_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define GRP_ID_VPSS BIT(0)
|
||||
|
||||
enum rkvpss_pad {
|
||||
RKVPSS_PAD_SINK,
|
||||
RKVPSS_PAD_SOURCE,
|
||||
RKVPSS_PAD_MAX
|
||||
};
|
||||
|
||||
enum rkvpss_state {
|
||||
VPSS_STOP = 0,
|
||||
VPSS_FS = BIT(0),
|
||||
VPSS_FE = BIT(1),
|
||||
|
||||
VPSS_START = BIT(8),
|
||||
VPSS_RX_START = BIT(9),
|
||||
};
|
||||
|
||||
struct vpsssd_fmt {
|
||||
u32 mbus_code;
|
||||
u32 fourcc;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u8 wr_fmt;
|
||||
};
|
||||
|
||||
struct rkvpss_subdev {
|
||||
struct rkvpss_device *dev;
|
||||
struct v4l2_subdev sd;
|
||||
struct media_pad pads[RKVPSS_PAD_MAX];
|
||||
struct v4l2_mbus_framefmt in_fmt;
|
||||
struct vpsssd_fmt out_fmt;
|
||||
u32 frame_seq;
|
||||
/* timestamp in ns */
|
||||
u64 frame_timestamp;
|
||||
enum rkvpss_state state;
|
||||
};
|
||||
|
||||
int rkvpss_register_subdev(struct rkvpss_device *dev,
|
||||
struct v4l2_device *v4l2_dev);
|
||||
void rkvpss_unregister_subdev(struct rkvpss_device *dev);
|
||||
#endif
|
||||
1075
drivers/media/platform/rockchip/vpss/vpss_offline.c
Normal file
1075
drivers/media/platform/rockchip/vpss/vpss_offline.c
Normal file
File diff suppressed because it is too large
Load diff
22
drivers/media/platform/rockchip/vpss/vpss_offline.h
Normal file
22
drivers/media/platform/rockchip/vpss/vpss_offline.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#ifndef _RKVPSS_OFFLINE_H
|
||||
#define _RKVPSS_OFFLINE_H
|
||||
|
||||
#include "hw.h"
|
||||
|
||||
struct rkvpss_offline_dev {
|
||||
struct rkvpss_hw_dev *hw;
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct video_device vfd;
|
||||
struct mutex apilock;
|
||||
struct completion cmpl;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
int rkvpss_register_offline(struct rkvpss_hw_dev *hw);
|
||||
void rkvpss_unregister_offline(struct rkvpss_hw_dev *hw);
|
||||
void rkvpss_offline_irq(struct rkvpss_hw_dev *hw, u32 irq);
|
||||
|
||||
#endif
|
||||
269
include/uapi/linux/rk-vpss-config.h
Normal file
269
include/uapi/linux/rk-vpss-config.h
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR MIT)
|
||||
*
|
||||
* Copyright (C) 2023 Rockchip Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_RK_VPSS_CONFIG_H
|
||||
#define _UAPI_RK_VPSS_CONFIG_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/v4l2-controls.h>
|
||||
|
||||
#define VPSS_API_VERSION KERNEL_VERSION(0, 1, 0)
|
||||
|
||||
/* |-------------------------------------------------------------------------------------------|
|
||||
* | mirror_cmsc_en |
|
||||
* | |1------------------------>| |
|
||||
* |ISP->| |->|crop1->scl->ddr channelX| isp->vpss online mode |
|
||||
* | |0---->| |->| media v4l2 driver |
|
||||
* |------------|->mirror->cmsc->|-------------------------------------------------------------|
|
||||
* | |1---->| |->| |
|
||||
* |DDR->| |->|crop0->scl->aspt->ddr channelY| ddr->vpss offline mode |
|
||||
* | |0------------------------>| independent driver |
|
||||
* | mirror_cmsc_en |
|
||||
* |-------------------------------------------------------------------------------------------|
|
||||
* mirror/cover mux to ISP or DDR
|
||||
* channelX or channelY = 0,1,2,3 but X != Y
|
||||
* ioctl RKVPSS_CMD_MODULE_SEL to select function using
|
||||
*/
|
||||
|
||||
/******vpss(online mode) v4l2 ioctl***************************/
|
||||
/* set before VIDIOC_S_FMT if dynamically changing output resolution */
|
||||
#define RKVPSS_CMD_SET_STREAM_MAX_SIZE \
|
||||
_IOW('V', BASE_VIDIOC_PRIVATE + 0, struct rkvpss_stream_size)
|
||||
/* for dynamically changing output resolution:
|
||||
* SET_STREAM_SUSPEND->VIDIOC_S_FMT/VIDIOC_S_SELECTION->SET_STREAM_RESUME
|
||||
*/
|
||||
#define RKVPSS_CMD_SET_STREAM_SUSPEND \
|
||||
_IO('V', BASE_VIDIOC_PRIVATE + 1)
|
||||
#define RKVPSS_CMD_SET_STREAM_RESUME \
|
||||
_IO('V', BASE_VIDIOC_PRIVATE + 2)
|
||||
|
||||
#define RKVPSS_CMD_GET_MIRROR_FLIP \
|
||||
_IOR('V', BASE_VIDIOC_PRIVATE + 3, struct rkvpss_mirror_flip)
|
||||
#define RKVPSS_CMD_SET_MIRROR_FLIP \
|
||||
_IOW('V', BASE_VIDIOC_PRIVATE + 4, struct rkvpss_mirror_flip)
|
||||
|
||||
#define RKVPSS_CMD_GET_CMSC \
|
||||
_IOR('V', BASE_VIDIOC_PRIVATE + 5, struct rkvpss_cmsc_cfg)
|
||||
#define RKVPSS_CMD_SET_CMSC \
|
||||
_IOW('V', BASE_VIDIOC_PRIVATE + 6, struct rkvpss_cmsc_cfg)
|
||||
|
||||
/******vpss(offline mode) independent video ioctl****************/
|
||||
#define RKVPSS_CMD_MODULE_SEL \
|
||||
_IOW('V', BASE_VIDIOC_PRIVATE + 50, struct rkvpss_module_sel)
|
||||
|
||||
#define RKVPSS_CMD_FRAME_HANDLE \
|
||||
_IOW('V', BASE_VIDIOC_PRIVATE + 51, struct rkvpss_frame_cfg)
|
||||
|
||||
/* request vpss to alloc or external dma buf add to vpss */
|
||||
#define RKVPSS_CMD_BUF_ADD \
|
||||
_IOWR('V', BASE_VIDIOC_PRIVATE + 52, struct rkvpss_buf_info)
|
||||
#define RKVPSS_CMD_BUF_DEL \
|
||||
_IOW('V', BASE_VIDIOC_PRIVATE + 53, struct rkvpss_buf_info)
|
||||
|
||||
/********************************************************************/
|
||||
|
||||
/* struct rkvpss_mirror_flip
|
||||
* mirror: global for all output stream
|
||||
* flip: independent for all output stream
|
||||
*/
|
||||
struct rkvpss_mirror_flip {
|
||||
unsigned char mirror;
|
||||
unsigned char flip;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* struct rkvpss_stream_size
|
||||
* set max resolution before VIDIOC_S_FMT for init buffer
|
||||
*/
|
||||
struct rkvpss_stream_size {
|
||||
unsigned int max_width;
|
||||
unsigned int max_height;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define RKVPSS_CMSC_WIN_MAX 8
|
||||
#define RKVPSS_CMSC_POINT_MAX 4
|
||||
#define RKVPSS_CMSC_COVER_MODE 0
|
||||
#define RKVPSS_CMSC_MOSAIC_MODE 1
|
||||
|
||||
struct rkvpss_cmsc_point {
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* struct rkvpss_cmsc_win
|
||||
* Priacy Mask Window configture, support windows
|
||||
*
|
||||
* win_index: window index 0~8. windows overlap, priority win8 > win0.
|
||||
* mode: RKVPSS_CMSC_MOSAIC_MODE:mosaic mode, RKVPSS_CMSC_COVER_MODE:cover mode
|
||||
* cover_color_y: cover mode Y value[0, 255].
|
||||
* cover_color_u: cover mode U value[0, 255].
|
||||
* cover_color_v: cover mode V value[0, 255].
|
||||
* cover_color_a: cover mode alpha value[0, 15], 0 is transparent.
|
||||
* point: four coordinates of any quadrilateral, the top left of the input image is the origin.
|
||||
* point0 must be the vertex, point0~ponit3 clockwise, and four coordinates should different.
|
||||
*/
|
||||
struct rkvpss_cmsc_win {
|
||||
unsigned short win_en;
|
||||
|
||||
/* following share for all channel when same win index */
|
||||
unsigned short mode;
|
||||
unsigned char cover_color_y;
|
||||
unsigned char cover_color_u;
|
||||
unsigned char cover_color_v;
|
||||
unsigned char cover_color_a;
|
||||
struct rkvpss_cmsc_point point[RKVPSS_CMSC_POINT_MAX];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* struct rkvpss_cmsc_cfg
|
||||
* cover and mosaic configure
|
||||
* win: priacy mask window
|
||||
* mosaic_block: Mosaic block size, 0:8x8 1:16x16 2:32x32 3:64x64, share for all windows
|
||||
* width_ro: vpss full resolution.
|
||||
* height_ro: vpss full resolution.
|
||||
*/
|
||||
struct rkvpss_cmsc_cfg {
|
||||
struct rkvpss_cmsc_win win[RKVPSS_CMSC_WIN_MAX];
|
||||
unsigned int mosaic_block;
|
||||
unsigned int width_ro;
|
||||
unsigned int height_ro;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* struct rkisp_aspt_cfg _____background____
|
||||
* aspective ratio for image background color filling |offs __image___ c |
|
||||
* width: width of background. 2 align | |scl_width | o |
|
||||
* height: height of background. 2 align | |scl_height| l |
|
||||
* h_offs: horizontal offset of image in the background. 2 align | |__________| o |
|
||||
* v_offs: vertical offset of image in the background. 2 align | color r |
|
||||
* color_y: background y color. 0~255 |___________________|
|
||||
* color_u: background u color. 0~255
|
||||
* color_v: background v color. 0~255
|
||||
* enable: function enable
|
||||
*/
|
||||
struct rkvpss_aspt_cfg {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
|
||||
unsigned int h_offs;
|
||||
unsigned int v_offs;
|
||||
|
||||
unsigned char color_y;
|
||||
unsigned char color_u;
|
||||
unsigned char color_v;
|
||||
|
||||
unsigned char enable;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/********************************************************************/
|
||||
|
||||
enum {
|
||||
RKVPSS_OUTPUT_CH0 = 0,
|
||||
RKVPSS_OUTPUT_CH1,
|
||||
RKVPSS_OUTPUT_CH2,
|
||||
RKVPSS_OUTPUT_CH3,
|
||||
RKVPSS_OUTPUT_MAX,
|
||||
};
|
||||
|
||||
/* struct rkvpss_module_sel
|
||||
* selection module for vpss offline mode, default select to online mode.
|
||||
* mirror_cmsc_en 1:miiror_cmsc sel to offline mode, 0:sel to online mode
|
||||
* ch_en 1:channel sel to offline mode, 0:sel to online mode
|
||||
*/
|
||||
struct rkvpss_module_sel {
|
||||
unsigned char mirror_cmsc_en;
|
||||
unsigned char ch_en[RKVPSS_OUTPUT_MAX];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* struct rkvpss_input_cfg
|
||||
* input configuration of image
|
||||
*
|
||||
* width: width of input image, range: 32~4672
|
||||
* height: height of input image, range: 32~3504
|
||||
* stride: virtual width of input image, 16 align. auto calculate according to width and format if 0.
|
||||
* format: V4L2_PIX_FMT_NV12/V4L2_PIX_FMT_NV16/V4L2_PIX_FMT_RGB565/V4L2_PIX_FMT_RGB24/V4L2_PIX_FMT_XBGR32
|
||||
* buf_fd: dmabuf fd of input image buf
|
||||
*/
|
||||
struct rkvpss_input_cfg {
|
||||
int width;
|
||||
int height;
|
||||
int stride;
|
||||
int format;
|
||||
int buf_fd;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* struct rkvpss_output_cfg __________________
|
||||
* output channel configuration of image |offs __________ |
|
||||
* | | ______ | |
|
||||
* enable: channel enable | | | | | |
|
||||
* crop_h_offs: horizontal offset of crop, 2 align | | | | | |
|
||||
* crop_v_offs: vertical offset of crop, 2align | | |scl___| | |
|
||||
* crop_width: crop output width, 2align | |crop______| |
|
||||
* crop_height: crop output height, 2align |input_____________|
|
||||
* scl_width: scale width. CH0 1~8 scale range. CH1/CH2/CH3 1~32 scale range. CH2/CH3 max 1080p with scale.
|
||||
* scl_height: scale height. CH0 1~6 scale range. CH1/CH2/CH3 1~32 scale range. CH2/CH3 max 1080p with scale.
|
||||
* stride: virtual width of output image, 16 align. auto calculate according to width and format if 0.
|
||||
* format: V4L2_PIX_FMT_NV12/V4L2_PIX_FMT_NV16/V4L2_PIX_FMT_GREY/V4L2_PIX_FMT_UYVY for all channel.
|
||||
* V4L2_PIX_FMT_RGB565/V4L2_PIX_FMT_RGB24/V4L2_PIX_FMT_XBGR32 only for RKVPSS_OUTPUT_CH1.
|
||||
* flip: flip enable
|
||||
* buf_fd: dmabuf fd of output image buf
|
||||
* cmsc: cover and mosaic configure
|
||||
* aspt: aspective ratio for image background color filling
|
||||
*/
|
||||
struct rkvpss_output_cfg {
|
||||
int enable;
|
||||
|
||||
int crop_h_offs;
|
||||
int crop_v_offs;
|
||||
int crop_width;
|
||||
int crop_height;
|
||||
|
||||
int scl_width;
|
||||
int scl_height;
|
||||
int stride;
|
||||
int format;
|
||||
int flip;
|
||||
int buf_fd;
|
||||
|
||||
struct rkvpss_cmsc_cfg cmsc;
|
||||
struct rkvpss_aspt_cfg aspt;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define RKVPSS_DEV_ID_MAX 128
|
||||
|
||||
/* struct rkvpss_frame_cfg
|
||||
* frame handle configure
|
||||
*
|
||||
* dev_id: device id, range 0~127.
|
||||
* sequence: frame sequence
|
||||
* mirror: mirror enable
|
||||
* input: input configuration of image
|
||||
* output: output channel configuration of image
|
||||
*/
|
||||
struct rkvpss_frame_cfg {
|
||||
int dev_id;
|
||||
int sequence;
|
||||
|
||||
int mirror;
|
||||
struct rkvpss_input_cfg input;
|
||||
struct rkvpss_output_cfg output[RKVPSS_OUTPUT_MAX];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define RKVPSS_BUF_MAX 32
|
||||
|
||||
/* struct rkvpss_buf_info
|
||||
* request vpss to alloc or external dma buf add to vpss.
|
||||
* dev_id: device id, range 0~127.
|
||||
* buf_alloc: request vpss alloc buf or no. 0: no alloc using external buf
|
||||
* buf_cnt: buffer count.
|
||||
* buf_size: buffer size.
|
||||
* buf_fd: dma buffer fd. return if buf_alloc=1, other user set for driver.
|
||||
*/
|
||||
struct rkvpss_buf_info {
|
||||
int dev_id;
|
||||
int buf_alloc;
|
||||
int buf_cnt;
|
||||
int buf_size[RKVPSS_BUF_MAX];
|
||||
int buf_fd[RKVPSS_BUF_MAX];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue