media: rockchip: rk-cif: v0.1.1

support the mipi vc multi-channel input in cif driver
for rk1808

Change-Id: I432c628b30e6f6f23e8515158dcf516e499bf79a
Signed-off-by: Xu Hongfei <xuhf@rock-chips.com>
This commit is contained in:
Xu Hongfei
2019-04-18 16:54:45 +08:00
committed by Tao Huang
parent c35748c9e6
commit ebf350dee0
5 changed files with 864 additions and 348 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,12 @@
#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/dma-iommu.h>
#include <dt-bindings/soc/rockchip-system-status.h>
#include <soc/rockchip/rockchip-system-status.h>
#include "dev.h"
#include "regs.h"
@@ -32,7 +37,7 @@ struct cif_match_data {
int rsts_num;
};
int rkcif_debug;
int rkcif_debug = 3;
module_param_named(debug, rkcif_debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
@@ -45,40 +50,205 @@ int using_pingpong;
static DEFINE_MUTEX(rkcif_dev_mutex);
static LIST_HEAD(rkcif_device_list);
/**************************** pipeline operations *****************************/
static int __cif_pipeline_prepare(struct rkcif_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_entity_remote_pad(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 __subdev_set_power(struct v4l2_subdev *sd, int on)
{
int ret;
if (!sd)
return -ENXIO;
ret = v4l2_subdev_call(sd, core, s_power, on);
return ret != -ENOIOCTLCMD ? ret : 0;
}
static int __cif_pipeline_s_power(struct rkcif_pipeline *p, bool on)
{
int i, ret;
if (on) {
for (i = p->num_subdevs - 1; i >= 0; --i) {
ret = __subdev_set_power(p->subdevs[i], true);
if (ret < 0 && ret != -ENXIO)
goto err_power_off;
}
} else {
for (i = 0; i < p->num_subdevs; ++i)
__subdev_set_power(p->subdevs[i], false);
}
return 0;
err_power_off:
for (++i; i < p->num_subdevs; ++i)
__subdev_set_power(p->subdevs[i], false);
return ret;
}
static int __cif_pipeline_s_cif_clk(struct rkcif_pipeline *p)
{
return 0;
}
static int rkcif_pipeline_open(struct rkcif_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;
ret = __cif_pipeline_s_power(p, 1);
if (ret < 0)
return ret;
return 0;
}
static int rkcif_pipeline_close(struct rkcif_pipeline *p)
{
int ret;
if (atomic_dec_return(&p->power_cnt) > 0)
return 0;
ret = __cif_pipeline_s_power(p, 0);
return ret == -ENXIO ? 0 : ret;
}
/*
* stream-on order: isp_subdev, mipi dphy, sensor
* stream-off order: mipi dphy, sensor, isp_subdev
*/
static int rkcif_pipeline_set_stream(struct rkcif_pipeline *p, bool on)
{
int i, ret;
if ((on && atomic_inc_return(&p->stream_cnt) > 1) ||
(!on && atomic_dec_return(&p->stream_cnt) > 0))
return 0;
if (on)
rockchip_set_system_status(SYS_STATUS_CIF0);
/* phy -> sensor */
for (i = p->num_subdevs; i > -1; --i) {
ret = v4l2_subdev_call(p->subdevs[i], video, s_stream, on);
if (on && ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
goto err_stream_off;
}
if (!on)
rockchip_clear_system_status(SYS_STATUS_CIF0);
return 0;
err_stream_off:
for (--i; i >= 0; --i)
v4l2_subdev_call(p->subdevs[i], video, s_stream, false);
rockchip_clear_system_status(SYS_STATUS_CIF0);
return ret;
}
/***************************** media controller *******************************/
static int rkcif_create_links(struct rkcif_device *dev)
{
unsigned int s, pad;
unsigned int s, pad, id;
int ret;
/* sensor links(or mipi-phy) */
for (s = 0; s < dev->num_sensors; ++s) {
struct rkcif_sensor_info *sensor = &dev->sensors[s];
for (pad = 0; pad < sensor->sd->entity.num_pads; pad++)
for (pad = 0; pad < sensor->sd->entity.num_pads; pad++) {
if (sensor->sd->entity.pads[pad].flags &
MEDIA_PAD_FL_SOURCE)
break;
MEDIA_PAD_FL_SOURCE) {
if (pad == sensor->sd->entity.num_pads) {
dev_err(dev->dev,
"failed to find src pad for %s\n",
sensor->sd->name);
if (pad == sensor->sd->entity.num_pads) {
dev_err(dev->dev, "failed to find src pad for %s\n",
sensor->sd->name);
return -ENXIO;
}
return -ENXIO;
}
ret = media_create_pad_link(&sensor->sd->entity,
pad, &dev->stream.vdev.entity, 0,
s ? 0 : MEDIA_LNK_FL_ENABLED);
if (ret) {
dev_err(dev->dev, "failed to create link for %s\n",
sensor->sd->name);
return ret;
for (id = 0; id < RKCIF_MAX_STREAM_MIPI; id++) {
ret = media_entity_create_link(
&sensor->sd->entity,
pad,
&dev->stream[id].vnode.vdev.entity,
0,
id == pad - 1 ? MEDIA_LNK_FL_ENABLED : 0);
if (ret) {
dev_err(dev->dev,
"failed to create link for %s\n",
sensor->sd->name);
return ret;
}
}
}
}
}
return 0;
}
static int _set_pipeline_default_fmt(struct rkcif_device *dev)
{
return 0;
}
static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
{
struct rkcif_device *dev;
@@ -86,7 +256,7 @@ static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
dev = container_of(notifier, struct rkcif_device, notifier);
mutex_lock(&dev->media_dev.graph_mutex);
/* mutex_lock(&dev->media_dev.graph_mutex); */
ret = rkcif_create_links(dev);
if (ret < 0)
@@ -96,10 +266,13 @@ static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
if (ret < 0)
goto unlock;
ret = _set_pipeline_default_fmt(dev);
if (ret < 0)
goto unlock;
v4l2_info(&dev->v4l2_dev, "Async subdev notifier completed\n");
unlock:
mutex_unlock(&dev->media_dev.graph_mutex);
/* mutex_unlock(&dev->media_dev.graph_mutex); */
return ret;
}
@@ -187,18 +360,26 @@ static int rkcif_register_platform_subdevs(struct rkcif_device *cif_dev)
{
int ret;
ret = rkcif_register_stream_vdev(cif_dev);
cif_dev->alloc_ctx = vb2_dma_contig_init_ctx(cif_dev->v4l2_dev.dev);
ret = rkcif_register_stream_vdevs(cif_dev);
if (ret < 0)
return ret;
goto err_cleanup_ctx;
ret = cif_subdev_notifier(cif_dev);
if (ret < 0) {
v4l2_err(&cif_dev->v4l2_dev,
"Failed to register subdev notifier(%d)\n", ret);
rkcif_unregister_stream_vdev(cif_dev);
goto err_unreg_stream_vdev;
}
return 0;
err_unreg_stream_vdev:
rkcif_unregister_stream_vdevs(cif_dev);
err_cleanup_ctx:
vb2_dma_contig_cleanup_ctx(cif_dev->alloc_ctx);
return ret;
}
static const char * const px30_cif_clks[] = {
@@ -219,7 +400,7 @@ static const char * const rk1808_cif_clks[] = {
"dclk_cif",
"hclk_cif",
"sclk_cif_out",
"pclk_csi2host"
/* "pclk_csi2host" */
};
static const char * const rk1808_cif_rsts[] = {
@@ -316,50 +497,6 @@ static irqreturn_t rkcif_irq_handler(int irq, void *ctx)
return IRQ_HANDLED;
}
#define CSIHOST_MAX_ERRINT_COUNT 10
static irqreturn_t rk_csirx_irq1_handler(int irq, void *ctx)
{
struct device *dev = ctx;
struct rkcif_device *cif_dev = dev_get_drvdata(dev);
static int csi_err1_cnt;
u32 val;
val = read_csihost_reg(cif_dev->csi_base, CSIHOST_ERR1);
if (val) {
pr_err("ERROR: csi err1 intr: 0x%x\n", val);
if (++csi_err1_cnt > CSIHOST_MAX_ERRINT_COUNT) {
write_csihost_reg(cif_dev->csi_base,
CSIHOST_MSK1, 0xffffffff);
csi_err1_cnt = 0;
}
}
return IRQ_HANDLED;
}
static irqreturn_t rk_csirx_irq2_handler(int irq, void *ctx)
{
struct device *dev = ctx;
struct rkcif_device *cif_dev = dev_get_drvdata(dev);
static int csi_err2_cnt;
u32 val;
val = read_csihost_reg(cif_dev->csi_base, CSIHOST_ERR2);
if (val) {
pr_err("ERROR: csi err2 intr: 0x%x\n", val);
if (++csi_err2_cnt > CSIHOST_MAX_ERRINT_COUNT) {
write_csihost_reg(cif_dev->csi_base,
CSIHOST_MSK2, 0xffffffff);
csi_err2_cnt = 0;
}
}
return IRQ_HANDLED;
}
static void rkcif_disable_sys_clk(struct rkcif_device *cif_dev)
{
int i;
@@ -379,6 +516,7 @@ static int rkcif_enable_sys_clk(struct rkcif_device *cif_dev)
goto err;
}
write_cif_reg_and(cif_dev->base_addr, CIF_CSI_INTEN, 0x0);
return 0;
err:
@@ -470,33 +608,6 @@ static int rkcif_plat_probe(struct platform_device *pdev)
cif_dev->base_addr = devm_ioremap_resource(dev, res);
if (IS_ERR(cif_dev->base_addr))
return PTR_ERR(cif_dev->base_addr);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csihost_regs");
cif_dev->csi_base = devm_ioremap_resource(dev, res);
if (IS_ERR(cif_dev->csi_base))
return PTR_ERR(cif_dev->csi_base);
irq = platform_get_irq_byname(pdev, "csi-intr1");
if (irq > 0) {
ret = devm_request_irq(dev, irq, rk_csirx_irq1_handler,
0, dev_driver_string(dev), dev);
if (ret < 0)
dev_err(dev, "request csi-intr1 irq failed: %d\n",
ret);
} else {
dev_err(dev, "No found irq csi-intr1\n");
}
irq = platform_get_irq_byname(pdev, "csi-intr2");
if (irq > 0) {
ret = devm_request_irq(dev, irq, rk_csirx_irq2_handler,
0, dev_driver_string(dev), dev);
if (ret < 0)
dev_err(dev, "request csi-intr2 failed: %d\n",
ret);
} else {
dev_err(dev, "No found irq csi-intr2\n");
}
} else {
using_pingpong = 0;
@@ -533,7 +644,18 @@ static int rkcif_plat_probe(struct platform_device *pdev)
cif_dev->cif_rst[i] = rst;
}
rkcif_stream_init(cif_dev);
atomic_set(&cif_dev->pipe.power_cnt, 0);
atomic_set(&cif_dev->pipe.stream_cnt, 0);
mutex_init(&cif_dev->dev_lock);
cif_dev->pipe.open = rkcif_pipeline_open;
cif_dev->pipe.close = rkcif_pipeline_close;
cif_dev->pipe.set_stream = rkcif_pipeline_set_stream;
rkcif_stream_init(cif_dev, RKCIF_STREAM_MIPI_ID0);
rkcif_stream_init(cif_dev, RKCIF_STREAM_MIPI_ID1);
rkcif_stream_init(cif_dev, RKCIF_STREAM_MIPI_ID2);
rkcif_stream_init(cif_dev, RKCIF_STREAM_MIPI_ID3);
rkcif_stream_init(cif_dev, RKCIF_STREAM_DVP);
strlcpy(cif_dev->media_dev.model, "rkcif",
sizeof(cif_dev->media_dev.model));
@@ -569,6 +691,7 @@ static int rkcif_plat_probe(struct platform_device *pdev)
"No reserved memory region assign to CIF\n");
}
rkcif_soft_reset(cif_dev);
pm_runtime_enable(&pdev->dev);
mutex_lock(&rkcif_dev_mutex);
@@ -592,7 +715,8 @@ static int rkcif_plat_remove(struct platform_device *pdev)
media_device_unregister(&cif_dev->media_dev);
v4l2_device_unregister(&cif_dev->v4l2_dev);
rkcif_unregister_stream_vdev(cif_dev);
rkcif_unregister_stream_vdevs(cif_dev);
vb2_dma_contig_cleanup_ctx(cif_dev->alloc_ctx);
return 0;
}

View File

@@ -19,19 +19,46 @@
#define CIF_DRIVER_NAME "rkcif"
#define CIF_VIDEODEVICE_NAME "stream_cif"
#define CIF_DVP_VDEV_NAME CIF_VIDEODEVICE_NAME "_dvp"
#define CIF_MIPI_ID0_VDEV_NAME CIF_VIDEODEVICE_NAME "_mipi_id0"
#define CIF_MIPI_ID1_VDEV_NAME CIF_VIDEODEVICE_NAME "_mipi_id1"
#define CIF_MIPI_ID2_VDEV_NAME CIF_VIDEODEVICE_NAME "_mipi_id2"
#define CIF_MIPI_ID3_VDEV_NAME CIF_VIDEODEVICE_NAME "_mipi_id3"
/*
* Rk1808 support 5 channel inputs simultaneously:
* dvp + 4 mipi virtual channels
*/
#define RKCIF_MAX_STREAM 5
#define RKCIF_STREAM_MIPI_ID0 0
#define RKCIF_STREAM_MIPI_ID1 1
#define RKCIF_STREAM_MIPI_ID2 2
#define RKCIF_STREAM_MIPI_ID3 3
#define RKCIF_MAX_STREAM_MIPI 4
#define RKCIF_STREAM_DVP 4
#define RKCIF_MAX_BUS_CLK 8
#define RKCIF_MAX_SENSOR 2
#define RKCIF_MAX_RESET 5
#define RKCIF_MAX_CSI_CHANNEL 4
#define RKCIF_MAX_PIPELINE 4
#define RKCIF_DEFAULT_WIDTH 640
#define RKCIF_DEFAULT_HEIGHT 480
#define write_cif_reg(base, addr, val) writel(val, (addr) + (base))
#define write_cif_reg(base, addr, val) \
do { \
writel(val, (addr) + (base)); \
} while (0)
#define read_cif_reg(base, addr) readl((addr) + (base))
#define write_csihost_reg(base, addr, val) writel(val, (addr) + (base))
#define read_csihost_reg(base, addr) readl((addr) + (base))
#define write_cif_reg_or(base, addr, val) \
do { \
writel(readl((addr) + (base)) | (val), (addr) + (base)); \
} while (0)
#define write_cif_reg_and(base, addr, val) \
do { \
writel(readl((addr) + (base)) & (val), (addr) + (base)); \
} while (0)
enum rkcif_state {
RKCIF_STATE_DISABLED,
@@ -51,6 +78,27 @@ enum host_type_t {
RK_DSI_RXHOST
};
/*
* struct rkcif_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 rkcif_pipeline {
struct media_pipeline pipe;
int num_subdevs;
atomic_t power_cnt;
atomic_t stream_cnt;
struct v4l2_subdev *subdevs[RKCIF_MAX_PIPELINE];
int (*open)(struct rkcif_pipeline *p,
struct media_entity *me, bool prepare);
int (*close)(struct rkcif_pipeline *p);
int (*set_stream)(struct rkcif_pipeline *p, bool on);
};
struct rkcif_buffer {
struct vb2_v4l2_buffer vb;
struct list_head queue;
@@ -68,11 +116,6 @@ struct rkcif_dummy_buffer {
extern int rkcif_debug;
static inline struct rkcif_buffer *to_rkcif_buffer(struct vb2_v4l2_buffer *vb)
{
return container_of(vb, struct rkcif_buffer, vb);
}
/*
* struct rkcif_sensor_info - Sensor infomations
* @mbus: media bus configuration
@@ -135,6 +178,14 @@ struct csi_channel_info {
unsigned int crop_st_y;
};
struct rkcif_vdev_node {
struct vb2_queue buf_queue;
/* vfd lock */
struct mutex vlock;
struct video_device vdev;
struct media_pad pad;
};
/*
* struct rkcif_stream - Stream states TODO
*
@@ -147,7 +198,9 @@ struct csi_channel_info {
* @next_buf: the buffer used for next frame
*/
struct rkcif_stream {
unsigned id:3;
struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
enum rkcif_state state;
bool stopping;
wait_queue_head_t wq_stopped;
@@ -155,16 +208,12 @@ struct rkcif_stream {
int frame_phase;
/* lock between irq and buf_queue */
spinlock_t vbq_lock;
struct vb2_queue buf_queue;
struct list_head buf_head;
struct rkcif_dummy_buffer dummy_buf;
struct rkcif_buffer *curr_buf;
struct rkcif_buffer *next_buf;
/* vfd lock */
struct mutex vlock;
struct video_device vdev;
spinlock_t vbq_lock; /* vfd lock */
/* TODO: pad for dvp and mipi separately? */
struct media_pad pad;
@@ -175,9 +224,33 @@ struct rkcif_stream {
int crop_enable;
};
static inline struct rkcif_stream *to_rkcif_stream(struct video_device *vdev)
static inline struct rkcif_buffer *to_rkcif_buffer(struct vb2_v4l2_buffer *vb)
{
return container_of(vdev, struct rkcif_stream, vdev);
return container_of(vb, struct rkcif_buffer, vb);
}
static inline
struct rkcif_vdev_node *vdev_to_node(struct video_device *vdev)
{
return container_of(vdev, struct rkcif_vdev_node, vdev);
}
static inline
struct rkcif_stream *to_rkcif_stream(struct rkcif_vdev_node *vnode)
{
return container_of(vnode, struct rkcif_stream, vnode);
}
static inline struct rkcif_vdev_node *queue_to_node(struct vb2_queue *q)
{
return container_of(q, struct rkcif_vdev_node, buf_queue);
}
static inline struct vb2_queue *to_vb2_queue(struct file *file)
{
struct rkcif_vdev_node *vnode = video_drvdata(file);
return &vnode->buf_queue;
}
/*
@@ -207,16 +280,21 @@ struct rkcif_device {
u32 num_sensors;
struct rkcif_sensor_info *active_sensor;
struct rkcif_stream stream;
struct rkcif_stream stream[RKCIF_MAX_STREAM];
struct rkcif_pipeline pipe;
struct csi_channel_info channels[RKCIF_MAX_CSI_CHANNEL];
int num_channels;
int chip_id;
bool working;
bool is_cif_rst;
/* dev operate mutex */
struct mutex dev_lock;
};
void rkcif_unregister_stream_vdev(struct rkcif_device *dev);
int rkcif_register_stream_vdev(struct rkcif_device *dev);
void rkcif_stream_init(struct rkcif_device *dev);
void rkcif_unregister_stream_vdevs(struct rkcif_device *dev);
int rkcif_register_stream_vdevs(struct rkcif_device *dev);
void rkcif_stream_init(struct rkcif_device *dev, u32 id);
void rkcif_irq_oneframe(struct rkcif_device *cif_dev);
void rkcif_irq_pingpong(struct rkcif_device *cif_dev);

View File

@@ -192,7 +192,6 @@
#define CSI_ENABLE_CROP (0x1 << 5)
/* CIF_CSI_INTEN */
#define CSI_FRAME0_START_INTEN(id) (0x1 << ((id) * 2))
#define CSI_FRAME1_START_INTEN(id) (0x1 << ((id) * 2 + 1))
#define CSI_FRAME0_END_INTEN(id) (0x1 << ((id) * 2 + 8))
#define CSI_FRAME1_END_INTEN(id) (0x1 << ((id) * 2 + 9))
@@ -205,6 +204,14 @@
#define CSI_ALL_FRAME_END_INTEN (0xff << 8)
#define CSI_ALL_ERROR_INTEN (0x1f << 16)
#define CSI_START_INTEN(id) (0x3 << ((id) * 2))
#define CSI_DMA_END_INTEN(id) (0x3 << ((id) * 2 + 8))
#define CSI_LINE_INTEN(id) (0x1 << ((id) + 21))
#define CSI_START_INTSTAT(id) (0x3 << ((id) * 2))
#define CSI_DMA_END_INTSTAT(id) (0x3 << ((id) * 2 + 8))
#define CSI_LINE_INTSTAT(id) (0x1 << ((id) + 21))
/* CIF_CSI_INTSTAT */
#define CSI_FRAME0_START_ID0 (0x1 << 0)
#define CSI_FRAME1_START_ID0 (0x1 << 1)
@@ -228,6 +235,14 @@
#define CSI_BANDWIDTH_LACK (0x1 << 19)
#define CSI_RX_FIFO_OVERFLOW (0x1 << 20)
#define CSI_FRAME_END_ID0 (CSI_FRAME0_END_ID0 |\
CSI_FRAME1_END_ID0)
#define CSI_FRAME_END_ID1 (CSI_FRAME0_END_ID1 |\
CSI_FRAME1_END_ID1)
#define CSI_FRAME_END_ID2 (CSI_FRAME0_END_ID2 |\
CSI_FRAME1_END_ID2)
#define CSI_FRAME_END_ID3 (CSI_FRAME0_END_ID3 |\
CSI_FRAME1_END_ID3)
#define CSI_FIFO_OVERFLOW (CSI_DMA_Y_FIFO_OVERFLOW | \
CSI_DMA_UV_FIFO_OVERFLOW | \
CSI_CONFIG_FIFO_OVERFLOW | \

View File

@@ -10,9 +10,11 @@
*
*v0.1.0:
*1. First version;
*v0.1.1
*support the mipi vc multi-channel input in cif driver for rk1808
*
*/
#define RKCIF_DRIVER_VERSION KERNEL_VERSION(0, 1, 0x0)
#define RKCIF_DRIVER_VERSION KERNEL_VERSION(0, 1, 0x1)
#endif