2342 lines
65 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip Flexbus CIF Driver
*
* Copyright (C) 2024 Rockchip Electronics Co., Ltd.
*/
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/iommu.h>
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-subdev.h>
#include <media/videobuf2-cma-sg.h>
#include <media/videobuf2-dma-contig.h>
#include <media/videobuf2-dma-sg.h>
#include "dev.h"
#include "common.h"
#define CIF_REQ_BUFS_MIN 1
#define CIF_MIN_WIDTH 64
#define CIF_MIN_HEIGHT 64
#define CIF_MAX_WIDTH 8192
#define CIF_MAX_HEIGHT 8192
#define OUTPUT_STEP_WISE 8
#define FLEXBUS_CIF_PLANE_Y 0
#define FLEXBUS_CIF_PLANE_CBCR 1
#define FLEXBUS_CIF_MAX_PLANE 3
#define STREAM_PAD_SINK 0
#define STREAM_PAD_SOURCE 1
#define CIF_TIMEOUT_FRAME_NUM (2)
#define CIF_PCLK_DUAL_EDGE (V4L2_MBUS_PCLK_SAMPLE_RISING |\
V4L2_MBUS_PCLK_SAMPLE_FALLING)
/*
* Round up height when allocate memory so that Rockchip encoder can
* use DMA buffer directly, though this may waste a bit of memory.
*/
#define MEMORY_ALIGN_ROUND_UP_HEIGHT 16
/* Get xsubs and ysubs for fourcc formats
*
* @xsubs: horizontal color samples in a 4*4 matrix, for yuv
* @ysubs: vertical color samples in a 4*4 matrix, for yuv
*/
static int fcc_xysubs(u32 fcc, u32 *xsubs, u32 *ysubs)
{
switch (fcc) {
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_VYUY:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YVYU:
*xsubs = 2;
*ysubs = 1;
break;
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV12:
*xsubs = 2;
*ysubs = 2;
break;
default:
return -EINVAL;
}
return 0;
}
static const struct cif_output_fmt out_fmts[] = {
{
.fourcc = V4L2_PIX_FMT_YUYV,
.cplanes = 2,
.mplanes = 1,
.bpp = { 8, 16 },
.cif_yuv_order = CIF_OUTPUT_ORDER_YUYV,
}, {
.fourcc = V4L2_PIX_FMT_YVYU,
.cplanes = 2,
.mplanes = 1,
.bpp = { 8, 16 },
.cif_yuv_order = CIF_OUTPUT_ORDER_YVYU,
}, {
.fourcc = V4L2_PIX_FMT_UYVY,
.cplanes = 2,
.mplanes = 1,
.bpp = { 8, 16 },
.cif_yuv_order = CIF_OUTPUT_ORDER_UYVY,
}, {
.fourcc = V4L2_PIX_FMT_VYUY,
.cplanes = 2,
.mplanes = 1,
.bpp = { 8, 16 },
.cif_yuv_order = CIF_OUTPUT_ORDER_VYUY,
},
{
.fourcc = V4L2_PIX_FMT_RGB24,
.cplanes = 1,
.mplanes = 1,
.bpp = { 24 },
.cif_yuv_order = CIF_OUTPUT_ORDER_YVYU,
},
{
.fourcc = V4L2_PIX_FMT_BGR24,
.cplanes = 1,
.mplanes = 1,
.bpp = { 24 },
.cif_yuv_order = CIF_OUTPUT_ORDER_YVYU,
}
};
static const struct cif_input_fmt in_fmts[] = {
{
.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.cif_yuv_order = CIF_INPUT_ORDER_YUYV,
.field = V4L2_FIELD_NONE,
}, {
.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.cif_yuv_order = CIF_INPUT_ORDER_YUYV,
.field = V4L2_FIELD_INTERLACED,
}, {
.mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
.cif_yuv_order = CIF_INPUT_ORDER_YVYU,
.field = V4L2_FIELD_NONE,
}, {
.mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
.cif_yuv_order = CIF_INPUT_ORDER_YVYU,
.field = V4L2_FIELD_INTERLACED,
}, {
.mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
.cif_yuv_order = CIF_INPUT_ORDER_UYVY,
.field = V4L2_FIELD_NONE,
}, {
.mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
.cif_yuv_order = CIF_INPUT_ORDER_UYVY,
.field = V4L2_FIELD_INTERLACED,
}, {
.mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
.cif_yuv_order = CIF_INPUT_ORDER_VYUY,
.field = V4L2_FIELD_NONE,
}, {
.mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
.cif_yuv_order = CIF_INPUT_ORDER_VYUY,
.field = V4L2_FIELD_INTERLACED,
}
};
static int flexbus_cif_output_fmt_check(struct flexbus_cif_stream *stream,
const struct cif_output_fmt *output_fmt)
{
const struct cif_input_fmt *input_fmt = stream->cif_fmt_in;
int ret = -EINVAL;
switch (input_fmt->mbus_code) {
case MEDIA_BUS_FMT_YUYV8_2X8:
case MEDIA_BUS_FMT_YVYU8_2X8:
case MEDIA_BUS_FMT_UYVY8_2X8:
case MEDIA_BUS_FMT_VYUY8_2X8:
if (output_fmt->fourcc == V4L2_PIX_FMT_YUYV ||
output_fmt->fourcc == V4L2_PIX_FMT_YVYU ||
output_fmt->fourcc == V4L2_PIX_FMT_UYVY ||
output_fmt->fourcc == V4L2_PIX_FMT_VYUY ||
output_fmt->fourcc == V4L2_PIX_FMT_RGB24 ||
output_fmt->fourcc == V4L2_PIX_FMT_BGR24)
ret = 0;
break;
default:
break;
}
if (ret)
v4l2_err(&stream->cif_dev->v4l2_dev,
"input mbus_code 0x%x, can't transform to %c%c%c%c\n",
input_fmt->mbus_code,
output_fmt->fourcc & 0xff,
(output_fmt->fourcc >> 8) & 0xff,
(output_fmt->fourcc >> 16) & 0xff,
(output_fmt->fourcc >> 24) & 0xff);
return ret;
}
static struct v4l2_subdev *get_remote_sensor(struct flexbus_cif_stream *stream, u16 *index)
{
struct media_pad *local, *remote;
struct media_entity *sensor_me;
struct v4l2_subdev *sub = NULL;
local = &stream->vnode.vdev.entity.pads[0];
if (!local) {
v4l2_err(&stream->cif_dev->v4l2_dev,
"%s: video pad[0] is null\n", __func__);
return NULL;
}
remote = media_pad_remote_pad_first(local);
if (!remote) {
v4l2_err(&stream->cif_dev->v4l2_dev,
"%s: remote pad is null\n", __func__);
return NULL;
}
if (index)
*index = remote->index;
sensor_me = remote->entity;
sub = media_entity_to_v4l2_subdev(sensor_me);
return sub;
}
static void get_remote_terminal_sensor(struct flexbus_cif_stream *stream,
struct v4l2_subdev **sensor_sd)
{
struct media_graph graph;
struct media_entity *entity = &stream->vnode.vdev.entity;
struct media_device *mdev = entity->graph_obj.mdev;
int ret;
/* Walk the graph to locate sensor nodes. */
mutex_lock(&mdev->graph_mutex);
ret = media_graph_walk_init(&graph, mdev);
if (ret) {
mutex_unlock(&mdev->graph_mutex);
*sensor_sd = NULL;
return;
}
media_graph_walk_start(&graph, entity);
while ((entity = media_graph_walk_next(&graph))) {
if (entity->function == MEDIA_ENT_F_CAM_SENSOR)
break;
}
mutex_unlock(&mdev->graph_mutex);
media_graph_walk_cleanup(&graph);
if (entity)
*sensor_sd = media_entity_to_v4l2_subdev(entity);
else
*sensor_sd = NULL;
}
static struct flexbus_cif_sensor_info *sd_to_sensor(struct flexbus_cif_device *dev,
struct v4l2_subdev *sd)
{
u32 i;
for (i = 0; i < dev->num_sensors; ++i)
if (dev->sensors[i].sd == sd)
return &dev->sensors[i];
return NULL;
}
static const struct
cif_input_fmt *get_input_fmt(struct v4l2_subdev *sd, struct v4l2_rect *rect,
u32 pad_id)
{
struct v4l2_subdev_format fmt;
int ret;
u32 i;
fmt.pad = 0;
fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
fmt.reserved[0] = 0;
fmt.format.field = V4L2_FIELD_NONE;
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
if (ret < 0) {
v4l2_warn(sd->v4l2_dev,
"sensor fmt invalid, set to default size\n");
goto set_default;
}
v4l2_dbg(1, flexbus_cif_debug, sd->v4l2_dev,
"remote fmt: mbus code:0x%x, size:%dx%d, field: %d\n",
fmt.format.code, fmt.format.width,
fmt.format.height, fmt.format.field);
rect->left = 0;
rect->top = 0;
rect->width = fmt.format.width;
rect->height = fmt.format.height;
for (i = 0; i < ARRAY_SIZE(in_fmts); i++)
if (fmt.format.code == in_fmts[i].mbus_code &&
fmt.format.field == in_fmts[i].field)
return &in_fmts[i];
v4l2_err(sd->v4l2_dev, "remote sensor mbus code not supported\n");
set_default:
rect->left = 0;
rect->top = 0;
rect->width = FLEXBUS_CIF_DEFAULT_WIDTH;
rect->height = FLEXBUS_CIF_DEFAULT_HEIGHT;
return NULL;
}
static const struct
cif_output_fmt *flexbus_cif_find_output_fmt(struct flexbus_cif_stream *stream, u32 pixelfmt)
{
const struct cif_output_fmt *fmt;
u32 i;
for (i = 0; i < ARRAY_SIZE(out_fmts); i++) {
fmt = &out_fmts[i];
if (fmt->fourcc == pixelfmt)
return fmt;
}
return NULL;
}
static void flexbus_cif_assign_new_buffer_init(struct flexbus_cif_stream *stream,
int channel_id)
{
struct flexbus_cif_device *dev = stream->cif_dev;
u32 frm0_addr;
u32 frm1_addr;
unsigned long flags;
struct flexbus_cif_dummy_buffer *dummy_buf = &dev->dummy_buf;
frm0_addr = FLEXBUS_DMA_DST_ADDR0;
frm1_addr = FLEXBUS_DMA_DST_ADDR1;
spin_lock_irqsave(&stream->vbq_lock, flags);
if (!stream->curr_buf) {
if (!list_empty(&stream->buf_head)) {
stream->curr_buf = list_first_entry(&stream->buf_head,
struct flexbus_cif_buffer,
queue);
list_del(&stream->curr_buf->queue);
}
}
if (stream->curr_buf) {
flexbus_cif_write_register(dev, frm0_addr,
stream->curr_buf->buff_addr[FLEXBUS_CIF_PLANE_Y] >> 2);
} else {
if (dummy_buf->vaddr) {
flexbus_cif_write_register(dev, frm0_addr, dummy_buf->dma_addr);
} else {
if (stream->lack_buf_cnt < 2)
stream->lack_buf_cnt++;
}
}
if (!stream->next_buf) {
if (!list_empty(&stream->buf_head)) {
stream->next_buf = list_first_entry(&stream->buf_head,
struct flexbus_cif_buffer, queue);
list_del(&stream->next_buf->queue);
}
}
if (stream->next_buf) {
flexbus_cif_write_register(dev, frm1_addr,
stream->next_buf->buff_addr[FLEXBUS_CIF_PLANE_Y] >> 2);
} else {
if (dummy_buf->vaddr) {
flexbus_cif_write_register(dev, frm1_addr, dummy_buf->dma_addr);
} else {
if (stream->curr_buf) {
stream->next_buf = stream->curr_buf;
flexbus_cif_write_register(dev, frm1_addr,
stream->next_buf->buff_addr[FLEXBUS_CIF_PLANE_Y] >> 2);
}
if (stream->lack_buf_cnt < 2)
stream->lack_buf_cnt++;
}
}
spin_unlock_irqrestore(&stream->vbq_lock, flags);
}
static int flexbus_cif_assign_new_buffer_update(struct flexbus_cif_stream *stream,
int channel_id)
{
struct flexbus_cif_device *dev = stream->cif_dev;
struct flexbus_cif_dummy_buffer *dummy_buf = &dev->dummy_buf;
struct flexbus_cif_buffer *buffer = NULL;
u32 frm_addr;
int ret = 0;
unsigned long flags;
frm_addr = stream->frame_phase & CIF_CSI_FRAME0_READY ?
FLEXBUS_DMA_DST_ADDR0 : FLEXBUS_DMA_DST_ADDR1;
spin_lock_irqsave(&stream->vbq_lock, flags);
if (!list_empty(&stream->buf_head)) {
if (stream->frame_phase == CIF_CSI_FRAME0_READY) {
if (!stream->curr_buf)
ret = -EINVAL;
stream->curr_buf = list_first_entry(&stream->buf_head,
struct flexbus_cif_buffer, queue);
if (stream->curr_buf) {
list_del(&stream->curr_buf->queue);
buffer = stream->curr_buf;
v4l2_dbg(3, flexbus_cif_debug, &dev->v4l2_dev,
"stream[%d] update curr_buf 0x%x\n",
stream->id, buffer->buff_addr[0]);
}
} else if (stream->frame_phase == CIF_CSI_FRAME1_READY) {
if (!stream->next_buf)
ret = -EINVAL;
stream->next_buf = list_first_entry(&stream->buf_head,
struct flexbus_cif_buffer, queue);
if (stream->next_buf) {
list_del(&stream->next_buf->queue);
buffer = stream->next_buf;
v4l2_dbg(3, flexbus_cif_debug, &dev->v4l2_dev,
"stream[%d] update next_buf 0x%x\n",
stream->id, buffer->buff_addr[0]);
}
}
} else {
buffer = NULL;
if (dummy_buf->vaddr) {
if (stream->frame_phase == CIF_CSI_FRAME0_READY) {
stream->curr_buf = NULL;
} else if (stream->frame_phase == CIF_CSI_FRAME1_READY) {
if (stream->cif_fmt_in->field == V4L2_FIELD_INTERLACED) {
stream->next_buf = stream->curr_buf;
buffer = stream->next_buf;
} else {
stream->next_buf = NULL;
}
}
} else if (stream->curr_buf && stream->next_buf &&
stream->curr_buf != stream->next_buf) {
if (stream->frame_phase == CIF_CSI_FRAME0_READY) {
stream->curr_buf = stream->next_buf;
buffer = stream->next_buf;
} else if (stream->frame_phase == CIF_CSI_FRAME1_READY) {
stream->next_buf = stream->curr_buf;
buffer = stream->curr_buf;
}
if (stream->lack_buf_cnt < 2)
stream->lack_buf_cnt++;
} else {
stream->curr_buf = NULL;
stream->next_buf = NULL;
if (stream->lack_buf_cnt < 2)
stream->lack_buf_cnt++;
}
}
stream->frame_phase_cache = stream->frame_phase;
if (buffer) {
flexbus_cif_write_register(dev, frm_addr,
buffer->buff_addr[FLEXBUS_CIF_PLANE_Y] >> 2);
} else {
if (dummy_buf->vaddr) {
flexbus_cif_write_register(dev, frm_addr, dummy_buf->dma_addr >> 2);
dev->err_state |= (FLEXBUS_CIF_ERR_ID0_NOT_BUF << stream->id);
dev->irq_stats.not_active_buf_cnt[stream->id]++;
} else {
ret = -EINVAL;
stream->curr_buf = NULL;
stream->next_buf = NULL;
dev->err_state |= (FLEXBUS_CIF_ERR_ID0_NOT_BUF << stream->id);
dev->irq_stats.not_active_buf_cnt[stream->id]++;
}
}
spin_unlock_irqrestore(&stream->vbq_lock, flags);
return ret;
}
static int flexbus_cif_assign_new_buffer_pingpong(struct flexbus_cif_stream *stream,
int init, int channel_id)
{
int ret = 0;
if (init)
flexbus_cif_assign_new_buffer_init(stream, channel_id);
else
ret = flexbus_cif_assign_new_buffer_update(stream, channel_id);
return ret;
}
static void flexbus_cif_stream_stop(struct flexbus_cif_stream *stream)
{
struct flexbus_cif_device *cif_dev = stream->cif_dev;
if (atomic_read(&cif_dev->pipe.stream_cnt) == 1)
flexbus_cif_write_register(cif_dev, FLEXBUS_ENR, (BIT(1) << 16));
stream->state = FLEXBUS_CIF_STATE_READY;
}
static int flexbus_cif_queue_setup(struct vb2_queue *queue,
unsigned int *num_buffers,
unsigned int *num_planes,
unsigned int sizes[],
struct device *alloc_ctxs[])
{
struct flexbus_cif_stream *stream = queue->drv_priv;
struct flexbus_cif_device *dev = stream->cif_dev;
const struct v4l2_pix_format_mplane *pixm = NULL;
const struct cif_output_fmt *cif_fmt;
u32 i, height;
pixm = &stream->pixm;
cif_fmt = stream->cif_fmt_out;
*num_planes = cif_fmt->mplanes;
if (stream->crop_enable)
height = stream->crop[CROP_SRC_ACT].height;
else
height = pixm->height;
for (i = 0; i < cif_fmt->mplanes; i++) {
const struct v4l2_plane_pix_format *plane_fmt;
int h = round_up(height, MEMORY_ALIGN_ROUND_UP_HEIGHT);
plane_fmt = &pixm->plane_fmt[i];
sizes[i] = plane_fmt->sizeimage / height * h;
}
v4l2_dbg(1, flexbus_cif_debug, &dev->v4l2_dev, "%s count %d, size %d\n",
v4l2_type_names[queue->type], *num_buffers, sizes[0]);
return 0;
}
static void flexbus_cif_check_buffer_update_pingpong(struct flexbus_cif_stream *stream,
int channel_id)
{
struct flexbus_cif_device *dev = stream->cif_dev;
struct flexbus_cif_buffer *buffer = NULL;
u32 frm_addr = 0;
u32 frm0_addr = 0;
u32 frm1_addr = 0;
unsigned long flags;
int frame_phase = 0;
bool is_dual_update_buf = false;
spin_lock_irqsave(&stream->vbq_lock, flags);
if (stream->state == FLEXBUS_CIF_STATE_STREAMING &&
(stream->curr_buf == NULL ||
stream->next_buf == NULL)) {
frame_phase = stream->frame_phase_cache;
frm0_addr = FLEXBUS_DMA_DST_ADDR0;
frm1_addr = FLEXBUS_DMA_DST_ADDR1;
if (frame_phase & CIF_CSI_FRAME0_READY)
frm_addr = frm0_addr;
else
frm_addr = frm1_addr;
if (stream->curr_buf == NULL && stream->next_buf == NULL)
is_dual_update_buf = true;
if (!list_empty(&stream->buf_head)) {
if (frame_phase == CIF_CSI_FRAME0_READY) {
stream->curr_buf = list_first_entry(&stream->buf_head,
struct flexbus_cif_buffer, queue);
if (stream->curr_buf) {
list_del(&stream->curr_buf->queue);
buffer = stream->curr_buf;
}
if (buffer && is_dual_update_buf)
stream->next_buf = buffer;
} else if (frame_phase == CIF_CSI_FRAME1_READY) {
stream->next_buf = list_first_entry(&stream->buf_head,
struct flexbus_cif_buffer, queue);
if (stream->next_buf) {
list_del(&stream->next_buf->queue);
buffer = stream->next_buf;
}
if (buffer && is_dual_update_buf)
stream->curr_buf = buffer;
}
} else {
v4l2_info(&dev->v4l2_dev, "%s %d\n", __func__, __LINE__);
}
if (buffer) {
if (is_dual_update_buf) {
flexbus_cif_write_register(dev, frm0_addr,
buffer->buff_addr[FLEXBUS_CIF_PLANE_Y]);
flexbus_cif_write_register(dev, frm1_addr,
buffer->buff_addr[FLEXBUS_CIF_PLANE_Y]);
} else {
flexbus_cif_write_register(dev, frm_addr,
buffer->buff_addr[FLEXBUS_CIF_PLANE_Y]);
}
}
v4l2_dbg(3, flexbus_cif_debug, &stream->cif_dev->v4l2_dev,
"%s, stream[%d] update buffer, frame_phase %d, lack_buf_cnt %d\n",
__func__, stream->id, frame_phase,
stream->lack_buf_cnt);
if (stream->lack_buf_cnt)
stream->lack_buf_cnt--;
} else {
v4l2_info(&dev->v4l2_dev, "%s %d, state %d, curr_buf %p, next_buf %p\n",
__func__, __LINE__, stream->state, stream->curr_buf, stream->next_buf);
}
spin_unlock_irqrestore(&stream->vbq_lock, flags);
}
/*
* The vb2_buffer are stored in flexbus_cif_buffer, in order to unify
* mplane buffer and none-mplane buffer.
*/
static void flexbus_cif_buf_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct flexbus_cif_buffer *cifbuf = to_flexbus_cif_buffer(vbuf);
struct vb2_queue *queue = vb->vb2_queue;
struct flexbus_cif_stream *stream = queue->drv_priv;
const struct cif_output_fmt *fmt = stream->cif_fmt_out;
unsigned long flags;
int i;
memset(cifbuf->buff_addr, 0, sizeof(cifbuf->buff_addr));
/* If mplanes > 1, every c-plane has its own m-plane,
* otherwise, multiple c-planes are in the same m-plane
*/
for (i = 0; i < fmt->mplanes; i++) {
if (stream->cif_dev->is_dma_sg_ops) {
struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, i);
cifbuf->buff_addr[i] = sg_dma_address(sgt->sgl);
} else {
cifbuf->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
}
}
spin_lock_irqsave(&stream->vbq_lock, flags);
list_add_tail(&cifbuf->queue, &stream->buf_head);
spin_unlock_irqrestore(&stream->vbq_lock, flags);
if (stream->lack_buf_cnt)
flexbus_cif_check_buffer_update_pingpong(stream, stream->id);
v4l2_dbg(3, flexbus_cif_debug, &stream->cif_dev->v4l2_dev,
"stream[%d] buf queue, index: %d, dma_addr 0x%x\n",
stream->id, vb->index, cifbuf->buff_addr[0]);
atomic_inc(&stream->buf_cnt);
}
static int flexbus_cif_create_dummy_buf(struct flexbus_cif_stream *stream)
{
struct flexbus_cif_device *dev = stream->cif_dev;
struct flexbus_cif_dummy_buffer *dummy_buf = &dev->dummy_buf;
int ret = 0;
if (stream->cif_fmt_out->fourcc == V4L2_PIX_FMT_RGB24 ||
stream->cif_fmt_out->fourcc == V4L2_PIX_FMT_BGR24)
dummy_buf->size = stream->pixm.width * stream->pixm.height * 3;
else
dummy_buf->size = stream->pixm.width * stream->pixm.height * 2;
dummy_buf->is_need_vaddr = true;
dummy_buf->is_need_dbuf = true;
ret = flexbus_cif_alloc_buffer(dev, dummy_buf);
if (ret) {
v4l2_err(&dev->v4l2_dev,
"Failed to allocate the memory for dummy buffer\n");
return -ENOMEM;
}
v4l2_info(&dev->v4l2_dev, "Allocate dummy buffer, size: 0x%08x\n",
dummy_buf->size);
return ret;
}
static void flexbus_cif_destroy_dummy_buf(struct flexbus_cif_stream *stream)
{
struct flexbus_cif_device *dev = stream->cif_dev;
struct flexbus_cif_dummy_buffer *dummy_buf = &dev->dummy_buf;
if (dummy_buf->vaddr)
flexbus_cif_free_buffer(dev, dummy_buf);
dummy_buf->dma_addr = 0;
dummy_buf->vaddr = NULL;
}
static void flexbus_cif_stop_streaming(struct vb2_queue *queue)
{
struct flexbus_cif_stream *stream = queue->drv_priv;
struct flexbus_cif_vdev_node *node = &stream->vnode;
struct flexbus_cif_device *dev = stream->cif_dev;
struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
struct flexbus_cif_buffer *buf = NULL;
int ret;
unsigned long flags;
mutex_lock(&dev->stream_lock);
v4l2_info(&dev->v4l2_dev, "stream[%d] start stopping\n",
stream->id);
stream->stopping = true;
ret = wait_event_timeout(stream->wq_stopped,
stream->state != FLEXBUS_CIF_STATE_STREAMING,
msecs_to_jiffies(500));
if (!ret) {
flexbus_cif_stream_stop(stream);
stream->stopping = false;
}
video_device_pipeline_stop(&node->vdev);
ret = dev->pipe.set_stream(&dev->pipe, false);
if (ret < 0)
v4l2_err(v4l2_dev, "pipeline stream-off failed error:%d\n",
ret);
/* release buffers */
spin_lock_irqsave(&stream->vbq_lock, flags);
if (stream->curr_buf)
list_add_tail(&stream->curr_buf->queue, &stream->buf_head);
if (stream->next_buf &&
stream->next_buf != stream->curr_buf)
list_add_tail(&stream->next_buf->queue, &stream->buf_head);
spin_unlock_irqrestore(&stream->vbq_lock, flags);
stream->curr_buf = NULL;
stream->next_buf = NULL;
list_for_each_entry(buf, &stream->buf_head, queue) {
v4l2_dbg(3, flexbus_cif_debug, &dev->v4l2_dev,
"stream[%d] buf return addr 0x%x\n",
stream->id, buf->buff_addr[0]);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
}
INIT_LIST_HEAD(&stream->buf_head);
while (!list_empty(&stream->vb_done_list)) {
buf = list_first_entry(&stream->vb_done_list,
struct flexbus_cif_buffer, queue);
if (buf) {
list_del(&buf->queue);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
}
}
atomic_set(&stream->buf_cnt, 0);
stream->lack_buf_cnt = 0;
ret = dev->pipe.close(&dev->pipe);
if (ret < 0)
v4l2_err(v4l2_dev, "pipeline close failed error:%d\n", ret);
tasklet_disable(&stream->vb_done_tasklet);
if (dev->dummy_buf.vaddr)
flexbus_cif_destroy_dummy_buf(stream);
INIT_LIST_HEAD(&stream->vb_done_list);
v4l2_info(&dev->v4l2_dev, "stream[%d] stopping finished, dma_en 0x%x\n", stream->id, stream->dma_en);
mutex_unlock(&dev->stream_lock);
}
static u32 flexbus_cif_align_bits_per_pixel(struct flexbus_cif_stream *stream,
const struct cif_output_fmt *fmt,
int plane_index)
{
u32 bpp = 0, i, cal = 0;
if (fmt) {
switch (fmt->fourcc) {
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_GREY:
case V4L2_PIX_FMT_Y16:
bpp = fmt->bpp[plane_index];
break;
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YVYU:
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_VYUY:
bpp = fmt->bpp[plane_index];
break;
case V4L2_PIX_FMT_RGB24:
case V4L2_PIX_FMT_BGR24:
case V4L2_PIX_FMT_RGB565:
case V4L2_PIX_FMT_BGR666:
case V4L2_PIX_FMT_SRGGB8:
case V4L2_PIX_FMT_SGRBG8:
case V4L2_PIX_FMT_SGBRG8:
case V4L2_PIX_FMT_SBGGR8:
case V4L2_PIX_FMT_SRGGB10:
case V4L2_PIX_FMT_SGRBG10:
case V4L2_PIX_FMT_SGBRG10:
case V4L2_PIX_FMT_SBGGR10:
case V4L2_PIX_FMT_SRGGB12:
case V4L2_PIX_FMT_SGRBG12:
case V4L2_PIX_FMT_SGBRG12:
case V4L2_PIX_FMT_SBGGR12:
case V4L2_PIX_FMT_SBGGR16:
case V4L2_PIX_FMT_SGBRG16:
case V4L2_PIX_FMT_SGRBG16:
case V4L2_PIX_FMT_SRGGB16:
case V4l2_PIX_FMT_SPD16:
case V4l2_PIX_FMT_EBD8:
case V4L2_PIX_FMT_Y10:
case V4L2_PIX_FMT_Y12:
bpp = max(fmt->bpp[plane_index], (u8)CIF_RAW_STORED_BIT_WIDTH);
cal = CIF_RAW_STORED_BIT_WIDTH;
for (i = 1; i < 5; i++) {
if (i * cal >= bpp) {
bpp = i * cal;
break;
}
}
break;
default:
v4l2_err(&stream->cif_dev->v4l2_dev, "fourcc: %d is not supported!\n",
fmt->fourcc);
break;
}
}
return bpp;
}
static void flexbus_cif_sync_crop_info(struct flexbus_cif_stream *stream)
{
struct flexbus_cif_device *dev = stream->cif_dev;
struct v4l2_subdev_selection input_sel;
int ret;
if (dev->terminal_sensor.sd) {
input_sel.target = V4L2_SEL_TGT_CROP_BOUNDS;
input_sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
input_sel.pad = 0;
ret = v4l2_subdev_call(dev->terminal_sensor.sd,
pad, get_selection, NULL,
&input_sel);
if (!ret) {
stream->crop[CROP_SRC_SENSOR] = input_sel.r;
stream->crop_enable = true;
stream->crop_mask |= CROP_SRC_SENSOR_MASK;
dev->terminal_sensor.selection = input_sel;
} else {
dev->terminal_sensor.selection.r = dev->terminal_sensor.raw_rect;
}
}
if ((stream->crop_mask & 0x3) == (CROP_SRC_USR_MASK | CROP_SRC_SENSOR_MASK)) {
if (stream->crop[CROP_SRC_USR].left + stream->crop[CROP_SRC_USR].width >
stream->crop[CROP_SRC_SENSOR].width ||
stream->crop[CROP_SRC_USR].top + stream->crop[CROP_SRC_USR].height >
stream->crop[CROP_SRC_SENSOR].height)
stream->crop[CROP_SRC_USR] = stream->crop[CROP_SRC_SENSOR];
}
if (stream->crop_mask & CROP_SRC_USR_MASK) {
stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_USR];
if (stream->crop_mask & CROP_SRC_SENSOR_MASK) {
stream->crop[CROP_SRC_ACT].left = stream->crop[CROP_SRC_USR].left +
stream->crop[CROP_SRC_SENSOR].left;
stream->crop[CROP_SRC_ACT].top = stream->crop[CROP_SRC_USR].top +
stream->crop[CROP_SRC_SENSOR].top;
}
} else {
stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_SENSOR];
}
}
/**flexbus_cif_sanity_check_fmt - check fmt for setting
* @stream - the stream for setting
* @s_crop - the crop information
*/
static int flexbus_cif_sanity_check_fmt(struct flexbus_cif_stream *stream,
const struct v4l2_rect *s_crop)
{
struct flexbus_cif_device *dev = stream->cif_dev;
struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
struct v4l2_rect input, *crop;
if (dev->terminal_sensor.sd) {
stream->cif_fmt_in = get_input_fmt(dev->terminal_sensor.sd,
&input, stream->id);
if (!stream->cif_fmt_in) {
v4l2_err(v4l2_dev, "Input fmt is invalid\n");
return -EINVAL;
}
} else {
v4l2_err(v4l2_dev, "terminal_sensor is invalid\n");
return -EINVAL;
}
if (s_crop)
crop = (struct v4l2_rect *)s_crop;
else
crop = &stream->crop[CROP_SRC_ACT];
if (crop->width + crop->left > input.width ||
crop->height + crop->top > input.height) {
v4l2_err(v4l2_dev, "crop size is bigger than input\n");
return -EINVAL;
}
return 0;
}
static int flexbus_cif_update_sensor_info(struct flexbus_cif_stream *stream)
{
struct flexbus_cif_sensor_info *sensor, *terminal_sensor;
struct v4l2_subdev *sensor_sd;
int ret = 0;
sensor_sd = get_remote_sensor(stream, NULL);
if (!sensor_sd) {
v4l2_err(&stream->cif_dev->v4l2_dev,
"%s: stream[%d] get remote sensor_sd failed!\n",
__func__, stream->id);
return -ENODEV;
}
sensor = sd_to_sensor(stream->cif_dev, sensor_sd);
if (!sensor) {
v4l2_err(&stream->cif_dev->v4l2_dev,
"%s: stream[%d] get remote sensor failed!\n",
__func__, stream->id);
return -ENODEV;
}
ret = v4l2_subdev_call(sensor->sd, pad, get_mbus_config,
0, &sensor->mbus);
if (ret && ret != -ENOIOCTLCMD) {
v4l2_err(&stream->cif_dev->v4l2_dev,
"%s: get remote %s mbus failed!\n", __func__, sensor->sd->name);
return ret;
}
stream->cif_dev->active_sensor = sensor;
terminal_sensor = &stream->cif_dev->terminal_sensor;
get_remote_terminal_sensor(stream, &terminal_sensor->sd);
if (terminal_sensor->sd) {
ret = v4l2_subdev_call(terminal_sensor->sd, pad, get_mbus_config,
0, &terminal_sensor->mbus);
if (ret && ret != -ENOIOCTLCMD) {
v4l2_err(&stream->cif_dev->v4l2_dev,
"%s: get terminal %s mbus failed!\n",
__func__, terminal_sensor->sd->name);
return ret;
}
ret = v4l2_subdev_call(terminal_sensor->sd, video,
g_frame_interval, &terminal_sensor->fi);
if (ret) {
v4l2_err(&stream->cif_dev->v4l2_dev,
"%s: get terminal %s g_frame_interval failed!\n",
__func__, terminal_sensor->sd->name);
return ret;
}
} else {
v4l2_err(&stream->cif_dev->v4l2_dev,
"%s: stream[%d] get remote terminal sensor failed!\n",
__func__, stream->id);
return -ENODEV;
}
return ret;
}
static void flexbus_cif_config_cif_clk_sampling_edge(struct flexbus_cif_device *dev,
enum flexbus_cif_clk_edge edge)
{
u32 val = 0;
if (edge == FLEXBUS_CIF_CLK_RISING)
dev->fb_dev->config->grf_config(dev->fb_dev, 1, false, false);
else
dev->fb_dev->config->grf_config(dev->fb_dev, 1, false, true);
v4l2_dbg(1, flexbus_cif_debug, &dev->v4l2_dev,
"%s %d, val %x\n", __func__, __LINE__, val);
}
static int flexbus_cif_stream_start(struct flexbus_cif_stream *stream)
{
u32 val, mbus_flags, href_pol, vsync_pol;
struct flexbus_cif_device *dev = stream->cif_dev;
struct flexbus_cif_sensor_info *sensor_info;
struct v4l2_mbus_config *mbus;
struct flexbus_cif_cif_sof_subdev *sof_sd = &dev->cif_sof_subdev;
struct v4l2_rect rect = {0};
if (stream->state < FLEXBUS_CIF_STATE_STREAMING) {
stream->frame_idx = 0;
stream->buf_wake_up_cnt = 0;
stream->lack_buf_cnt = 0;
stream->frame_phase = 0;
stream->is_in_vblank = false;
stream->is_change_toisp = false;
}
sensor_info = dev->active_sensor;
mbus = &sensor_info->mbus;
mbus_flags = mbus->bus.parallel.flags;
if (mbus_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
flexbus_cif_config_cif_clk_sampling_edge(dev, FLEXBUS_CIF_CLK_RISING);
else
flexbus_cif_config_cif_clk_sampling_edge(dev, FLEXBUS_CIF_CLK_FALLING);
href_pol = (mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) ?
HSY_HIGH_ACTIVE : HSY_LOW_ACTIVE;
vsync_pol = (mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) ?
VSY_HIGH_ACTIVE : VSY_LOW_ACTIVE;
flexbus_cif_write_register(dev, FLEXBUS_DVP_POL, href_pol | vsync_pol);
v4l2_dbg(1, flexbus_cif_debug, &dev->v4l2_dev, "href_pol %d vsync_pol %d\n",
href_pol, vsync_pol);
v4l2_dbg(1, flexbus_cif_debug, &dev->v4l2_dev,
"mbus_flags %x V4L2_MBUS_PCLK_SAMPLE_RISING %lx\n",
mbus_flags, V4L2_MBUS_PCLK_SAMPLE_RISING);
if (stream->crop_enable) {
rect.left = stream->crop[CROP_SRC_ACT].left * 2;
rect.top = stream->crop[CROP_SRC_ACT].top;
rect.width = stream->crop[CROP_SRC_ACT].width * 2;
rect.height = stream->crop[CROP_SRC_ACT].height;
} else {
rect.left = 0;
rect.top = 0;
rect.width = stream->pixm.width * 2;
rect.height = stream->pixm.height;
}
flexbus_cif_write_register(dev, FLEXBUS_DVP_CROP_SIZE,
rect.width |
(rect.height << 16));
flexbus_cif_write_register(dev, FLEXBUS_DVP_CROP_START,
rect.top << CIF_CROP_Y_SHIFT |
rect.left);
flexbus_cif_assign_new_buffer_pingpong(stream,
FLEXBUS_CIF_YUV_ADDR_STATE_INIT,
stream->id);
flexbus_cif_write_register(dev, FLEXBUS_SLAVE_MODE, BIT(1) | BIT(0));
val = flexbus_cif_read_register(dev, FLEXBUS_RX_CTL);
val &= ~FLEXBUS_DST_WAT_LVL_MASK;
val |= dev->fb_dev->dfs_reg->dfs_8bit;
val |= FLEXBUS_CONTINUE_MODE;
val |= FLEXBUS_AUTOPAD;
flexbus_cif_write_register(dev, FLEXBUS_RX_CTL, val);
flexbus_cif_write_register(dev, FLEXBUS_COM_CTL, 0x2);
flexbus_cif_write_register(dev, FLEXBUS_DMA_WR_OUTSTD, 0x1f);
flexbus_cif_write_register(dev, FLEXBUS_DVP_ORDER, stream->cif_fmt_in->cif_yuv_order |
stream->cif_fmt_out->cif_yuv_order);
if (dev->chip_id == RK_FLEXBUS_CIF_RK3506) {
if (stream->cif_fmt_out->fourcc == V4L2_PIX_FMT_RGB24)
flexbus_cif_write_register(dev, FLEXBUS_DVP_YUV2RGB,
CIF_YUV2RGB_ENABLE | CIF_YUV2RGB_B_LSB | CIF_YUV2RGB_BT601_FULL);
else if (stream->cif_fmt_out->fourcc == V4L2_PIX_FMT_BGR24)
flexbus_cif_write_register(dev, FLEXBUS_DVP_YUV2RGB,
CIF_YUV2RGB_ENABLE | CIF_YUV2RGB_BT601_FULL);
else
flexbus_cif_write_register(dev, FLEXBUS_DVP_YUV2RGB, 0);
}
flexbus_cif_write_register_or(dev, FLEXBUS_IMR, CIF_FIFO_OVERFLOW |
CIF_BANDWIDTH_LACK |
CIF_DMA_END |
CIF_SIZE_ERR);
flexbus_cif_write_register(dev, FLEXBUS_ENR, BIT(1) | ((BIT(1) | BIT(0)) << 16));
atomic_set(&sof_sd->frm_sync_seq, 0);
stream->state = FLEXBUS_CIF_STATE_STREAMING;
return 0;
}
static int flexbus_cif_start_streaming(struct vb2_queue *queue, unsigned int count)
{
struct flexbus_cif_stream *stream = queue->drv_priv;
struct flexbus_cif_vdev_node *node = &stream->vnode;
struct flexbus_cif_device *dev = stream->cif_dev;
struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
struct flexbus_cif_sensor_info *terminal_sensor = &dev->terminal_sensor;
int ret;
v4l2_info(&dev->v4l2_dev, "stream[%d] start streaming\n", stream->id);
mutex_lock(&dev->stream_lock);
if (stream->state == FLEXBUS_CIF_STATE_STREAMING) {
ret = -EBUSY;
v4l2_err(v4l2_dev, "stream in busy state\n");
goto destroy_buf;
}
if (dev->active_sensor) {
ret = flexbus_cif_update_sensor_info(stream);
if (ret < 0) {
v4l2_err(v4l2_dev,
"update sensor info failed %d\n",
ret);
goto out;
}
}
if (terminal_sensor->sd) {
ret = v4l2_subdev_call(terminal_sensor->sd,
video, g_frame_interval, &terminal_sensor->fi);
if (ret)
terminal_sensor->fi.interval = (struct v4l2_fract) {1, 30};
flexbus_cif_sync_crop_info(stream);
}
ret = flexbus_cif_sanity_check_fmt(stream, NULL);
if (ret < 0)
goto destroy_buf;
if (dev->is_use_dummybuf && (!dev->dummy_buf.vaddr)) {
ret = flexbus_cif_create_dummy_buf(stream);
if (ret < 0) {
v4l2_err(v4l2_dev, "Failed to create dummy_buf, %d\n", ret);
goto destroy_buf;
}
}
tasklet_enable(&stream->vb_done_tasklet);
ret = dev->pipe.open(&dev->pipe, &node->vdev.entity, true);
if (ret < 0) {
v4l2_err(v4l2_dev, "open cif pipeline failed %d\n",
ret);
goto destroy_buf;
}
ret = dev->pipe.set_stream(&dev->pipe, true);
if (ret < 0)
goto stop_stream;
msleep(50);
ret = flexbus_cif_stream_start(stream);
if (ret < 0)
goto destroy_buf;
ret = video_device_pipeline_start(&node->vdev, &dev->pipe.pipe);
if (ret < 0) {
v4l2_err(&dev->v4l2_dev, "start pipeline failed %d\n",
ret);
goto pipe_stream_off;
}
goto out;
stop_stream:
flexbus_cif_stream_stop(stream);
pipe_stream_off:
dev->pipe.set_stream(&dev->pipe, false);
destroy_buf:
if (stream->next_buf)
vb2_buffer_done(&stream->next_buf->vb.vb2_buf,
VB2_BUF_STATE_QUEUED);
if (stream->curr_buf)
vb2_buffer_done(&stream->curr_buf->vb.vb2_buf,
VB2_BUF_STATE_QUEUED);
while (!list_empty(&stream->buf_head)) {
struct flexbus_cif_buffer *buf;
buf = list_first_entry(&stream->buf_head,
struct flexbus_cif_buffer, queue);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
list_del(&buf->queue);
}
out:
mutex_unlock(&dev->stream_lock);
return ret;
}
static const struct vb2_ops flexbus_cif_vb2_ops = {
.queue_setup = flexbus_cif_queue_setup,
.buf_queue = flexbus_cif_buf_queue,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
.stop_streaming = flexbus_cif_stop_streaming,
.start_streaming = flexbus_cif_start_streaming,
};
static int flexbus_cif_init_vb2_queue(struct vb2_queue *q,
struct flexbus_cif_stream *stream,
enum v4l2_buf_type buf_type)
{
q->type = buf_type;
q->io_modes = VB2_MMAP | VB2_DMABUF;
q->drv_priv = stream;
q->ops = &flexbus_cif_vb2_ops;
q->mem_ops = &vb2_cma_sg_memops;
q->buf_struct_size = sizeof(struct flexbus_cif_buffer);
q->min_buffers_needed = CIF_REQ_BUFS_MIN;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &stream->vnode.vlock;
q->dev = stream->cif_dev->dev;
q->allow_cache_hints = 1;
q->bidirectional = 1;
q->gfp_flags = GFP_DMA32;
q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
return vb2_queue_init(q);
}
static int flexbus_cif_set_fmt(struct flexbus_cif_stream *stream,
struct v4l2_pix_format_mplane *pixm,
bool try)
{
struct flexbus_cif_device *dev = stream->cif_dev;
const struct cif_output_fmt *fmt;
const struct cif_input_fmt *cif_fmt_in = NULL;
struct v4l2_rect input_rect;
unsigned int imagesize = 0, planes;
u32 xsubs = 1, ysubs = 1, i;
int ret;
for (i = 0; i < FLEXBUS_CIF_MAX_PLANE; i++)
memset(&pixm->plane_fmt[i], 0, sizeof(struct v4l2_plane_pix_format));
fmt = flexbus_cif_find_output_fmt(stream, pixm->pixelformat);
if (!fmt)
fmt = &out_fmts[0];
input_rect.width = FLEXBUS_CIF_DEFAULT_WIDTH;
input_rect.height = FLEXBUS_CIF_DEFAULT_HEIGHT;
if (dev->terminal_sensor.sd) {
cif_fmt_in = get_input_fmt(dev->terminal_sensor.sd,
&input_rect, stream->id);
stream->cif_fmt_in = cif_fmt_in;
} else {
v4l2_err(&stream->cif_dev->v4l2_dev,
"terminal subdev does not exist\n");
return -EINVAL;
}
ret = flexbus_cif_output_fmt_check(stream, fmt);
if (ret)
return -EINVAL;
dev->terminal_sensor.raw_rect = input_rect;
/* CIF has not scale function,
* the size should not be larger than input
*/
pixm->width = clamp_t(u32, pixm->width,
CIF_MIN_WIDTH, input_rect.width);
pixm->height = clamp_t(u32, pixm->height,
CIF_MIN_HEIGHT, input_rect.height);
pixm->num_planes = fmt->mplanes;
pixm->field = V4L2_FIELD_NONE;
pixm->quantization = V4L2_QUANTIZATION_DEFAULT;
flexbus_cif_sync_crop_info(stream);
/* calculate plane size and image size */
fcc_xysubs(fmt->fourcc, &xsubs, &ysubs);
planes = fmt->cplanes ? fmt->cplanes : fmt->mplanes;
for (i = 0; i < planes; i++) {
struct v4l2_plane_pix_format *plane_fmt;
int width, height, bpl, size, bpp;
if (i == 0) {
if (stream->crop_enable) {
width = stream->crop[CROP_SRC_ACT].width;
height = stream->crop[CROP_SRC_ACT].height;
} else {
width = pixm->width;
height = pixm->height;
}
} else {
if (stream->crop_enable) {
width = stream->crop[CROP_SRC_ACT].width / xsubs;
height = stream->crop[CROP_SRC_ACT].height / ysubs;
} else {
width = pixm->width / xsubs;
height = pixm->height / ysubs;
}
}
bpp = flexbus_cif_align_bits_per_pixel(stream, fmt, i);
bpl = width * bpp / CIF_YUV_STORED_BIT_WIDTH;
size = bpl * height;
imagesize += size;
if (fmt->mplanes > i) {
/* Set bpl and size for each mplane */
plane_fmt = pixm->plane_fmt + i;
plane_fmt->bytesperline = bpl;
plane_fmt->sizeimage = size;
}
v4l2_dbg(1, flexbus_cif_debug, &stream->cif_dev->v4l2_dev,
"C-Plane %i size: %d, Total imagesize: %d\n",
i, size, imagesize);
}
/* convert to non-MPLANE format.
* It's important since we want to unify non-MPLANE
* and MPLANE.
*/
if (fmt->mplanes == 1)
pixm->plane_fmt[0].sizeimage = imagesize;
if (!try) {
stream->cif_fmt_out = fmt;
stream->pixm = *pixm;
v4l2_dbg(1, flexbus_cif_debug, &stream->cif_dev->v4l2_dev,
"%s: req(%d, %d) out(%d, %d)\n", __func__,
pixm->width, pixm->height,
stream->pixm.width, stream->pixm.height);
}
return 0;
}
void flexbus_cif_stream_init(struct flexbus_cif_device *dev, u32 id)
{
struct flexbus_cif_stream *stream = &dev->stream[id];
struct v4l2_pix_format_mplane pixm;
int i;
memset(stream, 0, sizeof(*stream));
memset(&pixm, 0, sizeof(pixm));
stream->id = id;
stream->cif_dev = dev;
INIT_LIST_HEAD(&stream->buf_head);
spin_lock_init(&stream->vbq_lock);
spin_lock_init(&stream->fps_lock);
stream->state = FLEXBUS_CIF_STATE_READY;
init_waitqueue_head(&stream->wq_stopped);
/* Set default format */
pixm.pixelformat = V4L2_PIX_FMT_NV12;
pixm.width = FLEXBUS_CIF_DEFAULT_WIDTH;
pixm.height = FLEXBUS_CIF_DEFAULT_HEIGHT;
flexbus_cif_set_fmt(stream, &pixm, false);
for (i = 0; i < CROP_SRC_MAX; i++) {
stream->crop[i].left = 0;
stream->crop[i].top = 0;
stream->crop[i].width = FLEXBUS_CIF_DEFAULT_WIDTH;
stream->crop[i].height = FLEXBUS_CIF_DEFAULT_HEIGHT;
}
stream->crop_enable = false;
stream->crop_dyn_en = false;
stream->crop_mask = 0x0;
stream->is_compact = false;
atomic_set(&stream->buf_cnt, 0);
}
static int flexbus_cif_fh_open(struct file *file)
{
struct video_device *vdev = video_devdata(file);
struct flexbus_cif_vdev_node *vnode = vdev_to_node(vdev);
struct flexbus_cif_stream *stream = to_flexbus_cif_stream(vnode);
struct flexbus_cif_device *cif_dev = stream->cif_dev;
int ret;
/* Make sure active sensor is valid before .set_fmt() */
ret = flexbus_cif_update_sensor_info(stream);
if (ret < 0) {
v4l2_err(vdev,
"update sensor info failed %d\n",
ret);
return ret;
}
ret = pm_runtime_resume_and_get(cif_dev->dev);
if (ret < 0) {
v4l2_err(vdev, "Failed to get runtime pm, %d\n",
ret);
return ret;
}
ret = v4l2_fh_open(file);
if (!ret) {
mutex_lock(&cif_dev->stream_lock);
ret = v4l2_pipeline_pm_get(&vnode->vdev.entity);
v4l2_info(vdev, "open video, entity use_count %d\n",
vnode->vdev.entity.use_count);
mutex_unlock(&cif_dev->stream_lock);
if (ret < 0)
vb2_fop_release(file);
}
return ret;
}
static int flexbus_cif_fh_release(struct file *file)
{
struct video_device *vdev = video_devdata(file);
struct flexbus_cif_vdev_node *vnode = vdev_to_node(vdev);
struct flexbus_cif_stream *stream = to_flexbus_cif_stream(vnode);
struct flexbus_cif_device *cif_dev = stream->cif_dev;
int ret = 0;
ret = vb2_fop_release(file);
if (!ret) {
mutex_lock(&cif_dev->stream_lock);
v4l2_pipeline_pm_put(&vnode->vdev.entity);
v4l2_info(vdev, "close video, entity use_count %d\n",
vnode->vdev.entity.use_count);
mutex_unlock(&cif_dev->stream_lock);
}
pm_runtime_put_sync(cif_dev->dev);
return ret;
}
static const struct v4l2_file_operations flexbus_cif_fops = {
.open = flexbus_cif_fh_open,
.release = flexbus_cif_fh_release,
.unlocked_ioctl = video_ioctl2,
.poll = vb2_fop_poll,
.mmap = vb2_fop_mmap,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = video_ioctl2,
#endif
};
static int flexbus_cif_enum_input(struct file *file, void *priv,
struct v4l2_input *input)
{
if (input->index > 0)
return -EINVAL;
input->type = V4L2_INPUT_TYPE_CAMERA;
strscpy(input->name, "Camera", sizeof(input->name));
return 0;
}
static int flexbus_cif_try_fmt_vid_cap_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct flexbus_cif_stream *stream = video_drvdata(file);
int ret = 0;
ret = flexbus_cif_set_fmt(stream, &f->fmt.pix_mp, true);
return ret;
}
static int flexbus_cif_enum_framesizes(struct file *file, void *prov,
struct v4l2_frmsizeenum *fsize)
{
struct v4l2_frmsize_stepwise *s = &fsize->stepwise;
struct flexbus_cif_stream *stream = video_drvdata(file);
struct flexbus_cif_device *dev = stream->cif_dev;
struct v4l2_rect input_rect;
if (fsize->index != 0)
return -EINVAL;
if (!flexbus_cif_find_output_fmt(stream, fsize->pixel_format))
return -EINVAL;
input_rect.width = FLEXBUS_CIF_DEFAULT_WIDTH;
input_rect.height = FLEXBUS_CIF_DEFAULT_HEIGHT;
if (dev->terminal_sensor.sd)
get_input_fmt(dev->terminal_sensor.sd,
&input_rect, stream->id);
fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
s->min_width = CIF_MIN_WIDTH;
s->min_height = CIF_MIN_HEIGHT;
s->max_width = input_rect.width;
s->max_height = input_rect.height;
s->step_width = OUTPUT_STEP_WISE;
s->step_height = OUTPUT_STEP_WISE;
return 0;
}
static int flexbus_cif_enum_frameintervals(struct file *file, void *fh,
struct v4l2_frmivalenum *fival)
{
struct flexbus_cif_stream *stream = video_drvdata(file);
struct flexbus_cif_device *dev = stream->cif_dev;
struct flexbus_cif_sensor_info *sensor = dev->active_sensor;
struct v4l2_subdev_frame_interval fi;
int ret;
if (fival->index != 0)
return -EINVAL;
if (!sensor || !sensor->sd) {
/* TODO: active_sensor is NULL if using DMARX path */
v4l2_err(&dev->v4l2_dev, "%s Not active sensor\n", __func__);
return -ENODEV;
}
ret = v4l2_subdev_call(sensor->sd, video, g_frame_interval, &fi);
if (ret && ret != -ENOIOCTLCMD) {
return ret;
} else if (ret == -ENOIOCTLCMD) {
/* Set a default value for sensors not implements ioctl */
fi.interval.numerator = 1;
fi.interval.denominator = 30;
}
fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
fival->stepwise.step.numerator = 1;
fival->stepwise.step.denominator = 1;
fival->stepwise.max.numerator = 1;
fival->stepwise.max.denominator = 1;
fival->stepwise.min.numerator = fi.interval.numerator;
fival->stepwise.min.denominator = fi.interval.denominator;
return 0;
}
static int flexbus_cif_enum_fmt_vid_cap_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
const struct cif_output_fmt *fmt = NULL;
struct flexbus_cif_stream *stream = video_drvdata(file);
struct flexbus_cif_device *dev = stream->cif_dev;
const struct cif_input_fmt *cif_fmt_in = NULL;
struct v4l2_rect input_rect;
int i = 0;
int ret = 0;
int fource_idx = 0;
if (f->index >= ARRAY_SIZE(out_fmts))
return -EINVAL;
if (dev->terminal_sensor.sd) {
cif_fmt_in = get_input_fmt(dev->terminal_sensor.sd,
&input_rect, stream->id);
stream->cif_fmt_in = cif_fmt_in;
} else {
v4l2_err(&stream->cif_dev->v4l2_dev,
"terminal subdev does not exist\n");
return -EINVAL;
}
if (f->index != 0)
fource_idx = stream->new_fource_idx;
for (i = fource_idx; i < ARRAY_SIZE(out_fmts); i++) {
fmt = &out_fmts[i];
ret = flexbus_cif_output_fmt_check(stream, fmt);
if (!ret) {
f->pixelformat = fmt->fourcc;
stream->new_fource_idx = i + 1;
break;
}
}
if (i == ARRAY_SIZE(out_fmts))
return -EINVAL;
switch (f->pixelformat) {
case V4l2_PIX_FMT_EBD8:
strscpy(f->description,
"Embedded data 8-bit",
sizeof(f->description));
break;
case V4l2_PIX_FMT_SPD16:
strscpy(f->description,
"Shield pix data 16-bit",
sizeof(f->description));
break;
default:
break;
}
return 0;
}
static int flexbus_cif_s_fmt_vid_cap_mplane(struct file *file,
void *priv, struct v4l2_format *f)
{
struct flexbus_cif_stream *stream = video_drvdata(file);
struct flexbus_cif_device *dev = stream->cif_dev;
int ret = 0;
if (vb2_is_busy(&stream->vnode.buf_queue)) {
v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
return -EBUSY;
}
ret = flexbus_cif_set_fmt(stream, &f->fmt.pix_mp, false);
return ret;
}
static int flexbus_cif_g_fmt_vid_cap_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct flexbus_cif_stream *stream = video_drvdata(file);
f->fmt.pix_mp = stream->pixm;
return 0;
}
static int flexbus_cif_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct flexbus_cif_stream *stream = video_drvdata(file);
struct device *dev = stream->cif_dev->dev;
strscpy(cap->driver, dev->driver->name, sizeof(cap->driver));
strscpy(cap->card, dev->driver->name, sizeof(cap->card));
snprintf(cap->bus_info, sizeof(cap->bus_info),
"platform:%s", dev_name(dev));
return 0;
}
static __maybe_unused int flexbus_cif_cropcap(struct file *file, void *fh,
struct v4l2_cropcap *cap)
{
struct flexbus_cif_stream *stream = video_drvdata(file);
struct flexbus_cif_device *dev = stream->cif_dev;
struct v4l2_rect *raw_rect = &dev->terminal_sensor.raw_rect;
int ret = 0;
if (stream->crop_mask & CROP_SRC_SENSOR) {
cap->bounds.left = stream->crop[CROP_SRC_SENSOR].left;
cap->bounds.top = stream->crop[CROP_SRC_SENSOR].top;
cap->bounds.width = stream->crop[CROP_SRC_SENSOR].width;
cap->bounds.height = stream->crop[CROP_SRC_SENSOR].height;
} else {
cap->bounds.left = raw_rect->left;
cap->bounds.top = raw_rect->top;
cap->bounds.width = raw_rect->width;
cap->bounds.height = raw_rect->height;
}
cap->defrect = cap->bounds;
cap->pixelaspect.numerator = 1;
cap->pixelaspect.denominator = 1;
return ret;
}
static int flexbus_cif_s_selection(struct file *file, void *fh,
struct v4l2_selection *s)
{
struct flexbus_cif_stream *stream = video_drvdata(file);
struct flexbus_cif_device *dev = stream->cif_dev;
struct v4l2_subdev *sensor_sd;
struct v4l2_subdev_selection sd_sel;
const struct v4l2_rect *rect = &s->r;
struct v4l2_rect sensor_crop;
struct v4l2_rect *raw_rect = &dev->terminal_sensor.raw_rect;
u16 pad = 0;
int ret = 0;
if (!s) {
v4l2_dbg(1, flexbus_cif_debug, &dev->v4l2_dev, "sel is null\n");
goto err;
}
if (s->target == V4L2_SEL_TGT_CROP_BOUNDS) {
sensor_sd = get_remote_sensor(stream, &pad);
sd_sel.r = s->r;
sd_sel.pad = pad;
sd_sel.target = s->target;
sd_sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(sensor_sd, pad, set_selection, NULL, &sd_sel);
if (!ret) {
s->r = sd_sel.r;
v4l2_dbg(1, flexbus_cif_debug, &dev->v4l2_dev, "%s: pad:%d, which:%d, target:%d\n",
__func__, pad, sd_sel.which, sd_sel.target);
}
} else if (s->target == V4L2_SEL_TGT_CROP) {
ret = flexbus_cif_sanity_check_fmt(stream, rect);
if (ret) {
v4l2_err(&dev->v4l2_dev, "set crop failed\n");
return ret;
}
if (stream->crop_mask & CROP_SRC_SENSOR) {
sensor_crop = stream->crop[CROP_SRC_SENSOR];
if (rect->left + rect->width > sensor_crop.width ||
rect->top + rect->height > sensor_crop.height) {
v4l2_err(&dev->v4l2_dev,
"crop size is bigger than sensor input:left:%d, top:%d, width:%d, height:%d\n",
sensor_crop.left, sensor_crop.top,
sensor_crop.width, sensor_crop.height);
return -EINVAL;
}
} else {
if (rect->left + rect->width > raw_rect->width ||
rect->top + rect->height > raw_rect->height) {
v4l2_err(&dev->v4l2_dev,
"crop size is bigger than sensor raw input:left:%d, top:%d, width:%d, height:%d\n",
raw_rect->left, raw_rect->top,
raw_rect->width, raw_rect->height);
return -EINVAL;
}
}
stream->crop[CROP_SRC_USR] = *rect;
stream->crop_enable = true;
stream->crop_mask |= CROP_SRC_USR_MASK;
stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_USR];
if (stream->crop_mask & CROP_SRC_SENSOR) {
sensor_crop = stream->crop[CROP_SRC_SENSOR];
stream->crop[CROP_SRC_ACT].left = sensor_crop.left + stream->crop[CROP_SRC_USR].left;
stream->crop[CROP_SRC_ACT].top = sensor_crop.top + stream->crop[CROP_SRC_USR].top;
}
if (stream->state == FLEXBUS_CIF_STATE_STREAMING) {
stream->crop_dyn_en = true;
v4l2_info(&dev->v4l2_dev, "enable dynamic crop, S_SELECTION(%ux%u@%u:%u) target: %d\n",
rect->width, rect->height, rect->left, rect->top, s->target);
} else {
v4l2_info(&dev->v4l2_dev, "static crop, S_SELECTION(%ux%u@%u:%u) target: %d\n",
rect->width, rect->height, rect->left, rect->top, s->target);
}
} else {
goto err;
}
return ret;
err:
return -EINVAL;
}
static int flexbus_cif_g_selection(struct file *file, void *fh,
struct v4l2_selection *s)
{
struct flexbus_cif_stream *stream = video_drvdata(file);
struct flexbus_cif_device *dev = stream->cif_dev;
struct v4l2_subdev *sensor_sd;
struct v4l2_subdev_selection sd_sel;
u16 pad = 0;
int ret = 0;
if (!s) {
v4l2_dbg(1, flexbus_cif_debug, &dev->v4l2_dev, "sel is null\n");
goto err;
}
if (s->target == V4L2_SEL_TGT_CROP_BOUNDS) {
sensor_sd = get_remote_sensor(stream, &pad);
sd_sel.pad = pad;
sd_sel.target = s->target;
sd_sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
v4l2_dbg(1, flexbus_cif_debug, &dev->v4l2_dev, "%s(line:%d): sd:%s pad:%d, which:%d, target:%d\n",
__func__, __LINE__, sensor_sd->name, pad, sd_sel.which, sd_sel.target);
ret = v4l2_subdev_call(sensor_sd, pad, get_selection, NULL, &sd_sel);
if (!ret) {
s->r = sd_sel.r;
} else {
s->r.left = 0;
s->r.top = 0;
s->r.width = stream->pixm.width;
s->r.height = stream->pixm.height;
}
} else if (s->target == V4L2_SEL_TGT_CROP) {
if (stream->crop_mask & (CROP_SRC_USR_MASK | CROP_SRC_SENSOR_MASK)) {
s->r = stream->crop[CROP_SRC_ACT];
} else {
s->r.left = 0;
s->r.top = 0;
s->r.width = stream->pixm.width;
s->r.height = stream->pixm.height;
}
} else {
goto err;
}
return ret;
err:
return -EINVAL;
}
static long flexbus_cif_ioctl_default(struct file *file, void *fh,
bool valid_prio, unsigned int cmd, void *arg)
{
return -EINVAL;
}
static const struct v4l2_ioctl_ops flexbus_cif_v4l2_ioctl_ops = {
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_expbuf = vb2_ioctl_expbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_enum_input = flexbus_cif_enum_input,
.vidioc_try_fmt_vid_cap_mplane = flexbus_cif_try_fmt_vid_cap_mplane,
.vidioc_enum_fmt_vid_cap = flexbus_cif_enum_fmt_vid_cap_mplane,
.vidioc_s_fmt_vid_cap_mplane = flexbus_cif_s_fmt_vid_cap_mplane,
.vidioc_g_fmt_vid_cap_mplane = flexbus_cif_g_fmt_vid_cap_mplane,
.vidioc_querycap = flexbus_cif_querycap,
.vidioc_s_selection = flexbus_cif_s_selection,
.vidioc_g_selection = flexbus_cif_g_selection,
.vidioc_enum_frameintervals = flexbus_cif_enum_frameintervals,
.vidioc_enum_framesizes = flexbus_cif_enum_framesizes,
.vidioc_default = flexbus_cif_ioctl_default,
};
static void flexbus_cif_vb_done_oneframe(struct flexbus_cif_stream *stream,
struct vb2_v4l2_buffer *vb_done)
{
const struct cif_output_fmt *fmt = stream->cif_fmt_out;
u32 i;
/* Dequeue a filled buffer */
for (i = 0; i < fmt->mplanes; i++) {
vb2_set_plane_payload(&vb_done->vb2_buf, i,
stream->pixm.plane_fmt[i].sizeimage);
}
vb2_buffer_done(&vb_done->vb2_buf, VB2_BUF_STATE_DONE);
v4l2_dbg(2, flexbus_cif_debug, &stream->cif_dev->v4l2_dev,
"stream[%d] vb done, index: %d, sequence %d\n", stream->id,
vb_done->vb2_buf.index, vb_done->sequence);
atomic_dec(&stream->buf_cnt);
}
static void flexbus_cif_tasklet_handle(unsigned long data)
{
struct flexbus_cif_stream *stream = (struct flexbus_cif_stream *)data;
struct flexbus_cif_buffer *buf = NULL;
unsigned long flags = 0;
LIST_HEAD(local_list);
spin_lock_irqsave(&stream->vbq_lock, flags);
list_replace_init(&stream->vb_done_list, &local_list);
spin_unlock_irqrestore(&stream->vbq_lock, flags);
while (!list_empty(&local_list)) {
buf = list_first_entry(&local_list,
struct flexbus_cif_buffer, queue);
list_del(&buf->queue);
flexbus_cif_vb_done_oneframe(stream, &buf->vb);
}
}
static void flexbus_cif_vb_done_tasklet(struct flexbus_cif_stream *stream,
struct flexbus_cif_buffer *buf)
{
unsigned long flags = 0;
if (!stream || !buf)
return;
spin_lock_irqsave(&stream->vbq_lock, flags);
list_add_tail(&buf->queue, &stream->vb_done_list);
spin_unlock_irqrestore(&stream->vbq_lock, flags);
tasklet_schedule(&stream->vb_done_tasklet);
}
static void flexbus_cif_unregister_stream_vdev(struct flexbus_cif_stream *stream)
{
tasklet_kill(&stream->vb_done_tasklet);
media_entity_cleanup(&stream->vnode.vdev.entity);
video_unregister_device(&stream->vnode.vdev);
}
static int flexbus_cif_register_stream_vdev(struct flexbus_cif_stream *stream,
bool is_multi_input)
{
struct flexbus_cif_device *dev = stream->cif_dev;
struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
struct video_device *vdev = &stream->vnode.vdev;
struct flexbus_cif_vdev_node *node;
int ret = 0;
char *vdev_name;
vdev_name = CIF_CIF_VDEV_NAME;
strscpy(vdev->name, vdev_name, sizeof(vdev->name));
node = vdev_to_node(vdev);
mutex_init(&node->vlock);
vdev->ioctl_ops = &flexbus_cif_v4l2_ioctl_ops;
vdev->release = video_device_release_empty;
vdev->fops = &flexbus_cif_fops;
vdev->minor = -1;
vdev->v4l2_dev = v4l2_dev;
vdev->lock = &node->vlock;
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
V4L2_CAP_STREAMING;
video_set_drvdata(vdev, stream);
vdev->vfl_dir = VFL_DIR_RX;
node->pad.flags = MEDIA_PAD_FL_SINK;
flexbus_cif_init_vb2_queue(&node->buf_queue, stream,
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
vdev->queue = &node->buf_queue;
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
v4l2_err(v4l2_dev,
"video_register_device failed with error %d\n", ret);
return ret;
}
ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
if (ret < 0)
goto unreg;
INIT_LIST_HEAD(&stream->vb_done_list);
tasklet_init(&stream->vb_done_tasklet,
flexbus_cif_tasklet_handle,
(unsigned long)stream);
tasklet_disable(&stream->vb_done_tasklet);
return 0;
unreg:
video_unregister_device(vdev);
return ret;
}
void flexbus_cif_unregister_stream_vdevs(struct flexbus_cif_device *dev,
int stream_num)
{
struct flexbus_cif_stream *stream;
int i;
for (i = 0; i < stream_num; i++) {
stream = &dev->stream[i];
flexbus_cif_unregister_stream_vdev(stream);
}
}
int flexbus_cif_register_stream_vdevs(struct flexbus_cif_device *dev,
int stream_num,
bool is_multi_input)
{
struct flexbus_cif_stream *stream;
int i, j, ret;
for (i = 0; i < stream_num; i++) {
stream = &dev->stream[i];
stream->cif_dev = dev;
ret = flexbus_cif_register_stream_vdev(stream, is_multi_input);
if (ret < 0)
goto err;
}
return 0;
err:
for (j = 0; j < i; j++) {
stream = &dev->stream[j];
flexbus_cif_unregister_stream_vdev(stream);
}
return ret;
}
static int flexbus_cif_sof_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
if (sub->type == V4L2_EVENT_FRAME_SYNC)
return v4l2_event_subscribe(fh, sub, 4, NULL);
else
return -EINVAL;
}
static void flexbus_cif_cif_event_inc_sof(struct flexbus_cif_device *dev)
{
struct flexbus_cif_cif_sof_subdev *subdev = &dev->cif_sof_subdev;
if (subdev) {
struct v4l2_event event = {
.type = V4L2_EVENT_FRAME_SYNC,
.u.frame_sync.frame_sequence =
atomic_inc_return(&subdev->frm_sync_seq) - 1,
};
v4l2_event_queue(subdev->sd.devnode, &event);
}
}
static const struct v4l2_subdev_core_ops flexbus_cif_cif_sof_sd_core_ops = {
.subscribe_event = flexbus_cif_sof_subscribe_event,
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
};
static const struct v4l2_subdev_ops flexbus_cif_cif_sof_sd_ops = {
.core = &flexbus_cif_cif_sof_sd_core_ops,
};
int flexbus_cif_register_cif_sof_subdev(struct flexbus_cif_device *dev)
{
struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
struct flexbus_cif_cif_sof_subdev *subdev = &dev->cif_sof_subdev;
struct v4l2_subdev *sd;
int ret;
memset(subdev, 0, sizeof(*subdev));
subdev->cif_dev = dev;
sd = &subdev->sd;
v4l2_subdev_init(sd, &flexbus_cif_cif_sof_sd_ops);
sd->owner = THIS_MODULE;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
snprintf(sd->name, sizeof(sd->name), "flexbus_cif-cif-sof");
v4l2_set_subdevdata(sd, subdev);
ret = v4l2_device_register_subdev(v4l2_dev, sd);
if (ret < 0)
goto end;
ret = v4l2_device_register_subdev_nodes(v4l2_dev);
if (ret < 0)
goto free_subdev;
return ret;
free_subdev:
v4l2_device_unregister_subdev(sd);
end:
v4l2_err(sd, "Failed to register subdev, ret:%d\n", ret);
return ret;
}
void flexbus_cif_unregister_cif_sof_subdev(struct flexbus_cif_device *dev)
{
struct v4l2_subdev *sd = &dev->cif_sof_subdev.sd;
v4l2_device_unregister_subdev(sd);
}
static void flexbus_cif_dynamic_crop(struct flexbus_cif_stream *stream)
{
struct flexbus_cif_device *cif_dev = stream->cif_dev;
u32 raw_width, crop_width = 64,
crop_height = 64, crop_x = 0, crop_y = 0;
if (!cif_dev->active_sensor)
return;
raw_width = stream->crop[CROP_SRC_ACT].width;
crop_width = raw_width;
crop_height = stream->crop[CROP_SRC_ACT].height;
crop_x = stream->crop[CROP_SRC_ACT].left;
crop_y = stream->crop[CROP_SRC_ACT].top;
flexbus_cif_write_register(cif_dev, FLEXBUS_DVP_CROP_START,
crop_y << CIF_CROP_Y_SHIFT | crop_x);
flexbus_cif_write_register(cif_dev, FLEXBUS_DVP_CROP_SIZE,
crop_height << CIF_CROP_Y_SHIFT | crop_width);
stream->crop_dyn_en = false;
}
static void flexbus_cif_buf_done_prepare(struct flexbus_cif_stream *stream,
struct flexbus_cif_buffer *active_buf,
int mipi_id,
u32 mode)
{
struct vb2_v4l2_buffer *vb_done = NULL;
if (active_buf) {
vb_done = &active_buf->vb;
vb_done->vb2_buf.timestamp = ktime_get_ns();
vb_done->sequence = stream->frame_idx - 1;
active_buf->fe_timestamp = ktime_get_ns();
}
flexbus_cif_vb_done_tasklet(stream, active_buf);
}
static void flexbus_cif_deal_readout_time(struct flexbus_cif_stream *stream)
{
unsigned long flags;
spin_lock_irqsave(&stream->fps_lock, flags);
stream->readout.fe_timestamp = ktime_get_ns();
spin_unlock_irqrestore(&stream->fps_lock, flags);
}
static void flexbus_cif_update_stream(struct flexbus_cif_device *cif_dev,
struct flexbus_cif_stream *stream,
int mipi_id)
{
struct flexbus_cif_buffer *active_buf = NULL;
unsigned long flags;
int ret = 0;
if (stream->frame_phase == (CIF_CSI_FRAME0_READY | CIF_CSI_FRAME1_READY)) {
cif_dev->err_state |= (FLEXBUS_CIF_ERR_ID0_TRIG_SIMULT << stream->id);
cif_dev->irq_stats.trig_simult_cnt[stream->id]++;
return;
}
spin_lock_irqsave(&stream->fps_lock, flags);
if (stream->frame_phase & CIF_CSI_FRAME0_READY) {
if (stream->curr_buf)
active_buf = stream->curr_buf;
stream->fps_stats.frm0_timestamp = ktime_get_ns();
} else if (stream->frame_phase & CIF_CSI_FRAME1_READY) {
if (stream->next_buf)
active_buf = stream->next_buf;
stream->fps_stats.frm1_timestamp = ktime_get_ns();
}
spin_unlock_irqrestore(&stream->fps_lock, flags);
flexbus_cif_deal_readout_time(stream);
ret = flexbus_cif_assign_new_buffer_pingpong(stream,
FLEXBUS_CIF_YUV_ADDR_STATE_UPDATE,
mipi_id);
if (ret)
return;
flexbus_cif_buf_done_prepare(stream, active_buf, mipi_id, 0);
}
static u32 flexbus_cif_mbus_pixelcode_to_v4l2(u32 pixelcode)
{
u32 pixelformat;
switch (pixelcode) {
case MEDIA_BUS_FMT_Y8_1X8:
pixelformat = V4L2_PIX_FMT_GREY;
break;
case MEDIA_BUS_FMT_SBGGR8_1X8:
pixelformat = V4L2_PIX_FMT_SBGGR8;
break;
case MEDIA_BUS_FMT_SGBRG8_1X8:
pixelformat = V4L2_PIX_FMT_SGBRG8;
break;
case MEDIA_BUS_FMT_SGRBG8_1X8:
pixelformat = V4L2_PIX_FMT_SGRBG8;
break;
case MEDIA_BUS_FMT_SRGGB8_1X8:
pixelformat = V4L2_PIX_FMT_SRGGB8;
break;
case MEDIA_BUS_FMT_Y10_1X10:
pixelformat = V4L2_PIX_FMT_Y10;
break;
case MEDIA_BUS_FMT_SBGGR10_1X10:
pixelformat = V4L2_PIX_FMT_SBGGR10;
break;
case MEDIA_BUS_FMT_SGBRG10_1X10:
pixelformat = V4L2_PIX_FMT_SGBRG10;
break;
case MEDIA_BUS_FMT_SGRBG10_1X10:
pixelformat = V4L2_PIX_FMT_SGRBG10;
break;
case MEDIA_BUS_FMT_SRGGB10_1X10:
pixelformat = V4L2_PIX_FMT_SRGGB10;
break;
case MEDIA_BUS_FMT_Y12_1X12:
pixelformat = V4L2_PIX_FMT_Y12;
break;
case MEDIA_BUS_FMT_SBGGR12_1X12:
pixelformat = V4L2_PIX_FMT_SBGGR12;
break;
case MEDIA_BUS_FMT_SGBRG12_1X12:
pixelformat = V4L2_PIX_FMT_SGBRG12;
break;
case MEDIA_BUS_FMT_SGRBG12_1X12:
pixelformat = V4L2_PIX_FMT_SGRBG12;
break;
case MEDIA_BUS_FMT_SRGGB12_1X12:
pixelformat = V4L2_PIX_FMT_SRGGB12;
break;
case MEDIA_BUS_FMT_SPD_2X8:
pixelformat = V4l2_PIX_FMT_SPD16;
break;
case MEDIA_BUS_FMT_EBD_1X8:
pixelformat = V4l2_PIX_FMT_EBD8;
break;
default:
pixelformat = V4L2_PIX_FMT_SRGGB10;
}
return pixelformat;
}
void flexbus_cif_set_default_fmt(struct flexbus_cif_device *cif_dev)
{
struct v4l2_subdev_selection input_sel;
struct v4l2_pix_format_mplane pixm;
struct v4l2_subdev_format fmt;
int stream_num = 0;
int ret, i;
stream_num = 1;
if (!cif_dev->terminal_sensor.sd)
flexbus_cif_update_sensor_info(&cif_dev->stream[0]);
if (cif_dev->terminal_sensor.sd) {
for (i = 0; i < stream_num; i++) {
memset(&fmt, 0, sizeof(fmt));
fmt.pad = i;
fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
v4l2_subdev_call(cif_dev->terminal_sensor.sd, pad, get_fmt, NULL, &fmt);
memset(&pixm, 0, sizeof(pixm));
pixm.pixelformat = flexbus_cif_mbus_pixelcode_to_v4l2(fmt.format.code);
pixm.width = fmt.format.width;
pixm.height = fmt.format.height;
memset(&input_sel, 0, sizeof(input_sel));
input_sel.pad = i;
input_sel.target = V4L2_SEL_TGT_CROP_BOUNDS;
input_sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(cif_dev->terminal_sensor.sd,
pad, get_selection, NULL,
&input_sel);
if (!ret) {
pixm.width = input_sel.r.width;
pixm.height = input_sel.r.height;
}
flexbus_cif_set_fmt(&cif_dev->stream[i], &pixm, false);
}
}
}
static void flexbus_cif_send_sof(struct flexbus_cif_device *cif_dev)
{
flexbus_cif_cif_event_inc_sof(cif_dev);
}
static void flexbus_cif_deal_sof(struct flexbus_cif_device *cif_dev)
{
struct flexbus_cif_stream *detect_stream = &cif_dev->stream[0];
unsigned long flags;
spin_lock_irqsave(&detect_stream->fps_lock, flags);
detect_stream->readout.fs_timestamp = ktime_get_ns();
spin_unlock_irqrestore(&detect_stream->fps_lock, flags);
flexbus_cif_send_sof(cif_dev);
detect_stream->frame_idx++;
v4l2_dbg(3, flexbus_cif_debug, &cif_dev->v4l2_dev,
"stream[%d] sof %d %lld\n",
detect_stream->id,
detect_stream->frame_idx - 1,
ktime_get_ns());
}
void flexbus_cif_err_print_work(struct work_struct *work)
{
struct flexbus_cif_err_state_work *err_state_work = container_of(work,
struct flexbus_cif_err_state_work,
work);
struct flexbus_cif_device *dev = container_of(err_state_work,
struct flexbus_cif_device,
err_state_work);
u32 err_state = 0;
u64 cur_time = 0;
bool is_print = false;
cur_time = ktime_get_ns();
if (err_state_work->last_timestamp == 0) {
is_print = true;
} else {
if (cur_time - err_state_work->last_timestamp > 500000000)
is_print = true;
}
err_state_work->last_timestamp = cur_time;
err_state = err_state_work->err_state;
if (err_state & FLEXBUS_CIF_ERR_ID0_NOT_BUF && is_print)
v4l2_err(&dev->v4l2_dev,
"stream[0] not active buffer, frame num %d, cnt %llu\n",
dev->stream[0].frame_idx, dev->irq_stats.not_active_buf_cnt[0]);
if (err_state & FLEXBUS_CIF_ERR_ID1_NOT_BUF && is_print)
v4l2_err(&dev->v4l2_dev,
"stream[1] not active buffer, frame num %d, cnt %llu\n",
dev->stream[1].frame_idx, dev->irq_stats.not_active_buf_cnt[1]);
if (err_state & FLEXBUS_CIF_ERR_ID2_NOT_BUF && is_print)
v4l2_err(&dev->v4l2_dev,
"stream[2] not active buffer, frame num %d, cnt %llu\n",
dev->stream[2].frame_idx, dev->irq_stats.not_active_buf_cnt[2]);
if (err_state & FLEXBUS_CIF_ERR_ID3_NOT_BUF && is_print)
v4l2_err(&dev->v4l2_dev,
"stream[3] not active buffer, frame num %d, cnt %llu\n",
dev->stream[3].frame_idx, dev->irq_stats.not_active_buf_cnt[3]);
if (err_state & FLEXBUS_CIF_ERR_SIZE)
v4l2_err(&dev->v4l2_dev,
"ERROR: size err, cnt %llu\n",
dev->irq_stats.cif_size_err_cnt);
if (err_state & FLEXBUS_CIF_ERR_OVERFLOW)
v4l2_err(&dev->v4l2_dev,
"ERROR: fifo overflow, cnt %llu, dnum %d\n",
dev->irq_stats.cif_overflow_cnt, err_state_work->fifo_dnum);
if (err_state & FLEXBUS_CIF_ERR_BANDWIDTH_LACK)
v4l2_err(&dev->v4l2_dev,
"ERROR: bandwidth lack, cnt %llu\n",
dev->irq_stats.cif_bwidth_lack_cnt);
}
void flexbus_cif_irq_pingpong(struct flexbus_cif_device *cif_dev, u32 isr)
{
struct flexbus_cif_stream *stream;
if (!cif_dev->active_sensor)
return;
stream = &cif_dev->stream[FLEXBUS_CIF_STREAM_CIF];
if (isr & CIF_FIFO_OVERFLOW) {
cif_dev->irq_stats.cif_overflow_cnt++;
cif_dev->err_state |= FLEXBUS_CIF_ERR_OVERFLOW;
cif_dev->err_state_work.fifo_dnum = flexbus_cif_read_register(cif_dev, FLEXBUS_RXFIFO_DNUM);
}
if (isr & CIF_BANDWIDTH_LACK) {
cif_dev->irq_stats.cif_bwidth_lack_cnt++;
cif_dev->err_state |= FLEXBUS_CIF_ERR_BANDWIDTH_LACK;
}
if (isr & CIF_SIZE_ERR) {
cif_dev->irq_stats.cif_size_err_cnt++;
//RESET
stream->buf_wake_up_cnt = 0;
cif_dev->err_state |= FLEXBUS_CIF_ERR_SIZE;
}
if (isr & CIF_DMA_END) {
stream->frame_idx++;
stream->buf_wake_up_cnt++;
if (stream->stopping) {
flexbus_cif_stream_stop(stream);
stream->stopping = false;
wake_up(&stream->wq_stopped);
return;
}
if (stream->state != FLEXBUS_CIF_STATE_STREAMING)
return;
//need to change
stream->frame_phase = (isr >> FLEXBUS_IMR_DMA_DST0_SHIFT) & 0x3;
flexbus_cif_update_stream(cif_dev, stream, 0);
cif_dev->irq_stats.frm_end_cnt[stream->id]++;
if (stream->crop_dyn_en)
flexbus_cif_dynamic_crop(stream);
}
if (isr & CIF_DMA_START)
flexbus_cif_deal_sof(cif_dev);
}