media: rockchip/cif: support capture mipi csi data for rk1808

Also add support pingpong frame mode.

Change-Id: Ibc29e3452351ae6ffafebbf72386b47bfb853f8c
Signed-off-by: Wenlong Zhuang <daisen.zhuang@rock-chips.com>
This commit is contained in:
Wenlong Zhuang
2018-09-28 21:07:25 +08:00
committed by Tao Huang
parent 7cc71c2e2f
commit 573f0547a8
4 changed files with 763 additions and 17 deletions

View File

@@ -83,6 +83,81 @@ static const struct cif_output_fmt out_fmts[] = {
.cplanes = 2,
.mplanes = 1,
.bpp = { 8, 16 },
}, {
.fourcc = V4L2_PIX_FMT_RGB24,
.cplanes = 1,
.mplanes = 1,
.bpp = { 24 },
}, {
.fourcc = V4L2_PIX_FMT_RGB565,
.cplanes = 1,
.mplanes = 1,
.bpp = { 16 },
}, {
.fourcc = V4L2_PIX_FMT_BGR666,
.cplanes = 1,
.mplanes = 1,
.bpp = { 18 },
}, {
.fourcc = V4L2_PIX_FMT_SRGGB8,
.cplanes = 1,
.mplanes = 1,
.bpp = { 8 },
}, {
.fourcc = V4L2_PIX_FMT_SGRBG8,
.cplanes = 1,
.mplanes = 1,
.bpp = { 8 },
}, {
.fourcc = V4L2_PIX_FMT_SGBRG8,
.cplanes = 1,
.mplanes = 1,
.bpp = { 8 },
}, {
.fourcc = V4L2_PIX_FMT_SBGGR8,
.cplanes = 1,
.mplanes = 1,
.bpp = { 8 },
}, {
.fourcc = V4L2_PIX_FMT_SRGGB10,
.cplanes = 1,
.mplanes = 1,
.bpp = { 16 },
}, {
.fourcc = V4L2_PIX_FMT_SGRBG10,
.cplanes = 1,
.mplanes = 1,
.bpp = { 16 },
}, {
.fourcc = V4L2_PIX_FMT_SGBRG10,
.cplanes = 1,
.mplanes = 1,
.bpp = { 16 },
}, {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.cplanes = 1,
.mplanes = 1,
.bpp = { 16 },
}, {
.fourcc = V4L2_PIX_FMT_SRGGB12,
.cplanes = 1,
.mplanes = 1,
.bpp = { 16 },
}, {
.fourcc = V4L2_PIX_FMT_SGRBG12,
.cplanes = 1,
.mplanes = 1,
.bpp = { 16 },
}, {
.fourcc = V4L2_PIX_FMT_SGBRG12,
.cplanes = 1,
.mplanes = 1,
.bpp = { 16 },
}, {
.fourcc = V4L2_PIX_FMT_SBGGR12,
.cplanes = 1,
.mplanes = 1,
.bpp = { 16 },
},
/* TODO: We can support NV12M/NV21M/NV16M/NV61M too */
@@ -105,6 +180,45 @@ static const struct cif_input_fmt in_fmts[] = {
.mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
.fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_VYUY,
.fmt_type = CIF_FMT_TYPE_YUV,
}, {
.mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
.fmt_val = CSI_WRDDR_TYPE_RAW8,
}, {
.mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
.fmt_val = CSI_WRDDR_TYPE_RAW8,
}, {
.mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
.fmt_val = CSI_WRDDR_TYPE_RAW8,
}, {
.mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
.fmt_val = CSI_WRDDR_TYPE_RAW8,
}, {
.mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
.fmt_val = CSI_WRDDR_TYPE_RAW10,
}, {
.mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
.fmt_val = CSI_WRDDR_TYPE_RAW10,
}, {
.mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
.fmt_val = CSI_WRDDR_TYPE_RAW10,
}, {
.mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
.fmt_val = CSI_WRDDR_TYPE_RAW10,
}, {
.mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
.fmt_val = CSI_WRDDR_TYPE_RAW12,
}, {
.mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
.fmt_val = CSI_WRDDR_TYPE_RAW12,
}, {
.mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
.fmt_val = CSI_WRDDR_TYPE_RAW12,
}, {
.mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
.fmt_val = CSI_WRDDR_TYPE_RAW12,
}, {
.mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
.fmt_val = CSI_WRDDR_TYPE_RGB888,
},
};
@@ -132,6 +246,32 @@ static struct rkcif_sensor_info *get_active_sensor(struct rkcif_stream *stream)
return NULL;
}
static unsigned char get_data_type(u32 pixelformat, u8 cmd_mode_en)
{
switch (pixelformat) {
/* csi raw8 */
case V4L2_PIX_FMT_SRGGB8:
case V4L2_PIX_FMT_SGRBG8:
case V4L2_PIX_FMT_SGBRG8:
case V4L2_PIX_FMT_SBGGR8:
return 0x2a;
/* csi raw10 */
case V4L2_PIX_FMT_SRGGB10:
case V4L2_PIX_FMT_SGRBG10:
case V4L2_PIX_FMT_SGBRG10:
case V4L2_PIX_FMT_SBGGR10:
return 0x2b;
case V4L2_PIX_FMT_RGB24: {
if (cmd_mode_en) /* dsi command mode*/
return 0x39;
else /* dsi video mode */
return 0x3e;
}
default:
return 0x2b;
}
}
static const struct
cif_input_fmt *get_input_fmt(struct v4l2_subdev *sd, struct v4l2_rect *rect)
{
@@ -221,11 +361,306 @@ static void rkcif_assign_new_buffer_oneframe(struct rkcif_stream *stream)
}
}
static void rkcif_assign_new_buffer_pingpong(struct rkcif_stream *stream,
int init, int csi_ch)
{
struct rkcif_dummy_buffer *dummy_buf = &stream->dummy_buf;
struct rkcif_device *dev = stream->cifdev;
struct rkcif_buffer *buffer = NULL;
void __iomem *base = dev->base_addr;
u32 frm_addr_y, frm_addr_uv;
/* Set up an empty buffer for the next frame */
spin_lock(&stream->vbq_lock);
if (!list_empty(&stream->buf_head)) {
if (stream->frame_phase == 0 || init) {
stream->curr_buf = list_first_entry(&stream->buf_head,
struct rkcif_buffer, queue);
list_del(&stream->curr_buf->queue);
buffer = stream->curr_buf;
}
if (stream->frame_phase == 1 || init) {
stream->next_buf = list_first_entry(&stream->buf_head,
struct rkcif_buffer, queue);
list_del(&stream->next_buf->queue);
buffer = stream->next_buf;
}
} else {
if (stream->frame_phase == 0)
stream->curr_buf = NULL;
if (stream->frame_phase == 1)
stream->next_buf = NULL;
buffer = NULL;
}
spin_unlock(&stream->vbq_lock);
if (init) {
u32 frm0_addr_y, frm0_addr_uv;
u32 frm1_addr_y, frm1_addr_uv;
if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2) {
frm0_addr_y = CIF_CSI_FRM0_ADDR_Y_ID0 + 0x20 * csi_ch;
frm0_addr_uv = CIF_CSI_FRM0_ADDR_UV_ID0 + 0x20 * csi_ch;
frm1_addr_y = CIF_CSI_FRM1_ADDR_Y_ID0 + 0x20 * csi_ch;
frm1_addr_uv = CIF_CSI_FRM1_ADDR_UV_ID0 + 0x20 * csi_ch;
} else {
frm0_addr_y = CIF_FRM0_ADDR_Y;
frm0_addr_uv = CIF_FRM0_ADDR_UV;
frm1_addr_y = CIF_FRM1_ADDR_Y;
frm1_addr_uv = CIF_FRM1_ADDR_UV;
}
if (stream->curr_buf && stream->next_buf) {
write_cif_reg(base, frm0_addr_y,
stream->curr_buf->buff_addr[RKCIF_PLANE_Y]);
write_cif_reg(base, frm0_addr_uv,
stream->curr_buf->buff_addr[RKCIF_PLANE_CBCR]);
write_cif_reg(base, frm1_addr_y,
stream->next_buf->buff_addr[RKCIF_PLANE_Y]);
write_cif_reg(base, frm1_addr_uv,
stream->next_buf->buff_addr[RKCIF_PLANE_CBCR]);
} else {
v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "Drop to dummy buf\n");
write_cif_reg(base, frm0_addr_y, dummy_buf->dma_addr);
write_cif_reg(base, frm0_addr_uv, dummy_buf->dma_addr);
write_cif_reg(base, frm1_addr_y, dummy_buf->dma_addr);
write_cif_reg(base, frm1_addr_uv, dummy_buf->dma_addr);
}
return;
}
if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2) {
frm_addr_y = stream->frame_phase == 1 ?
(CIF_CSI_FRM1_ADDR_Y_ID0 + 0x20 * csi_ch) :
(CIF_CSI_FRM0_ADDR_Y_ID0 + 0x20 * csi_ch);
frm_addr_uv = stream->frame_phase == 1 ?
(CIF_CSI_FRM1_ADDR_UV_ID0 + 0x20 * csi_ch) :
(CIF_CSI_FRM0_ADDR_UV_ID0 + 0x20 * csi_ch);
} else {
frm_addr_y = stream->frame_phase == 1 ?
CIF_FRM1_ADDR_Y : CIF_FRM0_ADDR_Y;
frm_addr_uv = stream->frame_phase == 1 ?
CIF_FRM1_ADDR_UV : CIF_FRM0_ADDR_UV;
}
if (buffer) {
write_cif_reg(base, frm_addr_y,
buffer->buff_addr[RKCIF_PLANE_Y]);
write_cif_reg(base, frm_addr_uv,
buffer->buff_addr[RKCIF_PLANE_CBCR]);
} else {
v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "Drop to dummy buf\n");
write_cif_reg(base, frm_addr_y, dummy_buf->dma_addr);
write_cif_reg(base, frm_addr_uv, dummy_buf->dma_addr);
}
}
static void rkcif_csihost_disable(struct rkcif_device *dev)
{
void __iomem *base = dev->csi_base;
write_csihost_reg(base, CSIHOST_RESETN, 0);
v4l2_info(&dev->v4l2_dev, "mipi csi host disable\n");
}
static void rkcif_csihost_enable(struct rkcif_device *dev,
enum host_type_t host_type, int lanes)
{
void __iomem *base = dev->csi_base;
write_csihost_reg(base, CSIHOST_N_LANES, lanes - 1);
if (host_type == RK_DSI_RXHOST) {
write_csihost_reg(base, CSIHOST_CONTROL,
SW_CPHY_EN(0) | SW_DSI_EN(1) |
SW_DATATYPE_FS(0x01) | SW_DATATYPE_FE(0x11) |
SW_DATATYPE_LS(0x21) | SW_DATATYPE_LE(0x31));
} else {
write_csihost_reg(base, CSIHOST_CONTROL,
SW_CPHY_EN(0) | SW_DSI_EN(0));
}
write_csihost_reg(base, CSIHOST_RESETN, 1);
v4l2_info(&dev->v4l2_dev, "mipi csi host enable\n");
}
static int rkcif_csi_crop_check(struct rkcif_device *dev,
struct csi_channel_info *channel)
{
switch (channel->fmt_val) {
case CSI_WRDDR_TYPE_RGB888:
if (channel->crop_st_x % 24 != 0) {
channel->crop_st_x = ALIGN(channel->crop_st_x, 24);
v4l2_info(&dev->v4l2_dev,
"align 24 pixel: crop_st_x change to %d",
channel->crop_st_x);
}
channel->width -= channel->crop_st_x / 3;
break;
case CSI_WRDDR_TYPE_RAW10:
case CSI_WRDDR_TYPE_RAW12:
if (channel->crop_st_x % 4 != 0) {
channel->crop_st_x = ALIGN(channel->crop_st_x, 4);
v4l2_info(&dev->v4l2_dev,
"align 4 pixel: crop_st_x change to %d",
channel->crop_st_x);
}
channel->width -= channel->crop_st_x;
break;
case CSI_WRDDR_TYPE_RAW8:
case CSI_WRDDR_TYPE_YUV422:
if (channel->crop_st_x % 8 != 0) {
channel->crop_st_x = ALIGN(channel->crop_st_x, 8);
v4l2_info(&dev->v4l2_dev,
"align 8 pixel: crop_st_x change to %d",
channel->crop_st_x);
}
channel->width -= channel->crop_st_x;
break;
default:
break;
}
channel->height -= channel->crop_st_y;
return 0;
}
static int rkcif_csi_channel_init(struct rkcif_stream *stream,
struct csi_channel_info *channel)
{
struct rkcif_device *dev = stream->cifdev;
struct rkcif_sensor_info *sensor_info = dev->active_sensor;
const struct cif_output_fmt *fmt;
u32 mbus_flags = sensor_info->mbus.flags;
channel->enable = 1;
channel->width = stream->pixm.width;
channel->height = stream->pixm.height;
channel->fmt_val = stream->cif_fmt_in->fmt_val;
channel->cmd_mode_en = 0; /* default use DSI Video Mode */
channel->crop_en = 1; /* Force crop enable to avoid write overflow */
channel->crop_st_x = stream->crop.left;
channel->crop_st_y = stream->crop.top;
if (channel->crop_en)
rkcif_csi_crop_check(dev, channel);
fmt = find_output_fmt(stream, stream->pixm.pixelformat);
if (!fmt) {
v4l2_err(&dev->v4l2_dev, "can not find output format: 0x%x",
stream->pixm.pixelformat);
return -EINVAL;
}
channel->virtual_width = ALIGN(channel->width * fmt->bpp[0] / 8, 8);
/* TODO Modify data type !!!!!!!!!!!!!!! */
channel->data_type =
get_data_type(stream->pixm.pixelformat, channel->cmd_mode_en);
if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_0)
channel->vc = 0;
else if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_1)
channel->vc = 1;
else if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_2)
channel->vc = 2;
else if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_3)
channel->vc = 3;
return 0;
}
static int rkcif_csi_channel_set(struct rkcif_stream *stream,
struct csi_channel_info *channel)
{
struct rkcif_device *dev = stream->cifdev;
void __iomem *base = dev->base_addr;
unsigned int val;
if (channel->id >= 4)
return -EINVAL;
if (!channel->enable) {
write_cif_reg(base, CIF_CSI_ID0_CTRL0 + 0x8 * channel->id,
CSI_DISABLE_CAPTURE);
return 0;
}
write_cif_reg(base, CIF_CSI_ID0_CTRL1 + 0x8 * channel->id,
channel->width | (channel->height << 16));
write_cif_reg(base, CIF_CSI_FRM0_VLW_Y_ID0 + 0x20 * channel->id,
channel->virtual_width);
write_cif_reg(base, CIF_CSI_FRM1_VLW_Y_ID0 + 0x20 * channel->id,
channel->virtual_width);
if (channel->crop_en)
write_cif_reg(base, CIF_CSI_ID0_CROP_START + 0x4 * channel->id,
channel->crop_st_y << 16 | channel->crop_st_x);
/* Set up an buffer for the next frame */
rkcif_assign_new_buffer_pingpong(stream, 1, channel->id);
val = CSI_ENABLE_CAPTURE | channel->fmt_val |
channel->cmd_mode_en << 4 | channel->crop_en << 5 |
channel->vc << 8 | channel->data_type << 10;
write_cif_reg(base, CIF_CSI_ID0_CTRL0 + 0x8 * channel->id, val);
return 0;
}
static int rkcif_csi_stream_start(struct rkcif_stream *stream)
{
struct rkcif_device *dev = stream->cifdev;
struct rkcif_sensor_info *sensor_info = dev->active_sensor;
void __iomem *base = dev->base_addr;
enum host_type_t host_type;
int i;
stream->frame_idx = 0;
/* TODO Modify to support multiple channel */
for (i = 0; i < RKCIF_MAX_CSI_CHANNEL; i++) {
struct csi_channel_info *channel = &dev->channels[i];
channel->id = i;
if (i < dev->num_channels)
rkcif_csi_channel_init(stream, channel);
else
channel->enable = 0;
rkcif_csi_channel_set(stream, channel);
}
write_cif_reg(base, CIF_CSI_INTSTAT, 0x0);
write_cif_reg(base, CIF_CSI_INTEN,
CSI_ALL_FRAME_START_INTEN |
CSI_ALL_FRAME_END_INTEN |
CSI_ALL_ERROR_INTEN);
/* enable csi host */
if (stream->cif_fmt_in->mbus_code == MEDIA_BUS_FMT_RGB888_1X24)
host_type = RK_DSI_RXHOST;
else
host_type = RK_CSI_RXHOST;
rkcif_csihost_enable(dev, host_type, sensor_info->lanes);
stream->state = RKCIF_STATE_STREAMING;
return 0;
}
static void rkcif_stream_stop(struct rkcif_stream *stream)
{
struct rkcif_device *cif_dev = stream->cifdev;
void __iomem *base = cif_dev->base_addr;
u32 val;
int i;
val = read_cif_reg(base, CIF_CTRL);
write_cif_reg(base, CIF_CTRL, val & (~ENABLE_CAPTURE));
@@ -233,6 +668,21 @@ static void rkcif_stream_stop(struct rkcif_stream *stream)
write_cif_reg(base, CIF_INTSTAT, 0x3ff);
write_cif_reg(base, CIF_FRAME_STATUS, 0x0);
if (cif_dev->active_sensor->mbus.type == V4L2_MBUS_CSI2) {
/* disable csi host */
rkcif_csihost_disable(cif_dev);
/* disable cif csi */
for (i = 0; i < 4; i++) {
val = read_cif_reg(base, CIF_CSI_ID0_CTRL0 + 0x8 * i);
write_cif_reg(base, CIF_CSI_ID0_CTRL0 + 0x8 * i,
(val & (~CSI_ENABLE_CAPTURE)));
}
write_cif_reg(base, CIF_CSI_INTEN, 0x0);
write_cif_reg(base, CIF_CSI_INTSTAT, 0x1ffffff);
}
stream->state = RKCIF_STATE_READY;
}
@@ -378,6 +828,11 @@ static void rkcif_stop_streaming(struct vb2_queue *queue)
list_add_tail(&stream->curr_buf->queue, &stream->buf_head);
stream->curr_buf = NULL;
}
if (stream->next_buf) {
list_add_tail(&stream->next_buf->queue, &stream->buf_head);
stream->next_buf = NULL;
}
while (!list_empty(&stream->buf_head)) {
buf = list_first_entry(&stream->buf_head,
struct rkcif_buffer, queue);
@@ -536,7 +991,13 @@ static int rkcif_start_streaming(struct vb2_queue *queue, unsigned int count)
v4l2_err(v4l2_dev, "Failed to get runtime pm, %d\n", ret);
goto destroy_dummy_buf;
}
ret = rkcif_stream_start(stream);
if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2) {
dev->num_channels = 1;
ret = rkcif_csi_stream_start(stream);
} else {
ret = rkcif_stream_start(stream);
}
if (ret < 0)
goto runtime_put;
@@ -844,7 +1305,7 @@ int rkcif_register_stream_vdev(struct rkcif_device *dev)
struct video_device *vdev = &stream->vdev;
int ret;
strlcpy(vdev->name, "stream_cif", sizeof(vdev->name));
strlcpy(vdev->name, CIF_VIDEODEVICE_NAME, sizeof(vdev->name));
mutex_init(&stream->vlock);
vdev->ioctl_ops = &rkcif_v4l2_ioctl_ops;
@@ -968,5 +1429,80 @@ void rkcif_irq_oneframe(struct rkcif_device *cif_dev)
void rkcif_irq_pingpong(struct rkcif_device *cif_dev)
{
/* TODO */
struct rkcif_stream *stream = &cif_dev->stream;
void __iomem *base = cif_dev->base_addr;
unsigned int intstat;
if (cif_dev->active_sensor->mbus.type == V4L2_MBUS_CSI2) {
intstat = read_cif_reg(base, CIF_CSI_INTSTAT);
/* clear all interrupts that has been triggered */
write_cif_reg(base, CIF_CSI_INTSTAT, intstat);
if (intstat & CSI_FIFO_OVERFLOW) {
v4l2_err(&cif_dev->v4l2_dev,
"ERROR: csi fifo overflow!! intstat: 0x%x\n",
intstat);
return;
}
if (intstat & CSI_BANDWIDTH_LACK) {
v4l2_err(&cif_dev->v4l2_dev,
"ERROR: csi bandwidth lack!!\n");
return;
}
if (intstat & CSI_FRAME0_END_ID0 &&
intstat & CSI_FRAME1_END_ID0) {
v4l2_err(&cif_dev->v4l2_dev,
"ERROR: both frame0 and frame1 int\n");
return;
}
if (intstat & CSI_FRAME0_END_ID0 ||
intstat & CSI_FRAME1_END_ID0) {
struct vb2_v4l2_buffer *vb_done = NULL;
if (stream->stopping) {
rkcif_stream_stop(stream);
stream->stopping = false;
wake_up(&stream->wq_stopped);
return;
}
if (stream->frame_idx == 0)
stream->frame_phase =
intstat & CSI_FRAME0_END_ID0 ? 0 : 1;
else
stream->frame_phase ^= 1;
if (intstat & CSI_FRAME0_END_ID0 &&
stream->frame_phase != 0) {
stream->frame_phase = 0;
v4l2_err(&cif_dev->v4l2_dev,
"ERROR: last frame1 intr miss\n");
}
if (intstat & CSI_FRAME1_END_ID0 &&
stream->frame_phase != 1) {
stream->frame_phase = 1;
v4l2_err(&cif_dev->v4l2_dev,
"ERROR: last frame0 intr miss\n");
}
if (stream->frame_phase == 1) {
if (stream->next_buf)
vb_done = &stream->next_buf->vb;
} else {
if (stream->curr_buf)
vb_done = &stream->curr_buf->vb;
}
rkcif_assign_new_buffer_pingpong(stream, 0, 0);
if (vb_done)
rkcif_vb_done_oneframe(stream, vb_done);
stream->frame_idx++;
}
} else {
/* TODO */
}
}

View File

@@ -23,6 +23,7 @@
#include "regs.h"
struct cif_match_data {
int chip_id;
const char * const *clks;
const char * const *rsts;
int clks_num;
@@ -96,6 +97,7 @@ unlock:
struct rkcif_async_subdev {
struct v4l2_async_subdev asd;
struct v4l2_mbus_config mbus;
int lanes;
};
static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
@@ -110,6 +112,7 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
if (cif_dev->num_sensors == ARRAY_SIZE(cif_dev->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;
@@ -127,17 +130,20 @@ static int rkcif_fwnode_parse(struct device *dev,
container_of(asd, struct rkcif_async_subdev, asd);
struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel;
/*
* MIPI sensor is linked with a mipi dphy and its media bus config can
* not be get in here
*/
if (vep->bus_type != V4L2_MBUS_BT656 &&
vep->bus_type != V4L2_MBUS_PARALLEL)
vep->bus_type != V4L2_MBUS_PARALLEL &&
vep->bus_type != V4L2_MBUS_CSI2)
return 0;
rk_asd->mbus.flags = bus->flags;
rk_asd->mbus.type = vep->bus_type;
if (vep->bus_type == V4L2_MBUS_CSI2) {
rk_asd->mbus.flags = vep->bus.mipi_csi2.flags;
rk_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
} else {
rk_asd->mbus.flags = bus->flags;
}
return 0;
}
@@ -199,6 +205,22 @@ static const char * const px30_cif_rsts[] = {
"rst_cif_pclkin",
};
static const char * const rk1808_cif_clks[] = {
"aclk_cif",
"dclk_cif",
"hclk_cif",
"sclk_cif_out",
"pclk_csi2host"
};
static const char * const rk1808_cif_rsts[] = {
"rst_cif_a",
"rst_cif_h",
"rst_cif_i",
"rst_cif_d",
"rst_cif_pclkin",
};
static const char * const rk3128_cif_clks[] = {
"aclk_cif",
"hclk_cif",
@@ -220,21 +242,32 @@ static const char * const rk3288_cif_rsts[] = {
"rst_cif",
};
static const struct cif_match_data px30_cif_clk_data = {
static const struct cif_match_data px30_cif_match_data = {
.chip_id = CHIP_PX30_CIF,
.clks = px30_cif_clks,
.clks_num = ARRAY_SIZE(px30_cif_clks),
.rsts = px30_cif_rsts,
.rsts_num = ARRAY_SIZE(px30_cif_rsts),
};
static const struct cif_match_data rk3128_cif_clk_data = {
static const struct cif_match_data rk1808_cif_match_data = {
.chip_id = CHIP_RK1808_CIF,
.clks = rk1808_cif_clks,
.clks_num = ARRAY_SIZE(rk1808_cif_clks),
.rsts = rk1808_cif_rsts,
.rsts_num = ARRAY_SIZE(rk1808_cif_rsts),
};
static const struct cif_match_data rk3128_cif_match_data = {
.chip_id = CHIP_RK3128_CIF,
.clks = rk3128_cif_clks,
.clks_num = ARRAY_SIZE(rk3128_cif_clks),
.rsts = rk3128_cif_rsts,
.rsts_num = ARRAY_SIZE(rk3128_cif_rsts),
};
static const struct cif_match_data rk3288_cif_clk_data = {
static const struct cif_match_data rk3288_cif_match_data = {
.chip_id = CHIP_RK3288_CIF,
.clks = rk3288_cif_clks,
.clks_num = ARRAY_SIZE(rk3288_cif_clks),
.rsts = rk3288_cif_rsts,
@@ -244,15 +277,19 @@ static const struct cif_match_data rk3288_cif_clk_data = {
static const struct of_device_id rkcif_plat_of_match[] = {
{
.compatible = "rockchip,px30-cif",
.data = &px30_cif_clk_data,
.data = &px30_cif_match_data,
},
{
.compatible = "rockchip,rk1808-cif",
.data = &rk1808_cif_match_data,
},
{
.compatible = "rockchip,rk3128-cif",
.data = &rk3128_cif_clk_data,
.data = &rk3128_cif_match_data,
},
{
.compatible = "rockchip,rk3288-cif",
.data = &rk3288_cif_clk_data,
.data = &rk3288_cif_match_data,
},
{},
};
@@ -414,7 +451,7 @@ static int rkcif_plat_probe(struct platform_device *pdev)
dev_set_drvdata(dev, cif_dev);
cif_dev->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cif_regs");
cif_dev->base_addr = devm_ioremap_resource(dev, res);
if (IS_ERR(cif_dev->base_addr))
return PTR_ERR(cif_dev->base_addr);
@@ -432,6 +469,17 @@ static int rkcif_plat_probe(struct platform_device *pdev)
cif_dev->irq = irq;
data = match->data;
cif_dev->chip_id = data->chip_id;
if (data->chip_id == CHIP_RK1808_CIF) {
using_pingpong = 1;
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);
} else {
using_pingpong = 0;
}
if (data->clks_num > RKCIF_MAX_BUS_CLK ||
data->rsts_num > RKCIF_MAX_RESET) {
dev_err(dev, "out of range: clks(%d %d) rsts(%d %d)\n",

View File

@@ -16,10 +16,12 @@
#include <media/videobuf2-v4l2.h>
#define CIF_DRIVER_NAME "rkcif"
#define CIF_VIDEODEVICE_NAME "stream_cif"
#define RKCIF_MAX_BUS_CLK 8
#define RKCIF_MAX_SENSOR 2
#define RKCIF_MAX_RESET 3
#define RKCIF_MAX_RESET 5
#define RKCIF_MAX_CSI_CHANNEL 4
#define RKCIF_DEFAULT_WIDTH 640
#define RKCIF_DEFAULT_HEIGHT 480
@@ -27,12 +29,27 @@
#define write_cif_reg(base, addr, val) writel(val, (addr) + (base))
#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))
enum rkcif_state {
RKCIF_STATE_DISABLED,
RKCIF_STATE_READY,
RKCIF_STATE_STREAMING
};
enum rkcif_chip_id {
CHIP_PX30_CIF,
CHIP_RK1808_CIF,
CHIP_RK3128_CIF,
CHIP_RK3288_CIF
};
enum host_type_t {
RK_CSI_RXHOST,
RK_DSI_RXHOST
};
struct rkcif_buffer {
struct vb2_v4l2_buffer vb;
struct list_head queue;
@@ -62,6 +79,7 @@ static inline struct rkcif_buffer *to_rkcif_buffer(struct vb2_v4l2_buffer *vb)
struct rkcif_sensor_info {
struct v4l2_subdev *sd;
struct v4l2_mbus_config mbus;
int lanes;
};
/*
@@ -97,6 +115,21 @@ struct cif_input_fmt {
enum cif_fmt_type fmt_type;
};
struct csi_channel_info {
unsigned char id;
unsigned char enable; /* capture enable */
unsigned char vc;
unsigned char data_type;
unsigned char crop_en;
unsigned char cmd_mode_en;
unsigned char fmt_val;
unsigned int width;
unsigned int height;
unsigned int virtual_width;
unsigned int crop_st_x;
unsigned int crop_st_y;
};
/*
* struct rkcif_stream - Stream states TODO
*
@@ -106,6 +139,7 @@ struct cif_input_fmt {
*
* rkcif 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
*/
struct rkcif_stream {
struct rkcif_device *cifdev;
@@ -113,6 +147,7 @@ struct rkcif_stream {
bool stopping;
wait_queue_head_t wq_stopped;
int frame_idx;
int frame_phase;
/* lock between irq and buf_queue */
spinlock_t vbq_lock;
@@ -120,6 +155,7 @@ struct rkcif_stream {
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;
@@ -148,6 +184,7 @@ struct rkcif_device {
struct device *dev;
int irq;
void __iomem *base_addr;
void __iomem *csi_base;
struct clk *clks[RKCIF_MAX_BUS_CLK];
int clk_size;
struct vb2_alloc_ctx *alloc_ctx;
@@ -165,6 +202,10 @@ struct rkcif_device {
struct rkcif_sensor_info *active_sensor;
struct rkcif_stream stream;
struct csi_channel_info channels[RKCIF_MAX_CSI_CHANNEL];
int num_channels;
int chip_id;
};
void rkcif_unregister_stream_vdev(struct rkcif_device *dev);

View File

@@ -37,6 +37,59 @@
#define CIF_LAST_LINE 0x68
#define CIF_LAST_PIX 0x6c
/* RK1808 CIF CSI Registers Offset */
#define CIF_CSI_ID0_CTRL0 0x80
#define CIF_CSI_ID0_CTRL1 0x84
#define CIF_CSI_ID1_CTRL0 0x88
#define CIF_CSI_ID1_CTRL1 0x8c
#define CIF_CSI_ID2_CTRL0 0x90
#define CIF_CSI_ID2_CTRL1 0x94
#define CIF_CSI_ID3_CTRL0 0x98
#define CIF_CSI_ID3_CTRL1 0x9c
#define CIF_CSI_WATER_LINE 0xa0
#define CIF_CSI_FRM0_ADDR_Y_ID0 0xa4
#define CIF_CSI_FRM1_ADDR_Y_ID0 0xa8
#define CIF_CSI_FRM0_ADDR_UV_ID0 0xac
#define CIF_CSI_FRM1_ADDR_UV_ID0 0xb0
#define CIF_CSI_FRM0_VLW_Y_ID0 0xb4
#define CIF_CSI_FRM1_VLW_Y_ID0 0xb8
#define CIF_CSI_FRM0_VLW_UV_ID0 0xbc
#define CIF_CSI_FRM1_VLW_UV_ID0 0xc0
#define CIF_CSI_FRM0_ADDR_Y_ID1 0xc4
#define CIF_CSI_FRM1_ADDR_Y_ID1 0xc8
#define CIF_CSI_FRM0_ADDR_UV_ID1 0xcc
#define CIF_CSI_FRM1_ADDR_UV_ID1 0xd0
#define CIF_CSI_FRM0_VLW_Y_ID1 0xd4
#define CIF_CSI_FRM1_VLW_Y_ID1 0xd8
#define CIF_CSI_FRM0_VLW_UV_ID1 0xdc
#define CIF_CSI_FRM1_VLW_UV_ID1 0xe0
#define CIF_CSI_FRM0_ADDR_Y_ID2 0xe4
#define CIF_CSI_FRM1_ADDR_Y_ID2 0xe8
#define CIF_CSI_FRM0_ADDR_UV_ID2 0xec
#define CIF_CSI_FRM1_ADDR_UV_ID2 0xf0
#define CIF_CSI_FRM0_VLW_Y_ID2 0xf4
#define CIF_CSI_FRM1_VLW_Y_ID2 0xf8
#define CIF_CSI_FRM0_VLW_UV_ID2 0xfc
#define CIF_CSI_FRM1_VLW_UV_ID2 0x100
#define CIF_CSI_FRM0_ADDR_Y_ID3 0x104
#define CIF_CSI_FRM1_ADDR_Y_ID3 0x108
#define CIF_CSI_FRM0_ADDR_UV_ID3 0x10c
#define CIF_CSI_FRM1_ADDR_UV_ID3 0x110
#define CIF_CSI_FRM0_VLW_Y_ID3 0x114
#define CIF_CSI_FRM1_VLW_Y_ID3 0x118
#define CIF_CSI_FRM0_VLW_UV_ID3 0x11c
#define CIF_CSI_FRM1_VLW_UV_ID3 0x120
#define CIF_CSI_INTEN 0x124
#define CIF_CSI_INTSTAT 0x128
#define CIF_CSI_LINE_INT_NUM_ID0_1 0x12c
#define CIF_CSI_LINE_INT_NUM_ID2_3 0x130
#define CIF_CSI_LINE_CNT_ID0_1 0x134
#define CIF_CSI_LINE_CNT_ID2_3 0x138
#define CIF_CSI_ID0_CROP_START 0x13c
#define CIF_CSI_ID1_CROP_START 0x140
#define CIF_CSI_ID2_CROP_START 0x144
#define CIF_CSI_ID3_CROP_START 0x148
/* The key register bit description */
/* CIF_CTRL Reg */
@@ -119,4 +172,72 @@
#define CIF_CROP_Y_SHIFT 16
#define CIF_CROP_X_SHIFT 0
/* CIF_CSI_ID_CTRL0 */
#define CSI_DISABLE_CAPTURE (0x0 << 0)
#define CSI_ENABLE_CAPTURE (0x1 << 0)
#define CSI_WRDDR_TYPE_RAW8 (0x0 << 1)
#define CSI_WRDDR_TYPE_RAW10 (0x1 << 1)
#define CSI_WRDDR_TYPE_RAW12 (0x2 << 1)
#define CSI_WRDDR_TYPE_RGB888 (0x3 << 1)
#define CSI_WRDDR_TYPE_YUV422 (0x4 << 1)
#define CSI_DISABLE_COMMAND_MODE (0x0 << 4)
#define CSI_ENABLE_COMMAND_MODE (0x1 << 4)
#define CSI_DISABLE_CROP (0x0 << 5)
#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))
#define CSI_DMA_Y_FIFO_OVERFLOW_INTEN (0x1 << 16)
#define CSI_DMA_UV_FIFO_OVERFLOW_INTEN (0x1 << 17)
#define CSI_CONFIG_FIFO_OVERFLOW_INTEN (0x1 << 18)
#define CSI_BANDWIDTH_LACK_INTEN (0x1 << 19)
#define CSI_RX_FIFO_OVERFLOW_INTEN (0x1 << 20)
#define CSI_ALL_FRAME_START_INTEN (0xff << 0)
#define CSI_ALL_FRAME_END_INTEN (0xff << 8)
#define CSI_ALL_ERROR_INTEN (0x1f << 16)
/* CIF_CSI_INTSTAT */
#define CSI_FRAME0_START_ID0 (0x1 << 0)
#define CSI_FRAME1_START_ID0 (0x1 << 1)
#define CSI_FRAME0_START_ID1 (0x1 << 2)
#define CSI_FRAME1_START_ID1 (0x1 << 3)
#define CSI_FRAME0_START_ID2 (0x1 << 4)
#define CSI_FRAME1_START_ID2 (0x1 << 5)
#define CSI_FRAME0_START_ID3 (0x1 << 6)
#define CSI_FRAME1_START_ID3 (0x1 << 7)
#define CSI_FRAME0_END_ID0 (0x1 << 8)
#define CSI_FRAME1_END_ID0 (0x1 << 9)
#define CSI_FRAME0_END_ID1 (0x1 << 10)
#define CSI_FRAME1_END_ID1 (0x1 << 11)
#define CSI_FRAME0_END_ID2 (0x1 << 12)
#define CSI_FRAME1_END_ID2 (0x1 << 13)
#define CSI_FRAME0_END_ID3 (0x1 << 14)
#define CSI_FRAME1_END_ID3 (0x1 << 15)
#define CSI_DMA_Y_FIFO_OVERFLOW (0x1 << 16)
#define CSI_DMA_UV_FIFO_OVERFLOW (0x1 << 17)
#define CSI_CONFIG_FIFO_OVERFLOW (0x1 << 18)
#define CSI_BANDWIDTH_LACK (0x1 << 19)
#define CSI_RX_FIFO_OVERFLOW (0x1 << 20)
#define CSI_FIFO_OVERFLOW (CSI_DMA_Y_FIFO_OVERFLOW | \
CSI_DMA_UV_FIFO_OVERFLOW | \
CSI_CONFIG_FIFO_OVERFLOW | \
CSI_RX_FIFO_OVERFLOW)
/* CSI Host Registers Define */
#define CSIHOST_N_LANES 0x04
#define CSIHOST_PHY_RSTZ 0x0c
#define CSIHOST_RESETN 0x10
#define CSIHOST_CONTROL 0x40
#define SW_CPHY_EN(x) ((x) << 0)
#define SW_DSI_EN(x) ((x) << 4)
#define SW_DATATYPE_FS(x) ((x) << 8)
#define SW_DATATYPE_FE(x) ((x) << 14)
#define SW_DATATYPE_LS(x) ((x) << 20)
#define SW_DATATYPE_LE(x) ((x) << 26)
#endif