media: rockchip: add flexbus cif driver

Signed-off-by: Zefa Chen <zefa.chen@rock-chips.com>
Change-Id: I1335abf6bb590d145acaccfaf31bcc663b8da92a
This commit is contained in:
Zefa Chen
2024-06-17 11:31:31 +08:00
parent 34c17d0724
commit ae51ee3043
12 changed files with 4062 additions and 0 deletions

View File

@@ -3,6 +3,7 @@
comment "Rockchip media platform drivers"
source "drivers/media/platform/rockchip/cif/Kconfig"
source "drivers/media/platform/rockchip/flexbus_cif/Kconfig"
source "drivers/media/platform/rockchip/isp1/Kconfig"
source "drivers/media/platform/rockchip/isp/Kconfig"
source "drivers/media/platform/rockchip/ispp/Kconfig"

View File

@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += cif/
obj-y += flexbus_cif/
obj-y += isp1/
obj-y += isp/
obj-y += ispp/

View File

@@ -0,0 +1,37 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2024 Rockchip Electronics Co., Ltd.
config ROCKCHIP_FLEXBUS_CIF
tristate "Rockchip Flexbus CIF Camera Interface driver"
depends on V4L_PLATFORM_DRIVERS
depends on VIDEO_DEV
depends on ARCH_ROCKCHIP || COMPILE_TEST
depends on MEDIA_CAMERA_SUPPORT
select MFD_ROCKCHIP_FLEXBUS
select VIDEO_V4L2_SUBDEV_API
select VIDEOBUF2_CMA_SG
select VIDEOBUF2_VMALLOC
select V4L2_FWNODE
default n
help
Support for flexbus cif on the rockchip SoCs
choice
prompt "rockchip flexbuf-cif device dummy buffer choice"
depends on ROCKCHIP_FLEXBUS_CIF
default ROCKCHIP_FLEXBUS_CIF_USE_DUMMY_BUF
help
choice to use dummy buffer or not
config ROCKCHIP_FLEXBUS_CIF_USE_DUMMY_BUF
bool "flexbuf-cif use dummy buffer"
help
select to use dummy buffer
config ROCKCHIP_FLEXBUS_CIF_USE_NONE_DUMMY_BUF
bool "flexbuf-cif not use dummy buffer"
help
select not to use dummy buffer
endchoice

View File

@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2024 Rockchip Electronics Co., Ltd.
obj-$(CONFIG_ROCKCHIP_FLEXBUS_CIF) += flexbus_cif.o
flexbus_cif-objs += dev.o \
capture.o \
procfs.o \
common.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,98 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip Flexbus CIF Driver
*
* Copyright (C) 2024 Rockchip Electronics Co., Ltd.
*/
#include <media/videobuf2-cma-sg.h>
#include <media/videobuf2-dma-contig.h>
#include <linux/of_platform.h>
#include "dev.h"
#include "common.h"
static void flexbus_cif_init_dummy_vb2(struct flexbus_cif_device *dev,
struct flexbus_cif_dummy_buffer *buf)
{
unsigned long attrs = buf->is_need_vaddr ? 0 : DMA_ATTR_NO_KERNEL_MAPPING;
memset(&buf->vb2_queue, 0, sizeof(struct vb2_queue));
memset(&buf->vb, 0, sizeof(struct vb2_buffer));
buf->vb2_queue.gfp_flags = GFP_KERNEL | GFP_DMA32;
buf->vb2_queue.dma_dir = DMA_BIDIRECTIONAL;
attrs |= DMA_ATTR_FORCE_CONTIGUOUS;
buf->vb2_queue.dma_attrs = attrs;
buf->vb.vb2_queue = &buf->vb2_queue;
}
int flexbus_cif_alloc_buffer(struct flexbus_cif_device *dev,
struct flexbus_cif_dummy_buffer *buf)
{
const struct vb2_mem_ops *g_ops = &vb2_cma_sg_memops;
struct sg_table *sg_tbl;
void *mem_priv;
int ret = 0;
if (!buf->size) {
ret = -EINVAL;
goto err;
}
flexbus_cif_init_dummy_vb2(dev, buf);
buf->size = PAGE_ALIGN(buf->size);
mem_priv = g_ops->alloc(&buf->vb, dev->dev, buf->size);
if (IS_ERR_OR_NULL(mem_priv)) {
ret = -ENOMEM;
goto err;
}
buf->mem_priv = mem_priv;
sg_tbl = (struct sg_table *)g_ops->cookie(&buf->vb, mem_priv);
buf->dma_addr = sg_dma_address(sg_tbl->sgl);
g_ops->prepare(mem_priv);
if (buf->is_need_vaddr)
buf->vaddr = g_ops->vaddr(&buf->vb, mem_priv);
if (buf->is_need_dbuf) {
buf->dbuf = g_ops->get_dmabuf(&buf->vb, mem_priv, O_RDWR);
if (buf->is_need_dmafd) {
buf->dma_fd = dma_buf_fd(buf->dbuf, O_CLOEXEC);
if (buf->dma_fd < 0) {
dma_buf_put(buf->dbuf);
ret = buf->dma_fd;
goto err;
}
get_dma_buf(buf->dbuf);
}
}
v4l2_dbg(1, flexbus_cif_debug, &dev->v4l2_dev,
"%s buf:0x%x~0x%x size:%d\n", __func__,
(u32)buf->dma_addr, (u32)buf->dma_addr + buf->size, buf->size);
return ret;
err:
dev_err(dev->dev, "%s failed ret:%d\n", __func__, ret);
return ret;
}
void flexbus_cif_free_buffer(struct flexbus_cif_device *dev,
struct flexbus_cif_dummy_buffer *buf)
{
const struct vb2_mem_ops *g_ops = &vb2_cma_sg_memops;
if (buf && buf->mem_priv) {
v4l2_dbg(1, flexbus_cif_debug, &dev->v4l2_dev,
"%s buf:0x%x~0x%x\n", __func__,
(u32)buf->dma_addr, (u32)buf->dma_addr + buf->size);
if (buf->dbuf)
dma_buf_put(buf->dbuf);
g_ops->put(buf->mem_priv);
buf->size = 0;
buf->dbuf = NULL;
buf->vaddr = NULL;
buf->mem_priv = NULL;
buf->is_need_dbuf = false;
buf->is_need_vaddr = false;
buf->is_need_dmafd = false;
buf->is_free = true;
}
}

View File

@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rockchip Flexbus CIF Driver
*
* Copyright (C) 2024 Rockchip Electronics Co., Ltd.
*/
#ifndef _FLEXBUS_CIF_COMMON_H
#define _FLEXBUS_CIF_COMMON_H
#include <linux/clk.h>
#include <linux/mutex.h>
#include <linux/media.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 <media/v4l2-mc.h>
#include "dev.h"
int flexbus_cif_alloc_buffer(struct flexbus_cif_device *dev,
struct flexbus_cif_dummy_buffer *buf);
void flexbus_cif_free_buffer(struct flexbus_cif_device *dev,
struct flexbus_cif_dummy_buffer *buf);
#endif /* _FLEXBUS_CIF_COMMON_H */

View File

@@ -0,0 +1,674 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip Flexbus CIF Driver
*
* Copyright (C) 2024 Rockchip Electronics Co., Ltd.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_graph.h>
#include <linux/of_platform.h>
#include <linux/of_reserved_mem.h>
#include <linux/reset.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/consumer.h>
#include <linux/regmap.h>
#include <media/videobuf2-dma-contig.h>
#include <media/v4l2-fwnode.h>
#include <linux/iommu.h>
#include <dt-bindings/soc/rockchip-system-status.h>
#include <soc/rockchip/rockchip-system-status.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include "dev.h"
#include "procfs.h"
#include <linux/kthread.h>
#include <linux/of_reserved_mem.h>
#include <linux/of_address.h>
#include <dt-bindings/mfd/rockchip-flexbus.h>
int flexbus_cif_debug;
module_param_named(debug, flexbus_cif_debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
static DEFINE_MUTEX(flexbus_cif_mutex);
static LIST_HEAD(flexbus_cif_device_list);
void flexbus_cif_write_register(struct flexbus_cif_device *dev,
u32 offset, u32 val)
{
rockchip_flexbus_writel(dev->fb_dev, offset, val);
v4l2_dbg(4, flexbus_cif_debug, &dev->v4l2_dev,
"write reg[0x%x]:0x%x!!!\n",
offset, val);
}
void flexbus_cif_write_register_or(struct flexbus_cif_device *dev,
u32 offset, u32 val)
{
unsigned int reg_val = 0x0;
reg_val = rockchip_flexbus_readl(dev->fb_dev, offset);
reg_val |= val;
rockchip_flexbus_writel(dev->fb_dev, offset, reg_val);
v4l2_dbg(4, flexbus_cif_debug, &dev->v4l2_dev,
"write or reg[0x%x]:0x%x!!!\n",
offset, val);
}
void flexbus_cif_write_register_and(struct flexbus_cif_device *dev,
u32 offset, u32 val)
{
unsigned int reg_val = 0x0;
reg_val = rockchip_flexbus_readl(dev->fb_dev, offset);
reg_val &= val;
rockchip_flexbus_writel(dev->fb_dev, offset, reg_val);
v4l2_dbg(4, flexbus_cif_debug, &dev->v4l2_dev,
"write and reg[0x%x]:0x%x!!!\n",
offset, val);
}
unsigned int flexbus_cif_read_register(struct flexbus_cif_device *dev, u32 offset)
{
unsigned int val = 0x0;
val = rockchip_flexbus_readl(dev->fb_dev, offset);
return val;
}
/**************************** pipeline operations *****************************/
static int __cif_pipeline_prepare(struct flexbus_cif_pipeline *p,
struct media_entity *me)
{
struct v4l2_subdev *sd;
int i;
p->num_subdevs = 0;
memset(p->subdevs, 0, sizeof(p->subdevs));
while (1) {
struct media_pad *pad = NULL;
/* Find remote source pad */
for (i = 0; i < me->num_pads; i++) {
struct media_pad *spad = &me->pads[i];
if (!(spad->flags & MEDIA_PAD_FL_SINK))
continue;
pad = media_pad_remote_pad_first(spad);
if (pad)
break;
}
if (!pad)
break;
sd = media_entity_to_v4l2_subdev(pad->entity);
p->subdevs[p->num_subdevs++] = sd;
me = &sd->entity;
if (me->num_pads == 1)
break;
}
return 0;
}
static int __cif_pipeline_s_cif_clk(struct flexbus_cif_pipeline *p)
{
return 0;
}
static int flexbus_cif_pipeline_open(struct flexbus_cif_pipeline *p,
struct media_entity *me,
bool prepare)
{
int ret;
if (WARN_ON(!p || !me))
return -EINVAL;
if (atomic_inc_return(&p->power_cnt) > 1)
return 0;
/* go through media graphic and get subdevs */
if (prepare)
__cif_pipeline_prepare(p, me);
if (!p->num_subdevs)
return -EINVAL;
ret = __cif_pipeline_s_cif_clk(p);
if (ret < 0)
return ret;
return 0;
}
static int flexbus_cif_pipeline_close(struct flexbus_cif_pipeline *p)
{
atomic_dec_return(&p->power_cnt);
return 0;
}
/*
* stream-on order: isp_subdev, mipi dphy, sensor
* stream-off order: mipi dphy, sensor, isp_subdev
*/
static int flexbus_cif_pipeline_set_stream(struct flexbus_cif_pipeline *p, bool on)
{
int i, ret = 0;
if ((on && atomic_inc_return(&p->stream_cnt) > 1) ||
(!on && atomic_dec_return(&p->stream_cnt) > 0))
return 0;
/* phy -> sensor */
for (i = 0; i < p->num_subdevs; i++) {
ret = v4l2_subdev_call(p->subdevs[i], video, s_stream, on);
if (on && ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
goto err_stream_off;
}
return 0;
err_stream_off:
for (--i; i >= 0; --i)
v4l2_subdev_call(p->subdevs[i], video, s_stream, false);
return ret;
}
static int flexbus_cif_create_link(struct flexbus_cif_device *dev,
struct flexbus_cif_sensor_info *sensor)
{
struct flexbus_cif_sensor_info linked_sensor;
struct media_entity *source_entity, *sink_entity;
int ret = 0;
u32 pad;
linked_sensor.sd = sensor->sd;
memcpy(&linked_sensor.mbus, &sensor->mbus,
sizeof(struct v4l2_mbus_config));
for (pad = 0; pad < linked_sensor.sd->entity.num_pads; pad++) {
if (linked_sensor.sd->entity.pads[pad].flags &
MEDIA_PAD_FL_SOURCE) {
if (pad == linked_sensor.sd->entity.num_pads) {
dev_err(dev->dev,
"failed to find src pad for %s\n",
linked_sensor.sd->name);
break;
}
if (linked_sensor.mbus.type == V4L2_MBUS_BT656 ||
linked_sensor.mbus.type == V4L2_MBUS_PARALLEL) {
source_entity = &linked_sensor.sd->entity;
sink_entity = &dev->stream[FLEXBUS_CIF_STREAM_CIF].vnode.vdev.entity;
ret = media_create_pad_link(source_entity,
pad,
sink_entity,
0,
MEDIA_LNK_FL_ENABLED);
if (ret)
dev_err(dev->dev, "failed to create link for %s\n",
linked_sensor.sd->name);
break;
}
}
}
return ret;
}
/***************************** media controller *******************************/
static int flexbus_cif_create_links(struct flexbus_cif_device *dev)
{
u32 s = 0;
/* sensor links(or mipi-phy) */
for (s = 0; s < dev->num_sensors; ++s) {
struct flexbus_cif_sensor_info *sensor = &dev->sensors[s];
flexbus_cif_create_link(dev, sensor);
}
return 0;
}
static int _set_pipeline_default_fmt(struct flexbus_cif_device *dev)
{
flexbus_cif_set_default_fmt(dev);
return 0;
}
static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
{
struct flexbus_cif_device *dev;
struct flexbus_cif_sensor_info *sensor;
struct v4l2_subdev *sd;
struct v4l2_device *v4l2_dev = NULL;
int ret, index;
dev = container_of(notifier, struct flexbus_cif_device, notifier);
v4l2_dev = &dev->v4l2_dev;
for (index = 0; index < dev->num_sensors; index++) {
sensor = &dev->sensors[index];
list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
if (sd->ops) {
if (sd == sensor->sd) {
ret = v4l2_subdev_call(sd,
pad,
get_mbus_config,
0,
&sensor->mbus);
if (ret)
v4l2_err(v4l2_dev,
"get mbus config failed for linking\n");
}
}
}
if (sensor->mbus.type == V4L2_MBUS_PARALLEL ||
sensor->mbus.type == V4L2_MBUS_BT656) {
ret = flexbus_cif_register_cif_sof_subdev(dev);
if (ret < 0) {
v4l2_err(&dev->v4l2_dev,
"Err: register cif sof subdev failed!!!\n");
goto notifier_end;
}
break;
}
}
ret = flexbus_cif_create_links(dev);
if (ret < 0)
goto unregister_cif;
ret = v4l2_device_register_subdev_nodes(&dev->v4l2_dev);
if (ret < 0)
goto unregister_cif;
ret = _set_pipeline_default_fmt(dev);
if (ret < 0)
goto unregister_cif;
v4l2_info(&dev->v4l2_dev, "Async subdev notifier completed\n");
return ret;
unregister_cif:
flexbus_cif_unregister_cif_sof_subdev(dev);
notifier_end:
return ret;
}
struct flexbus_cif_async_subdev {
struct v4l2_async_subdev asd;
struct v4l2_mbus_config mbus;
int lanes;
};
static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd)
{
struct flexbus_cif_device *cif_dev = container_of(notifier,
struct flexbus_cif_device, notifier);
struct flexbus_cif_async_subdev *s_asd = container_of(asd,
struct flexbus_cif_async_subdev, asd);
if (cif_dev->num_sensors == ARRAY_SIZE(cif_dev->sensors)) {
v4l2_err(&cif_dev->v4l2_dev,
"%s: the num of subdev is beyond %d\n",
__func__, cif_dev->num_sensors);
return -EBUSY;
}
cif_dev->sensors[cif_dev->num_sensors].lanes = s_asd->lanes;
cif_dev->sensors[cif_dev->num_sensors].mbus = s_asd->mbus;
cif_dev->sensors[cif_dev->num_sensors].sd = subdev;
++cif_dev->num_sensors;
v4l2_err(subdev, "Async registered subdev\n");
return 0;
}
static int flexbus_cif_fwnode_parse(struct device *dev,
struct v4l2_fwnode_endpoint *vep,
struct v4l2_async_subdev *asd)
{
struct flexbus_cif_async_subdev *rk_asd =
container_of(asd, struct flexbus_cif_async_subdev, asd);
if (vep->bus_type != V4L2_MBUS_BT656 &&
vep->bus_type != V4L2_MBUS_PARALLEL)
return 0;
rk_asd->mbus.type = vep->bus_type;
return 0;
}
static const struct v4l2_async_notifier_operations subdev_notifier_ops = {
.bound = subdev_notifier_bound,
.complete = subdev_notifier_complete,
};
static int cif_subdev_notifier(struct flexbus_cif_device *cif_dev)
{
struct v4l2_async_notifier *ntf = &cif_dev->notifier;
struct device *dev = cif_dev->dev;
int ret;
v4l2_async_nf_init(ntf);
ret = v4l2_async_nf_parse_fwnode_endpoints(
dev, ntf, sizeof(struct flexbus_cif_async_subdev), flexbus_cif_fwnode_parse);
if (ret < 0) {
v4l2_err(&cif_dev->v4l2_dev,
"%s: parse fwnode failed\n", __func__);
return ret;
}
ntf->ops = &subdev_notifier_ops;
ret = v4l2_async_nf_register(&cif_dev->v4l2_dev, ntf);
return ret;
}
/***************************** platform deive *******************************/
static int flexbus_cif_register_platform_subdevs(struct flexbus_cif_device *cif_dev)
{
int stream_num = 0, ret;
stream_num = FLEXBUS_CIF_SINGLE_STREAM;
ret = flexbus_cif_register_stream_vdevs(cif_dev, stream_num,
false);
if (ret < 0) {
dev_err(cif_dev->dev, "cif register stream[%d] failed!\n", stream_num);
return -EINVAL;
}
ret = cif_subdev_notifier(cif_dev);
if (ret < 0) {
v4l2_err(&cif_dev->v4l2_dev,
"Failed to register subdev notifier(%d)\n", ret);
goto err_unreg_stream_vdev;
}
return 0;
err_unreg_stream_vdev:
flexbus_cif_unregister_stream_vdevs(cif_dev, stream_num);
return ret;
}
static void flexbus_cif_irq_handler(struct rockchip_flexbus *fb_dev, u32 isr)
{
struct flexbus_cif_device *cif_dev = (struct flexbus_cif_device *)fb_dev->fb1_data;
flexbus_cif_irq_pingpong(cif_dev, isr);
flexbus_cif_write_register(cif_dev, FLEXBUS_ICR, isr);
if (cif_dev->err_state &&
(!work_busy(&cif_dev->err_state_work.work))) {
cif_dev->err_state_work.err_state = cif_dev->err_state;
cif_dev->err_state = 0;
schedule_work(&cif_dev->err_state_work.work);
}
}
static int flexbus_cif_plat_init(struct flexbus_cif_device *cif_dev, struct device_node *node)
{
struct device *dev = cif_dev->dev;
struct v4l2_device *v4l2_dev;
int ret;
mutex_init(&cif_dev->stream_lock);
atomic_set(&cif_dev->pipe.power_cnt, 0);
atomic_set(&cif_dev->pipe.stream_cnt, 0);
atomic_set(&cif_dev->power_cnt, 0);
cif_dev->pipe.open = flexbus_cif_pipeline_open;
cif_dev->pipe.close = flexbus_cif_pipeline_close;
cif_dev->pipe.set_stream = flexbus_cif_pipeline_set_stream;
cif_dev->id_use_cnt = 0;
INIT_WORK(&cif_dev->err_state_work.work, flexbus_cif_err_print_work);
flexbus_cif_stream_init(cif_dev, FLEXBUS_CIF_STREAM_CIF);
cif_dev->is_dma_sg_ops = true;
#if defined(CONFIG_ROCKCHIP_FLEXBUS_CIF_USE_DUMMY_BUF)
cif_dev->is_use_dummybuf = true;
#else
cif_dev->is_use_dummybuf = false;
#endif
strscpy(cif_dev->media_dev.model, "flexbus-cif",
sizeof(cif_dev->media_dev.model));
cif_dev->media_dev.dev = dev;
v4l2_dev = &cif_dev->v4l2_dev;
v4l2_dev->mdev = &cif_dev->media_dev;
strscpy(v4l2_dev->name, "flexbus-cif", sizeof(v4l2_dev->name));
ret = v4l2_device_register(cif_dev->dev, &cif_dev->v4l2_dev);
if (ret < 0)
return ret;
media_device_init(&cif_dev->media_dev);
ret = media_device_register(&cif_dev->media_dev);
if (ret < 0) {
v4l2_err(v4l2_dev, "Failed to register media device: %d\n",
ret);
goto err_unreg_v4l2_dev;
}
/* create & register platefom subdev (from of_node) */
ret = flexbus_cif_register_platform_subdevs(cif_dev);
if (ret < 0)
goto err_unreg_media_dev;
mutex_lock(&flexbus_cif_mutex);
list_add_tail(&cif_dev->list, &flexbus_cif_device_list);
mutex_unlock(&flexbus_cif_mutex);
return 0;
err_unreg_media_dev:
media_device_unregister(&cif_dev->media_dev);
err_unreg_v4l2_dev:
v4l2_device_unregister(&cif_dev->v4l2_dev);
return ret;
}
static int flexbus_cif_plat_uninit(struct flexbus_cif_device *cif_dev)
{
int stream_num = 0;
if (cif_dev->active_sensor->mbus.type == V4L2_MBUS_BT656 ||
cif_dev->active_sensor->mbus.type == V4L2_MBUS_PARALLEL)
flexbus_cif_unregister_cif_sof_subdev(cif_dev);
media_device_unregister(&cif_dev->media_dev);
v4l2_device_unregister(&cif_dev->v4l2_dev);
stream_num = FLEXBUS_CIF_SINGLE_STREAM;
flexbus_cif_unregister_stream_vdevs(cif_dev, stream_num);
return 0;
}
static const struct flexbus_cif_match_data cif_match_data = {
.chip_id = RK_FLEXBUS_CIF_RK3576,
};
static const struct of_device_id flexbus_cif_plat_of_match[] = {
{
.compatible = "rockchip,flexbus-cif-rk3576",
.data = &cif_match_data,
},
{},
};
static int flexbus_cif_plat_probe(struct platform_device *pdev)
{
struct rockchip_flexbus *rkfb = dev_get_drvdata(pdev->dev.parent);
const struct of_device_id *match;
struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct flexbus_cif_device *cif_dev;
const struct flexbus_cif_match_data *data = NULL;
int ret;
dev_info(dev, "czf flexbus_cif driver probe\n");
if (rkfb->opmode1 != ROCKCHIP_FLEXBUS1_OPMODE_CIF) {
dev_err(&pdev->dev, "flexbus1 opmode mismatch!\n");
return -ENODEV;
}
match = of_match_node(flexbus_cif_plat_of_match, node);
if (IS_ERR(match))
return PTR_ERR(match);
cif_dev = devm_kzalloc(dev, sizeof(*cif_dev), GFP_KERNEL);
if (!cif_dev)
return -ENOMEM;
dev_set_drvdata(dev, cif_dev);
cif_dev->dev = dev;
data = match->data;
cif_dev->chip_id = data->chip_id;
ret = flexbus_cif_plat_init(cif_dev, node);
if (ret)
return ret;
cif_dev->fb_dev = rkfb;
rkfb->fb1_data = cif_dev;
rkfb->fb1_isr = flexbus_cif_irq_handler;
if (flexbus_cif_proc_init(cif_dev))
dev_warn(dev, "dev:%s create proc failed\n", dev_name(dev));
pm_runtime_enable(&pdev->dev);
return 0;
}
static int flexbus_cif_plat_remove(struct platform_device *pdev)
{
struct flexbus_cif_device *cif_dev = platform_get_drvdata(pdev);
flexbus_cif_plat_uninit(cif_dev);
flexbus_cif_proc_cleanup(cif_dev);
return 0;
}
static int __maybe_unused flexbus_cif_runtime_suspend(struct device *dev)
{
struct flexbus_cif_device *cif_dev = dev_get_drvdata(dev);
int ret = 0;
if (atomic_dec_return(&cif_dev->power_cnt))
return 0;
return (ret > 0) ? 0 : ret;
}
static int __maybe_unused flexbus_cif_runtime_resume(struct device *dev)
{
struct flexbus_cif_device *cif_dev = dev_get_drvdata(dev);
int ret = 0;
if (atomic_inc_return(&cif_dev->power_cnt) > 1)
return 0;
return (ret > 0) ? 0 : ret;
}
static int __maybe_unused __flexbus_cif_clr_unready_dev(void)
{
struct flexbus_cif_device *cif_dev;
mutex_lock(&flexbus_cif_mutex);
list_for_each_entry(cif_dev, &flexbus_cif_device_list, list) {
v4l2_async_notifier_clr_unready_dev(&cif_dev->notifier);
}
mutex_unlock(&flexbus_cif_mutex);
return 0;
}
static int flexbus_cif_clr_unready_dev_param_set(const char *val, const struct kernel_param *kp)
{
#ifdef MODULE
__flexbus_cif_clr_unready_dev();
#endif
return 0;
}
module_param_call(flexbus_cif_clr_unready_dev, flexbus_cif_clr_unready_dev_param_set, NULL, NULL, 0200);
MODULE_PARM_DESC(flexbus_cif_clr_unready_dev, "clear unready devices");
#ifndef MODULE
int flexbus_cif_clr_unready_dev(void)
{
__flexbus_cif_clr_unready_dev();
return 0;
}
late_initcall(flexbus_cif_clr_unready_dev);
#endif
static const struct dev_pm_ops flexbus_cif_plat_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(flexbus_cif_runtime_suspend, flexbus_cif_runtime_resume, NULL)
};
struct platform_driver flexbus_cif_plat_drv = {
.driver = {
.name = CIF_DRIVER_NAME,
.of_match_table = of_match_ptr(flexbus_cif_plat_of_match),
.pm = &flexbus_cif_plat_pm_ops,
},
.probe = flexbus_cif_plat_probe,
.remove = flexbus_cif_plat_remove,
};
static int flexbus_cif_plat_drv_init(void)
{
return platform_driver_register(&flexbus_cif_plat_drv);
}
static void __exit flexbus_cif_plat_drv_exit(void)
{
platform_driver_unregister(&flexbus_cif_plat_drv);
}
module_init(flexbus_cif_plat_drv_init);
module_exit(flexbus_cif_plat_drv_exit);
MODULE_AUTHOR("Rockchip Camera/ISP team");
MODULE_DESCRIPTION("Rockchip flexbus cif platform driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,467 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rockchip Flexbus CIF Driver
*
* Copyright (C) 2024 Rockchip Electronics Co., Ltd.
*/
#ifndef _FLEXBUS_CIF_DEV_H
#define _FLEXBUS_CIF_DEV_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 <media/v4l2-mc.h>
#include <linux/workqueue.h>
#include <linux/rk-camera-module.h>
#include <linux/interrupt.h>
#include <linux/mfd/rockchip-flexbus.h>
#include "regs.h"
#define CIF_DRIVER_NAME "flexbus"
#define CIF_VIDEODEVICE_NAME "stream"
#define FLEXBUS_CIF_SINGLE_STREAM 1
#define FLEXBUS_CIF_STREAM_CIF 0
#define CIF_CIF_VDEV_NAME CIF_VIDEODEVICE_NAME "_cif"
#define FLEXBUS_CIF_PLANE_Y 0
#define FLEXBUS_CIF_PLANE_CBCR 1
/*
* RK1808 support 5 channel inputs simultaneously:
* cif + 4 mipi virtual channels;
* RV1126/RK356X support 4 channels of BT.656/BT.1120/MIPI
*/
#define FLEXBUS_CIF_MULTI_STREAMS_NUM 5
#define FLEXBUS_CIF_MAX_STREAM_CIF 4
#define FLEXBUS_CIF_MAX_SENSOR 2
#define FLEXBUS_CIF_MAX_PIPELINE 4
#define FLEXBUS_CIF_DEFAULT_WIDTH 640
#define FLEXBUS_CIF_DEFAULT_HEIGHT 480
#define FLEXBUS_CIF_MAX_INTERVAL_NS 5000000
/*
* for distinguishing cropping from senosr or usr
*/
#define CROP_SRC_SENSOR_MASK (0x1 << 0)
#define CROP_SRC_USR_MASK (0x1 << 1)
enum flexbus_cif_yuvaddr_state {
FLEXBUS_CIF_YUV_ADDR_STATE_UPDATE = 0x0,
FLEXBUS_CIF_YUV_ADDR_STATE_INIT = 0x1
};
enum flexbus_cif_state {
FLEXBUS_CIF_STATE_DISABLED,
FLEXBUS_CIF_STATE_READY,
FLEXBUS_CIF_STATE_STREAMING,
FLEXBUS_CIF_STATE_RESET_IN_STREAMING,
};
enum flexbus_cif_clk_edge {
FLEXBUS_CIF_CLK_RISING = 0x0,
FLEXBUS_CIF_CLK_FALLING,
};
/*
* for distinguishing cropping from senosr or usr
*/
enum flexbus_cif_crop_src {
CROP_SRC_ACT = 0x0,
CROP_SRC_SENSOR,
CROP_SRC_USR,
CROP_SRC_MAX
};
enum flexbus_cif_chip_id {
RK_FLEXBUS_CIF_RK3576,
};
struct flexbus_cif_match_data {
int chip_id;
};
/*
* struct flexbus_cif_pipeline - An CIF hardware pipeline
*
* Capture device call other devices via pipeline
*
* @num_subdevs: number of linked subdevs
* @power_cnt: pipeline power count
* @stream_cnt: stream power count
*/
struct flexbus_cif_pipeline {
struct media_pipeline pipe;
int num_subdevs;
atomic_t power_cnt;
atomic_t stream_cnt;
struct v4l2_subdev *subdevs[FLEXBUS_CIF_MAX_PIPELINE];
int (*open)(struct flexbus_cif_pipeline *p,
struct media_entity *me, bool prepare);
int (*close)(struct flexbus_cif_pipeline *p);
int (*set_stream)(struct flexbus_cif_pipeline *p, bool on);
};
struct flexbus_cif_buffer {
struct vb2_v4l2_buffer vb;
struct list_head queue;
union {
u32 buff_addr[VIDEO_MAX_PLANES];
void *vaddr[VIDEO_MAX_PLANES];
};
struct dma_buf *dbuf;
u64 fe_timestamp;
};
struct flexbus_cif_dummy_buffer {
struct vb2_buffer vb;
struct vb2_queue vb2_queue;
struct list_head list;
struct dma_buf *dbuf;
dma_addr_t dma_addr;
struct page **pages;
void *mem_priv;
void *vaddr;
u32 size;
int dma_fd;
bool is_need_vaddr;
bool is_need_dbuf;
bool is_need_dmafd;
bool is_free;
};
extern int flexbus_cif_debug;
/*
* struct flexbus_cif_sensor_info - Sensor infomations
* @sd: v4l2 subdev of sensor
* @mbus: media bus configuration
* @fi: v4l2 subdev frame interval
* @lanes: lane num of sensor
* @raw_rect: raw output rectangle of sensor, not crop or selection
* @selection: selection info of sensor
*/
struct flexbus_cif_sensor_info {
struct v4l2_subdev *sd;
struct v4l2_mbus_config mbus;
struct v4l2_subdev_frame_interval fi;
int lanes;
struct v4l2_rect raw_rect;
struct v4l2_subdev_selection selection;
int dsi_input_en;
};
enum cif_fmt_type {
CIF_FMT_TYPE_YUV = 0,
CIF_FMT_TYPE_RAW,
};
/*
* struct cif_output_fmt - The output format
*
* @bpp: bits per pixel for each cplanes
* @fourcc: pixel format in fourcc
* @fmt_val: the fmt val corresponding to CIF_FOR register
* @csi_fmt_val: the fmt val corresponding to CIF_CSI_ID_CTRL
* @cplanes: number of colour planes
* @mplanes: number of planes for format
* @raw_bpp: bits per pixel for raw format
* @fmt_type: image format, raw or yuv
*/
struct cif_output_fmt {
u8 bpp[VIDEO_MAX_PLANES];
u32 fourcc;
u32 fmt_val;
u32 cif_yuv_order;
u8 cplanes;
u8 mplanes;
u8 raw_bpp;
};
/*
* struct cif_input_fmt - The input mbus format from sensor
*
* @mbus_code: mbus format
* @cif_fmt_val: the fmt val corresponding to CIF_FOR register
* @csi_fmt_val: the fmt val corresponding to CIF_CSI_ID_CTRL
* @fmt_type: image format, raw or yuv
* @field: the field type of the input from sensor
*/
struct cif_input_fmt {
u32 mbus_code;
u32 cif_yuv_order;
enum v4l2_field field;
};
struct flexbus_cif_vdev_node {
struct vb2_queue buf_queue;
/* vfd lock */
struct mutex vlock;
struct video_device vdev;
struct media_pad pad;
};
/*
* the mark that csi frame0/1 interrupts enabled
* in CIF_MIPI_INTEN
*/
enum cif_frame_ready {
CIF_CSI_FRAME0_READY = 0x1,
CIF_CSI_FRAME1_READY
};
/* struct flexbus_cif_hdr - hdr configured
* @op_mode: hdr optional mode
*/
struct flexbus_cif_hdr {
u8 mode;
};
/* struct flexbus_cif_fps_stats - take notes on timestamp of buf
* @frm0_timestamp: timesstamp of buf in frm0
* @frm1_timestamp: timesstamp of buf in frm1
*/
struct flexbus_cif_fps_stats {
u64 frm0_timestamp;
u64 frm1_timestamp;
};
/* struct flexbus_cif_fps_stats - take notes on timestamp of buf
* @fs_timestamp: timesstamp of frame start
* @fe_timestamp: timesstamp of frame end
* @wk_timestamp: timesstamp of buf send to user in wake up mode
* @readout_time: one frame of readout time
* @early_time: early time of buf send to user
* @total_time: totaltime of readout time in hdr
*/
struct flexbus_cif_readout_stats {
u64 fs_timestamp;
u64 fe_timestamp;
u64 wk_timestamp;
u64 readout_time;
u64 early_time;
u64 total_time;
};
/*
* struct flexbus_cif_stream - Stream states TODO
*
* @vbq_lock: lock to protect buf_queue
* @buf_queue: queued buffer list
* @dummy_buf: dummy space to store dropped data
* @crop_enable: crop status when stream off
* @crop_dyn_en: crop status when streaming
* flexbus_cif use shadowsock registers, so it need two buffer at a time
* @curr_buf: the buffer used for current frame
* @next_buf: the buffer used for next frame
* @fps_lock: to protect parameters about calculating fps
*/
struct flexbus_cif_stream {
unsigned id:3;
struct flexbus_cif_device *cif_dev;
struct flexbus_cif_vdev_node vnode;
enum flexbus_cif_state state;
wait_queue_head_t wq_stopped;
unsigned int frame_idx;
u32 frame_phase;
u32 frame_phase_cache;
unsigned int crop_mask;
/* lock between irq and buf_queue */
struct list_head buf_head;
struct flexbus_cif_buffer *curr_buf;
struct flexbus_cif_buffer *next_buf;
spinlock_t vbq_lock; /* vfd lock */
spinlock_t fps_lock;
/* TODO: pad for cif and mipi separately? */
struct media_pad pad;
const struct cif_output_fmt *cif_fmt_out;
const struct cif_input_fmt *cif_fmt_in;
struct v4l2_pix_format_mplane pixm;
struct v4l2_rect crop[CROP_SRC_MAX];
struct flexbus_cif_fps_stats fps_stats;
struct flexbus_cif_readout_stats readout;
unsigned int capture_mode;
int dma_en;
int to_en_dma;
int to_stop_dma;
int buf_owner;
int buf_replace_cnt;
struct list_head rx_buf_head_vicap;
unsigned int cur_stream_mode;
int lack_buf_cnt;
unsigned int buf_wake_up_cnt;
struct tasklet_struct vb_done_tasklet;
struct list_head vb_done_list;
int last_rx_buf_idx;
int last_frame_idx;
int new_fource_idx;
atomic_t buf_cnt;
bool stopping;
bool crop_enable;
bool crop_dyn_en;
bool is_compact;
bool is_buf_active;
bool is_high_align;
bool to_en_scale;
bool is_finish_stop_dma;
bool is_in_vblank;
bool is_change_toisp;
bool is_stop_capture;
};
struct flexbus_cif_cif_sof_subdev {
struct flexbus_cif_device *cif_dev;
struct v4l2_subdev sd;
atomic_t frm_sync_seq;
};
static inline struct flexbus_cif_buffer *to_flexbus_cif_buffer(struct vb2_v4l2_buffer *vb)
{
return container_of(vb, struct flexbus_cif_buffer, vb);
}
static inline
struct flexbus_cif_vdev_node *vdev_to_node(struct video_device *vdev)
{
return container_of(vdev, struct flexbus_cif_vdev_node, vdev);
}
static inline
struct flexbus_cif_stream *to_flexbus_cif_stream(struct flexbus_cif_vdev_node *vnode)
{
return container_of(vnode, struct flexbus_cif_stream, vnode);
}
static inline struct flexbus_cif_vdev_node *queue_to_node(struct vb2_queue *q)
{
return container_of(q, struct flexbus_cif_vdev_node, buf_queue);
}
static inline struct vb2_queue *to_vb2_queue(struct file *file)
{
struct flexbus_cif_vdev_node *vnode = video_drvdata(file);
return &vnode->buf_queue;
}
enum flexbus_cif_err_state {
FLEXBUS_CIF_ERR_ID0_NOT_BUF = 0x1,
FLEXBUS_CIF_ERR_ID1_NOT_BUF = 0x2,
FLEXBUS_CIF_ERR_ID2_NOT_BUF = 0x4,
FLEXBUS_CIF_ERR_ID3_NOT_BUF = 0x8,
FLEXBUS_CIF_ERR_ID0_TRIG_SIMULT = 0x10,
FLEXBUS_CIF_ERR_ID1_TRIG_SIMULT = 0x20,
FLEXBUS_CIF_ERR_ID2_TRIG_SIMULT = 0x40,
FLEXBUS_CIF_ERR_ID3_TRIG_SIMULT = 0x80,
FLEXBUS_CIF_ERR_SIZE = 0x100,
FLEXBUS_CIF_ERR_OVERFLOW = 0x200,
FLEXBUS_CIF_ERR_BANDWIDTH_LACK = 0x400,
FLEXBUS_CIF_ERR_BUS = 0X800,
FLEXBUS_CIF_ERR_ID0_MULTI_FS = 0x1000,
FLEXBUS_CIF_ERR_ID1_MULTI_FS = 0x2000,
FLEXBUS_CIF_ERR_ID2_MULTI_FS = 0x4000,
FLEXBUS_CIF_ERR_ID3_MULTI_FS = 0x8000,
FLEXBUS_CIF_ERR_PIXEL = 0x10000,
FLEXBUS_CIF_ERR_LINE = 0x20000,
};
struct flexbus_cif_err_state_work {
struct work_struct work;
u64 last_timestamp;
u32 err_state;
u32 intstat;
u32 lastline;
u32 lastpixel;
u32 fifo_dnum;
};
struct flexbus_cif_irq_stats {
u64 cif_bus_err_cnt;
u64 cif_overflow_cnt;
u64 cif_line_err_cnt;
u64 cif_pix_err_cnt;
u64 cif_size_err_cnt;
u64 cif_bwidth_lack_cnt;
u64 frm_end_cnt[4];
u64 not_active_buf_cnt[4];
u64 trig_simult_cnt[4];
u64 all_err_cnt;
};
/*
* struct flexbus_cif_device - ISP platform device
* @base_addr: base register address
* @active_sensor: sensor in-use, set when streaming on
* @stream: capture video device
*/
struct flexbus_cif_device {
struct list_head list;
struct device *dev;
struct rockchip_flexbus *fb_dev;
struct v4l2_device v4l2_dev;
struct media_device media_dev;
struct v4l2_async_notifier notifier;
int chip_id;
struct flexbus_cif_sensor_info sensors[FLEXBUS_CIF_MAX_SENSOR];
u32 num_sensors;
struct flexbus_cif_sensor_info *active_sensor;
struct flexbus_cif_sensor_info terminal_sensor;
struct flexbus_cif_stream stream[FLEXBUS_CIF_MULTI_STREAMS_NUM];
struct flexbus_cif_pipeline pipe;
atomic_t stream_cnt;
atomic_t power_cnt;
struct mutex stream_lock; /* lock between streams */
struct flexbus_cif_cif_sof_subdev cif_sof_subdev;
struct flexbus_cif_dummy_buffer dummy_buf;
struct flexbus_cif_irq_stats irq_stats;
u32 err_state;
struct flexbus_cif_err_state_work err_state_work;
struct proc_dir_entry *proc_dir;
int id_use_cnt;
bool is_use_dummybuf;
bool is_dma_sg_ops;
};
//extern struct platform_driver flexbus_cif_plat_drv;
void flexbus_cif_write_register(struct flexbus_cif_device *dev,
u32 offset, u32 val);
void flexbus_cif_write_register_or(struct flexbus_cif_device *dev,
u32 offset, u32 val);
void flexbus_cif_write_register_and(struct flexbus_cif_device *dev,
u32 offset, u32 val);
unsigned int flexbus_cif_read_register(struct flexbus_cif_device *dev,
u32 offset);
void flexbus_cif_write_grf_register(struct flexbus_cif_device *dev,
u32 offset, u32 val);
void flexbus_cif_unregister_stream_vdevs(struct flexbus_cif_device *dev,
int stream_num);
int flexbus_cif_register_stream_vdevs(struct flexbus_cif_device *dev,
int stream_num,
bool is_multi_input);
void flexbus_cif_stream_init(struct flexbus_cif_device *dev, u32 id);
void flexbus_cif_set_default_fmt(struct flexbus_cif_device *cif_dev);
void flexbus_cif_irq_pingpong(struct flexbus_cif_device *cif_dev, u32 isr);
int flexbus_cif_register_cif_sof_subdev(struct flexbus_cif_device *dev);
void flexbus_cif_unregister_cif_sof_subdev(struct flexbus_cif_device *dev);
int flexbus_cif_clr_unready_dev(void);
void flexbus_cif_err_print_work(struct work_struct *work);
#endif

View File

@@ -0,0 +1,339 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip Flexbus CIF Driver
*
* Copyright (C) 2024 Rockchip Electronics Co., Ltd.
*/
#include <linux/clk.h>
#include <linux/math64.h>
#include <linux/proc_fs.h>
#include <linux/sem.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/v4l2-mediabus.h>
#include "dev.h"
#include "procfs.h"
#ifdef CONFIG_PROC_FS
static const struct {
const char *name;
u32 mbus_code;
} mbus_formats[] = {
/* media bus code */
{ "RGB444_1X12", MEDIA_BUS_FMT_RGB444_1X12 },
{ "RGB444_2X8_PADHI_BE", MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE },
{ "RGB444_2X8_PADHI_LE", MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE },
{ "RGB555_2X8_PADHI_BE", MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE },
{ "RGB555_2X8_PADHI_LE", MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE },
{ "RGB565_1X16", MEDIA_BUS_FMT_RGB565_1X16 },
{ "BGR565_2X8_BE", MEDIA_BUS_FMT_BGR565_2X8_BE },
{ "BGR565_2X8_LE", MEDIA_BUS_FMT_BGR565_2X8_LE },
{ "RGB565_2X8_BE", MEDIA_BUS_FMT_RGB565_2X8_BE },
{ "RGB565_2X8_LE", MEDIA_BUS_FMT_RGB565_2X8_LE },
{ "RGB666_1X18", MEDIA_BUS_FMT_RGB666_1X18 },
{ "RBG888_1X24", MEDIA_BUS_FMT_RBG888_1X24 },
{ "RGB666_1X24_CPADHI", MEDIA_BUS_FMT_RGB666_1X24_CPADHI },
{ "RGB666_1X7X3_SPWG", MEDIA_BUS_FMT_RGB666_1X7X3_SPWG },
{ "BGR888_1X24", MEDIA_BUS_FMT_BGR888_1X24 },
{ "GBR888_1X24", MEDIA_BUS_FMT_GBR888_1X24 },
{ "RGB888_1X24", MEDIA_BUS_FMT_RGB888_1X24 },
{ "RGB888_2X12_BE", MEDIA_BUS_FMT_RGB888_2X12_BE },
{ "RGB888_2X12_LE", MEDIA_BUS_FMT_RGB888_2X12_LE },
{ "RGB888_1X7X4_SPWG", MEDIA_BUS_FMT_RGB888_1X7X4_SPWG },
{ "RGB888_1X7X4_JEIDA", MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA },
{ "ARGB8888_1X32", MEDIA_BUS_FMT_ARGB8888_1X32 },
{ "RGB888_1X32_PADHI", MEDIA_BUS_FMT_RGB888_1X32_PADHI },
{ "RGB101010_1X30", MEDIA_BUS_FMT_RGB101010_1X30 },
{ "RGB121212_1X36", MEDIA_BUS_FMT_RGB121212_1X36 },
{ "RGB161616_1X48", MEDIA_BUS_FMT_RGB161616_1X48 },
{ "Y8_1X8", MEDIA_BUS_FMT_Y8_1X8 },
{ "UV8_1X8", MEDIA_BUS_FMT_UV8_1X8 },
{ "UYVY8_1_5X8", MEDIA_BUS_FMT_UYVY8_1_5X8 },
{ "VYUY8_1_5X8", MEDIA_BUS_FMT_VYUY8_1_5X8 },
{ "YUYV8_1_5X8", MEDIA_BUS_FMT_YUYV8_1_5X8 },
{ "YVYU8_1_5X8", MEDIA_BUS_FMT_YVYU8_1_5X8 },
{ "UYVY8_2X8", MEDIA_BUS_FMT_UYVY8_2X8 },
{ "VYUY8_2X8", MEDIA_BUS_FMT_VYUY8_2X8 },
{ "YUYV8_2X8", MEDIA_BUS_FMT_YUYV8_2X8 },
{ "YVYU8_2X8", MEDIA_BUS_FMT_YVYU8_2X8 },
{ "Y10_1X10", MEDIA_BUS_FMT_Y10_1X10 },
{ "Y10_2X8_PADHI_LE", MEDIA_BUS_FMT_Y10_2X8_PADHI_LE },
{ "UYVY10_2X10", MEDIA_BUS_FMT_UYVY10_2X10 },
{ "VYUY10_2X10", MEDIA_BUS_FMT_VYUY10_2X10 },
{ "YUYV10_2X10", MEDIA_BUS_FMT_YUYV10_2X10 },
{ "YVYU10_2X10", MEDIA_BUS_FMT_YVYU10_2X10 },
{ "Y12_1X12", MEDIA_BUS_FMT_Y12_1X12 },
{ "UYVY12_2X12", MEDIA_BUS_FMT_UYVY12_2X12 },
{ "VYUY12_2X12", MEDIA_BUS_FMT_VYUY12_2X12 },
{ "YUYV12_2X12", MEDIA_BUS_FMT_YUYV12_2X12 },
{ "YVYU12_2X12", MEDIA_BUS_FMT_YVYU12_2X12 },
{ "UYVY8_1X16", MEDIA_BUS_FMT_UYVY8_1X16 },
{ "VYUY8_1X16", MEDIA_BUS_FMT_VYUY8_1X16 },
{ "YUYV8_1X16", MEDIA_BUS_FMT_YUYV8_1X16 },
{ "YVYU8_1X16", MEDIA_BUS_FMT_YVYU8_1X16 },
{ "YDYUYDYV8_1X16", MEDIA_BUS_FMT_YDYUYDYV8_1X16 },
{ "UYVY10_1X20", MEDIA_BUS_FMT_UYVY10_1X20 },
{ "VYUY10_1X20", MEDIA_BUS_FMT_VYUY10_1X20 },
{ "YUYV10_1X20", MEDIA_BUS_FMT_YUYV10_1X20 },
{ "YVYU10_1X20", MEDIA_BUS_FMT_YVYU10_1X20 },
{ "VUY8_1X24", MEDIA_BUS_FMT_VUY8_1X24 },
{ "YUV8_1X24", MEDIA_BUS_FMT_YUV8_1X24 },
{ "UYYVYY8_0_5X24", MEDIA_BUS_FMT_UYYVYY8_0_5X24 },
{ "UYVY12_1X24", MEDIA_BUS_FMT_UYVY12_1X24 },
{ "VYUY12_1X24", MEDIA_BUS_FMT_VYUY12_1X24 },
{ "YUYV12_1X24", MEDIA_BUS_FMT_YUYV12_1X24 },
{ "YVYU12_1X24", MEDIA_BUS_FMT_YVYU12_1X24 },
{ "YUV10_1X30", MEDIA_BUS_FMT_YUV10_1X30 },
{ "UYYVYY10_0_5X30", MEDIA_BUS_FMT_UYYVYY10_0_5X30 },
{ "AYUV8_1X32", MEDIA_BUS_FMT_AYUV8_1X32 },
{ "UYYVYY12_0_5X36", MEDIA_BUS_FMT_UYYVYY12_0_5X36 },
{ "YUV12_1X36", MEDIA_BUS_FMT_YUV12_1X36 },
{ "YUV16_1X48", MEDIA_BUS_FMT_YUV16_1X48 },
{ "UYYVYY16_0_5X48", MEDIA_BUS_FMT_UYYVYY16_0_5X48 },
{ "SBGGR8_1X8", MEDIA_BUS_FMT_SBGGR8_1X8 },
{ "SGBRG8_1X8", MEDIA_BUS_FMT_SGBRG8_1X8 },
{ "SGRBG8_1X8", MEDIA_BUS_FMT_SGRBG8_1X8 },
{ "SRGGB8_1X8", MEDIA_BUS_FMT_SRGGB8_1X8 },
{ "SBGGR10_ALAW8_1X8", MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8 },
{ "SGBRG10_ALAW8_1X8", MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8 },
{ "SGRBG10_ALAW8_1X8", MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8 },
{ "SRGGB10_ALAW8_1X8", MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8 },
{ "SBGGR10_DPCM8_1X8", MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8 },
{ "SGBRG10_DPCM8_1X8", MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8 },
{ "SGRBG10_DPCM8_1X8", MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8 },
{ "SRGGB10_DPCM8_1X8", MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8 },
{ "SBGGR10_2X8_PADHI_BE", MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE },
{ "SBGGR10_2X8_PADHI_LE", MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE },
{ "SBGGR10_2X8_PADLO_BE", MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE },
{ "SBGGR10_2X8_PADLO_LE", MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE },
{ "SBGGR10_1X10", MEDIA_BUS_FMT_SBGGR10_1X10 },
{ "SGBRG10_1X10", MEDIA_BUS_FMT_SGBRG10_1X10 },
{ "SGRBG10_1X10", MEDIA_BUS_FMT_SGRBG10_1X10 },
{ "SRGGB10_1X10", MEDIA_BUS_FMT_SRGGB10_1X10 },
{ "SBGGR12_1X12", MEDIA_BUS_FMT_SBGGR12_1X12 },
{ "SGBRG12_1X12", MEDIA_BUS_FMT_SGBRG12_1X12 },
{ "SGRBG12_1X12", MEDIA_BUS_FMT_SGRBG12_1X12 },
{ "SRGGB12_1X12", MEDIA_BUS_FMT_SRGGB12_1X12 },
{ "SBGGR14_1X14", MEDIA_BUS_FMT_SBGGR14_1X14 },
{ "SGBRG14_1X14", MEDIA_BUS_FMT_SGBRG14_1X14 },
{ "SGRBG14_1X14", MEDIA_BUS_FMT_SGRBG14_1X14 },
{ "SRGGB14_1X14", MEDIA_BUS_FMT_SRGGB14_1X14 },
{ "SBGGR16_1X16", MEDIA_BUS_FMT_SBGGR16_1X16 },
{ "SGBRG16_1X16", MEDIA_BUS_FMT_SGBRG16_1X16 },
{ "SGRBG16_1X16", MEDIA_BUS_FMT_SGRBG16_1X16 },
{ "SRGGB16_1X16", MEDIA_BUS_FMT_SRGGB16_1X16 },
{ "JPEG_1X8", MEDIA_BUS_FMT_JPEG_1X8 },
{ "S5C_UYVY_JPEG_1X8", MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8 },
{ "AHSV8888_1X32", MEDIA_BUS_FMT_AHSV8888_1X32 },
{ "FIXED", MEDIA_BUS_FMT_FIXED },
{ "Y8", MEDIA_BUS_FMT_Y8_1X8 },
{ "Y10", MEDIA_BUS_FMT_Y10_1X10 },
{ "Y12", MEDIA_BUS_FMT_Y12_1X12 },
{ "YUYV", MEDIA_BUS_FMT_YUYV8_1X16 },
{ "YUYV1_5X8", MEDIA_BUS_FMT_YUYV8_1_5X8 },
{ "YUYV2X8", MEDIA_BUS_FMT_YUYV8_2X8 },
{ "UYVY", MEDIA_BUS_FMT_UYVY8_1X16 },
{ "UYVY1_5X8", MEDIA_BUS_FMT_UYVY8_1_5X8 },
{ "UYVY2X8", MEDIA_BUS_FMT_UYVY8_2X8 },
{ "VUY24", MEDIA_BUS_FMT_VUY8_1X24 },
{ "SBGGR8", MEDIA_BUS_FMT_SBGGR8_1X8 },
{ "SGBRG8", MEDIA_BUS_FMT_SGBRG8_1X8 },
{ "SGRBG8", MEDIA_BUS_FMT_SGRBG8_1X8 },
{ "SRGGB8", MEDIA_BUS_FMT_SRGGB8_1X8 },
{ "SBGGR10", MEDIA_BUS_FMT_SBGGR10_1X10 },
{ "SGBRG10", MEDIA_BUS_FMT_SGBRG10_1X10 },
{ "SGRBG10", MEDIA_BUS_FMT_SGRBG10_1X10 },
{ "SRGGB10", MEDIA_BUS_FMT_SRGGB10_1X10 },
{ "SBGGR10_DPCM8", MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8 },
{ "SGBRG10_DPCM8", MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8 },
{ "SGRBG10_DPCM8", MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8 },
{ "SRGGB10_DPCM8", MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8 },
{ "SBGGR12", MEDIA_BUS_FMT_SBGGR12_1X12 },
{ "SGBRG12", MEDIA_BUS_FMT_SGBRG12_1X12 },
{ "SGRBG12", MEDIA_BUS_FMT_SGRBG12_1X12 },
{ "SRGGB12", MEDIA_BUS_FMT_SRGGB12_1X12 },
{ "AYUV32", MEDIA_BUS_FMT_AYUV8_1X32 },
{ "RBG24", MEDIA_BUS_FMT_RBG888_1X24 },
{ "RGB32", MEDIA_BUS_FMT_RGB888_1X32_PADHI },
{ "ARGB32", MEDIA_BUS_FMT_ARGB8888_1X32 },
/* v4l2 fourcc code */
{ "NV16", V4L2_PIX_FMT_NV16},
{ "NV61", V4L2_PIX_FMT_NV61},
{ "NV12", V4L2_PIX_FMT_NV12},
{ "NV21", V4L2_PIX_FMT_NV21},
{ "YUYV", V4L2_PIX_FMT_YUYV},
{ "YVYU", V4L2_PIX_FMT_YVYU},
{ "UYVY", V4L2_PIX_FMT_UYVY},
{ "VYUY", V4L2_PIX_FMT_VYUY},
{ "RGB3", V4L2_PIX_FMT_RGB24},
{ "RGBP", V4L2_PIX_FMT_RGB565},
{ "BGRH", V4L2_PIX_FMT_BGR666},
{ "RGGB", V4L2_PIX_FMT_SRGGB8},
{ "GRBG", V4L2_PIX_FMT_SGRBG8},
{ "GBRG", V4L2_PIX_FMT_SGBRG8},
{ "BA81", V4L2_PIX_FMT_SBGGR8},
{ "RG10", V4L2_PIX_FMT_SRGGB10},
{ "BA10", V4L2_PIX_FMT_SGRBG10},
{ "GB10", V4L2_PIX_FMT_SGBRG10},
{ "BG10", V4L2_PIX_FMT_SBGGR10},
{ "RG12", V4L2_PIX_FMT_SRGGB12},
{ "BA12", V4L2_PIX_FMT_SGRBG12},
{ "GB12", V4L2_PIX_FMT_SGBRG12},
{ "BG12", V4L2_PIX_FMT_SBGGR12},
{ "BYR2", V4L2_PIX_FMT_SBGGR16},
{ "Y16 ", V4L2_PIX_FMT_Y16},
};
static const char *flexbus_cif_pixelcode_to_string(u32 mbus_code)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(mbus_formats); ++i) {
if (mbus_formats[i].mbus_code == mbus_code)
return mbus_formats[i].name;
}
return "unknown";
}
static void flexbus_cif_show_format(struct flexbus_cif_device *dev, struct seq_file *f)
{
struct flexbus_cif_stream *stream = &dev->stream[0];
struct flexbus_cif_pipeline *pipe = &dev->pipe;
struct flexbus_cif_sensor_info *sensor = &dev->terminal_sensor;
struct v4l2_rect *rect = &sensor->raw_rect;
struct v4l2_subdev_frame_interval *interval = &sensor->fi;
struct v4l2_subdev_selection *sel = &sensor->selection;
u32 mbus_flags;
u64 fps, timestamp0, timestamp1;
unsigned long flags;
if (atomic_read(&pipe->stream_cnt) < 1)
return;
if (sensor) {
seq_puts(f, "Input Info:\n");
seq_printf(f, "\tsrc subdev:%s\n", sensor->sd->name);
mbus_flags = sensor->mbus.bus.parallel.flags;
if (sensor->mbus.type == V4L2_MBUS_PARALLEL ||
sensor->mbus.type == V4L2_MBUS_BT656) {
seq_printf(f, "\tinterface:%s\n",
sensor->mbus.type == V4L2_MBUS_PARALLEL ? "BT601" : "BT656/BT1120");
seq_printf(f, "\thref_pol:%s\n",
mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH ? "high active" : "low active");
seq_printf(f, "\tvsync_pol:%s\n",
mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH ? "high active" : "low active");
}
seq_printf(f, "\tformat:%s/%ux%u@%d\n",
flexbus_cif_pixelcode_to_string(stream->cif_fmt_in->mbus_code),
rect->width, rect->height,
interval->interval.denominator / interval->interval.numerator);
seq_printf(f, "\tcrop.bounds:(%u, %u)/%ux%u\n",
sel->r.left, sel->r.top,
sel->r.width, sel->r.height);
spin_lock_irqsave(&stream->fps_lock, flags);
timestamp0 = stream->fps_stats.frm0_timestamp;
timestamp1 = stream->fps_stats.frm1_timestamp;
spin_unlock_irqrestore(&stream->fps_lock, flags);
fps = timestamp0 > timestamp1 ?
timestamp0 - timestamp1 : timestamp1 - timestamp0;
fps = div_u64(fps, 1000000);
seq_puts(f, "Output Info:\n");
seq_printf(f, "\tformat:%s/%ux%u(%u,%u)\n",
flexbus_cif_pixelcode_to_string(stream->cif_fmt_out->fourcc),
sel->r.width, sel->r.height,
sel->r.left, sel->r.top);
seq_printf(f, "\tframe amount:%d\n", stream->frame_idx - 1);
seq_printf(f, "\trate:%llu ms\n", fps);
fps = div_u64(1000, fps);
seq_printf(f, "\tfps:%llu\n", fps);
seq_puts(f, "\tirq statistics:\n");
seq_printf(f, "\t\t\ttotal:%llu\n",
dev->irq_stats.frm_end_cnt[0] +
dev->irq_stats.frm_end_cnt[1] +
dev->irq_stats.frm_end_cnt[2] +
dev->irq_stats.frm_end_cnt[3] +
dev->irq_stats.all_err_cnt);
if (sensor->mbus.type == V4L2_MBUS_PARALLEL ||
sensor->mbus.type == V4L2_MBUS_BT656) {
seq_printf(f, "\t\t\tcif bus err:%llu\n", dev->irq_stats.cif_bus_err_cnt);
seq_printf(f, "\t\t\tcif pix err:%llu\n", dev->irq_stats.cif_pix_err_cnt);
seq_printf(f, "\t\t\tcif line err:%llu\n", dev->irq_stats.cif_line_err_cnt);
seq_printf(f, "\t\t\tcif over flow:%llu\n", dev->irq_stats.cif_overflow_cnt);
seq_printf(f, "\t\t\tcif bandwidth lack:%llu\n",
dev->irq_stats.cif_bwidth_lack_cnt);
seq_printf(f, "\t\t\tcif size err:%llu\n", dev->irq_stats.cif_size_err_cnt);
}
seq_printf(f, "\t\t\tnot active buf cnt:%llu %llu %llu %llu\n",
dev->irq_stats.not_active_buf_cnt[0],
dev->irq_stats.not_active_buf_cnt[1],
dev->irq_stats.not_active_buf_cnt[2],
dev->irq_stats.not_active_buf_cnt[3]);
seq_printf(f, "\t\t\tframe dma end:%llu %llu %llu %llu\n",
dev->irq_stats.frm_end_cnt[0],
dev->irq_stats.frm_end_cnt[1],
dev->irq_stats.frm_end_cnt[2],
dev->irq_stats.frm_end_cnt[3]);
seq_printf(f, "buf_cnt in drv: %d %d %d %d\n",
atomic_read(&dev->stream[0].buf_cnt),
atomic_read(&dev->stream[1].buf_cnt),
atomic_read(&dev->stream[2].buf_cnt),
atomic_read(&dev->stream[3].buf_cnt));
}
}
static int flexbus_cif_proc_show(struct seq_file *f, void *v)
{
struct flexbus_cif_device *dev = f->private;
if (dev)
flexbus_cif_show_format(dev, f);
else
seq_puts(f, "dev null\n");
return 0;
}
static int flexbus_cif_proc_open(struct inode *inode, struct file *file)
{
struct flexbus_cif_device *data = pde_data(inode);
return single_open(file, flexbus_cif_proc_show, data);
}
static const struct proc_ops flexbus_cif_proc_fops = {
.proc_open = flexbus_cif_proc_open,
.proc_release = single_release,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
};
int flexbus_cif_proc_init(struct flexbus_cif_device *dev)
{
dev->proc_dir = proc_create_data(dev_name(dev->dev), 0444,
NULL, &flexbus_cif_proc_fops,
dev);
if (!dev->proc_dir) {
dev_err(dev->dev, "create proc/%s failed!\n",
dev_name(dev->dev));
return -ENODEV;
}
return 0;
}
void flexbus_cif_proc_cleanup(struct flexbus_cif_device *dev)
{
remove_proc_entry(dev_name(dev->dev), NULL);
}
#endif

View File

@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rockchip Flexbus CIF Driver
*
* Copyright (C) 2024 Rockchip Electronics Co., Ltd.
*/
#ifndef _FLEXBUS_CIF_PROCFS_H
#define _FLEXBUS_CIF_PROCFS_H
#ifdef CONFIG_PROC_FS
int flexbus_cif_proc_init(struct flexbus_cif_device *dev);
void flexbus_cif_proc_cleanup(struct flexbus_cif_device *dev);
#else
static inline int flexbus_cif_proc_init(struct rkisp_device *dev)
{
return 0;
}
static inline void flexbus_cif_proc_cleanup(struct rkisp_device *dev)
{
}
#endif
#endif

View File

@@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rockchip Flexbus CIF Driver
*
* Copyright (C) 2024 Rockchip Electronics Co., Ltd.
*/
#ifndef _FLEXBUS_CIF_REGS_H
#define _FLEXBUS_CIF_REGS_H
#define GRF_FLEXBUS1_CON (0x6400)
#define CIF_INPUT_ORDER_YVYU (0x0 << 0)
#define CIF_INPUT_ORDER_VYUY (0x1 << 0)
#define CIF_INPUT_ORDER_YUYV (0x2 << 0)
#define CIF_INPUT_ORDER_UYVY (0x3 << 0)
#define CIF_OUTPUT_ORDER_YVYU (0x0 << 2)
#define CIF_OUTPUT_ORDER_VYUY (0x1 << 2)
#define CIF_OUTPUT_ORDER_YUYV (0x2 << 2)
#define CIF_OUTPUT_ORDER_UYVY (0x3 << 2)
#define YUV_INPUT_422 (0x00 << 7)
#define CIF_YUV_STORED_BIT_WIDTH (8U)
#define CIF_CROP_Y_SHIFT 16
#define CIF_FIFO_OVERFLOW FLEXBUS_RX_OVF_ISR
#define CIF_BANDWIDTH_LACK FLEXBUS_RX_UDF_ISR
#define CIF_SIZE_ERR FLEXBUS_DVP_FRAME_AB_ISR
#define CIF_DMA_START FLEXBUS_DVP_FRAME_START_ISR
#define CIF_DMA_END (FLEXBUS_DMA_DST0_ISR | FLEXBUS_DMA_DST1_ISR)
#define HSY_HIGH_ACTIVE (BIT(1))
#define HSY_LOW_ACTIVE (0)
#define VSY_HIGH_ACTIVE (BIT(0))
#define VSY_LOW_ACTIVE (0)
#define CIF_RAW_STORED_BIT_WIDTH (8U)
#define FLEXBUS_IMR_DMA_DST0_SHIFT (10U)
#define CIF_YUV2RGB_ENABLE (0x1 << 0)
#define CIF_YUV2RGB_B_LSB (0x1 << 3)
#define CIF_YUV2RGB_BT601_LIMIT (0 << 1)
#define CIF_YUV2RGB_BT601_FULL (2 << 1)
#define CIF_YUV2RGB_BT709_LIMIT (3 << 1)
#endif