mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-10 12:57:06 +09:00
media: rockchip: add flexbus cif driver
Signed-off-by: Zefa Chen <zefa.chen@rock-chips.com> Change-Id: I1335abf6bb590d145acaccfaf31bcc663b8da92a
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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/
|
||||
|
||||
37
drivers/media/platform/rockchip/flexbus_cif/Kconfig
Normal file
37
drivers/media/platform/rockchip/flexbus_cif/Kconfig
Normal 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
|
||||
|
||||
8
drivers/media/platform/rockchip/flexbus_cif/Makefile
Normal file
8
drivers/media/platform/rockchip/flexbus_cif/Makefile
Normal 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
|
||||
2333
drivers/media/platform/rockchip/flexbus_cif/capture.c
Normal file
2333
drivers/media/platform/rockchip/flexbus_cif/capture.c
Normal file
File diff suppressed because it is too large
Load Diff
98
drivers/media/platform/rockchip/flexbus_cif/common.c
Normal file
98
drivers/media/platform/rockchip/flexbus_cif/common.c
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
27
drivers/media/platform/rockchip/flexbus_cif/common.h
Normal file
27
drivers/media/platform/rockchip/flexbus_cif/common.h
Normal 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 */
|
||||
|
||||
674
drivers/media/platform/rockchip/flexbus_cif/dev.c
Normal file
674
drivers/media/platform/rockchip/flexbus_cif/dev.c
Normal 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");
|
||||
467
drivers/media/platform/rockchip/flexbus_cif/dev.h
Normal file
467
drivers/media/platform/rockchip/flexbus_cif/dev.h
Normal 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
|
||||
339
drivers/media/platform/rockchip/flexbus_cif/procfs.c
Normal file
339
drivers/media/platform/rockchip/flexbus_cif/procfs.c
Normal 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
|
||||
28
drivers/media/platform/rockchip/flexbus_cif/procfs.h
Normal file
28
drivers/media/platform/rockchip/flexbus_cif/procfs.h
Normal 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
|
||||
49
drivers/media/platform/rockchip/flexbus_cif/regs.h
Normal file
49
drivers/media/platform/rockchip/flexbus_cif/regs.h
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user