[media] s5p-fimc: Add support for ISP Writeback data input bus type
A second sink pad is added to each FIMC.N subdev that will be used
to link it to the FIMC-IS-ISP subdev. Only V4L2_MBUS_FMT_YUV10_1X30
format is supported at this pad (FIMC_SD_PAD_SINK_FIFO).
The routine checking for mismatch in the image formats at sides of
the links is updated to account for the fact FIMC.X subdevs now have
sink pads at the pad indexes 0, 1 and source pad at pad index 2.
If link to FIMC.X pad 1 is activated we switch FIMC input data bus
type to the ISP Writeback. Only a single active link to FIMC.X pad 0
or 1 will be allowed at any time.
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c
index ff9ca63..1675d38 100644
--- a/drivers/media/platform/s5p-fimc/fimc-capture.c
+++ b/drivers/media/platform/s5p-fimc/fimc-capture.c
@@ -33,26 +33,27 @@
static int fimc_capture_hw_init(struct fimc_dev *fimc)
{
+ struct fimc_source_info *si = &fimc->vid_cap.source_config;
struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct fimc_pipeline *p = &fimc->pipeline;
- struct fimc_sensor_info *sensor;
+ int ret;
unsigned long flags;
- int ret = 0;
- if (p->subdevs[IDX_SENSOR] == NULL || ctx == NULL)
- return -ENXIO;
- if (ctx->s_frame.fmt == NULL)
+ if (ctx == NULL || ctx->s_frame.fmt == NULL)
return -EINVAL;
- sensor = v4l2_get_subdev_hostdata(p->subdevs[IDX_SENSOR]);
+ if (si->fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) {
+ ret = fimc_hw_camblk_cfg_writeback(fimc);
+ if (ret < 0)
+ return ret;
+ }
spin_lock_irqsave(&fimc->slock, flags);
fimc_prepare_dma_offset(ctx, &ctx->d_frame);
fimc_set_yuv_order(ctx);
- fimc_hw_set_camera_polarity(fimc, &sensor->pdata);
- fimc_hw_set_camera_type(fimc, &sensor->pdata);
- fimc_hw_set_camera_source(fimc, &sensor->pdata);
+ fimc_hw_set_camera_polarity(fimc, si);
+ fimc_hw_set_camera_type(fimc, si);
+ fimc_hw_set_camera_source(fimc, si);
fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
ret = fimc_set_scaler_info(ctx);
@@ -606,18 +607,22 @@
fimc_fmt_is_user_defined(ctx->s_frame.fmt->color))
*code = ctx->s_frame.fmt->mbus_code;
- if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad != FIMC_SD_PAD_SINK)
+ if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad == FIMC_SD_PAD_SOURCE)
mask |= FMT_FLAGS_M2M;
+ if (pad == FIMC_SD_PAD_SINK_FIFO)
+ mask = FMT_FLAGS_WRITEBACK;
+
ffmt = fimc_find_format(fourcc, code, mask, 0);
if (WARN_ON(!ffmt))
return NULL;
+
if (code)
*code = ffmt->mbus_code;
if (fourcc)
*fourcc = ffmt->fourcc;
- if (pad == FIMC_SD_PAD_SINK) {
+ if (pad != FIMC_SD_PAD_SOURCE) {
max_w = fimc_fmt_is_user_defined(ffmt->color) ?
pl->scaler_dis_w : pl->scaler_en_w;
/* Apply the camera input interface pixel constraints */
@@ -851,7 +856,7 @@
tfmt->width = mf->width;
tfmt->height = mf->height;
ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height,
- NULL, &fcc, FIMC_SD_PAD_SINK);
+ NULL, &fcc, FIMC_SD_PAD_SINK_CAM);
ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height,
NULL, &fcc, FIMC_SD_PAD_SOURCE);
if (ffmt && ffmt->mbus_code)
@@ -938,7 +943,7 @@
if (fimc_jpeg_fourcc(pix->pixelformat)) {
fimc_capture_try_format(ctx, &pix->width, &pix->height,
NULL, &pix->pixelformat,
- FIMC_SD_PAD_SINK);
+ FIMC_SD_PAD_SINK_CAM);
ctx->s_frame.f_width = pix->width;
ctx->s_frame.f_height = pix->height;
}
@@ -992,7 +997,7 @@
{
struct fimc_ctx *ctx = fimc->vid_cap.ctx;
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
- struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.mf;
+ struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.ci_fmt;
struct fimc_frame *ff = &ctx->d_frame;
struct fimc_fmt *s_fmt = NULL;
int ret, i;
@@ -1004,7 +1009,7 @@
if (fimc_jpeg_fourcc(pix->pixelformat)) {
fimc_capture_try_format(ctx, &pix->width, &pix->height,
NULL, &pix->pixelformat,
- FIMC_SD_PAD_SINK);
+ FIMC_SD_PAD_SINK_CAM);
ctx->s_frame.f_width = pix->width;
ctx->s_frame.f_height = pix->height;
}
@@ -1121,44 +1126,51 @@
static int fimc_pipeline_validate(struct fimc_dev *fimc)
{
struct v4l2_subdev_format sink_fmt, src_fmt;
- struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- struct v4l2_subdev *sd;
- struct media_pad *pad;
- int ret;
-
- /* Start with the video capture node pad */
- pad = media_entity_remote_source(&vid_cap->vd_pad);
- if (pad == NULL)
- return -EPIPE;
- /* FIMC.{N} subdevice */
- sd = media_entity_to_v4l2_subdev(pad->entity);
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
+ struct v4l2_subdev *sd = &vc->subdev;
+ struct media_pad *sink_pad, *src_pad;
+ int i, ret;
while (1) {
- /* Retrieve format at the sink pad */
- pad = &sd->entity.pads[0];
- if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ /*
+ * Find current entity sink pad and any remote sink pad linked
+ * to it. We stop if there is no sink pad in current entity or
+ * it is not linked to any other remote entity.
+ */
+ src_pad = NULL;
+
+ for (i = 0; i < sd->entity.num_pads; i++) {
+ struct media_pad *p = &sd->entity.pads[i];
+
+ if (p->flags & MEDIA_PAD_FL_SINK) {
+ sink_pad = p;
+ src_pad = media_entity_remote_source(sink_pad);
+ if (src_pad)
+ break;
+ }
+ }
+
+ if (src_pad == NULL ||
+ media_entity_type(src_pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
break;
+
/* Don't call FIMC subdev operation to avoid nested locking */
- if (sd == &fimc->vid_cap.subdev) {
- struct fimc_frame *ff = &vid_cap->ctx->s_frame;
+ if (sd == &vc->subdev) {
+ struct fimc_frame *ff = &vc->ctx->s_frame;
sink_fmt.format.width = ff->f_width;
sink_fmt.format.height = ff->f_height;
sink_fmt.format.code = ff->fmt ? ff->fmt->mbus_code : 0;
} else {
- sink_fmt.pad = pad->index;
+ sink_fmt.pad = sink_pad->index;
sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt);
if (ret < 0 && ret != -ENOIOCTLCMD)
return -EPIPE;
}
- /* Retrieve format at the source pad */
- pad = media_entity_remote_source(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- break;
- sd = media_entity_to_v4l2_subdev(pad->entity);
- src_fmt.pad = pad->index;
+ /* Retrieve format at the source pad */
+ sd = media_entity_to_v4l2_subdev(src_pad->entity);
+ src_fmt.pad = src_pad->index;
src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
if (ret < 0 && ret != -ENOIOCTLCMD)
@@ -1172,7 +1184,7 @@
if (sd == fimc->pipeline.subdevs[IDX_SENSOR] &&
fimc_user_defined_mbus_fmt(src_fmt.format.code)) {
struct v4l2_plane_pix_format plane_fmt[FIMC_MAX_PLANES];
- struct fimc_frame *frame = &vid_cap->ctx->d_frame;
+ struct fimc_frame *frame = &vc->ctx->d_frame;
unsigned int i;
ret = fimc_get_sensor_frame_desc(sd, plane_fmt,
@@ -1196,6 +1208,8 @@
struct fimc_pipeline *p = &fimc->pipeline;
struct fimc_vid_cap *vc = &fimc->vid_cap;
struct media_entity *entity = &vc->vfd.entity;
+ struct fimc_source_info *si = NULL;
+ struct v4l2_subdev *sd;
int ret;
if (fimc_capture_active(fimc))
@@ -1205,6 +1219,23 @@
if (ret < 0)
return ret;
+ sd = p->subdevs[IDX_SENSOR];
+ if (sd)
+ si = v4l2_get_subdev_hostdata(sd);
+
+ if (si == NULL) {
+ ret = -EPIPE;
+ goto err_p_stop;
+ }
+ /*
+ * Save configuration data related to currently attached image
+ * sensor or other data source, e.g. FIMC-IS.
+ */
+ vc->source_config = *si;
+
+ if (vc->input == GRP_ID_FIMC_IS)
+ vc->source_config.fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK;
+
if (vc->user_subdev_api) {
ret = fimc_pipeline_validate(fimc);
if (ret < 0)
@@ -1461,25 +1492,37 @@
{
struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_frame *ff = &ctx->s_frame;
struct v4l2_mbus_framefmt *mf;
- struct fimc_frame *ff;
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
mf = v4l2_subdev_get_try_format(fh, fmt->pad);
fmt->format = *mf;
return 0;
}
- mf = &fmt->format;
- mf->colorspace = V4L2_COLORSPACE_JPEG;
- ff = fmt->pad == FIMC_SD_PAD_SINK ? &ctx->s_frame : &ctx->d_frame;
+ mf = &fmt->format;
mutex_lock(&fimc->lock);
- /* The pixel code is same on both input and output pad */
- if (!WARN_ON(ctx->s_frame.fmt == NULL))
- mf->code = ctx->s_frame.fmt->mbus_code;
- mf->width = ff->f_width;
- mf->height = ff->f_height;
+
+ switch (fmt->pad) {
+ case FIMC_SD_PAD_SOURCE:
+ if (!WARN_ON(ff->fmt == NULL))
+ mf->code = ff->fmt->mbus_code;
+ /* Sink pads crop rectangle size */
+ mf->width = ff->width;
+ mf->height = ff->height;
+ break;
+ case FIMC_SD_PAD_SINK_FIFO:
+ *mf = fimc->vid_cap.wb_fmt;
+ break;
+ case FIMC_SD_PAD_SINK_CAM:
+ default:
+ *mf = fimc->vid_cap.ci_fmt;
+ break;
+ }
+
mutex_unlock(&fimc->lock);
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
return 0;
}
@@ -1490,15 +1533,15 @@
{
struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf = &fmt->format;
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
+ struct fimc_ctx *ctx = vc->ctx;
struct fimc_frame *ff;
struct fimc_fmt *ffmt;
dbg("pad%d: code: 0x%x, %dx%d",
fmt->pad, mf->code, mf->width, mf->height);
- if (fmt->pad == FIMC_SD_PAD_SOURCE &&
- vb2_is_busy(&fimc->vid_cap.vbq))
+ if (fmt->pad == FIMC_SD_PAD_SOURCE && vb2_is_busy(&vc->vbq))
return -EBUSY;
mutex_lock(&fimc->lock);
@@ -1520,21 +1563,32 @@
fimc_alpha_ctrl_update(ctx);
fimc_capture_mark_jpeg_xfer(ctx, ffmt->color);
-
- ff = fmt->pad == FIMC_SD_PAD_SINK ?
- &ctx->s_frame : &ctx->d_frame;
+ if (fmt->pad == FIMC_SD_PAD_SOURCE) {
+ ff = &ctx->d_frame;
+ /* Sink pads crop rectangle size */
+ mf->width = ctx->s_frame.width;
+ mf->height = ctx->s_frame.height;
+ } else {
+ ff = &ctx->s_frame;
+ }
mutex_lock(&fimc->lock);
set_frame_bounds(ff, mf->width, mf->height);
- fimc->vid_cap.mf = *mf;
+
+ if (fmt->pad == FIMC_SD_PAD_SINK_FIFO)
+ vc->wb_fmt = *mf;
+ else if (fmt->pad == FIMC_SD_PAD_SINK_CAM)
+ vc->ci_fmt = *mf;
+
ff->fmt = ffmt;
/* Reset the crop rectangle if required. */
if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_COMPOSE)))
set_frame_crop(ff, 0, 0, mf->width, mf->height);
- if (fmt->pad == FIMC_SD_PAD_SINK)
+ if (fmt->pad != FIMC_SD_PAD_SOURCE)
ctx->state &= ~FIMC_COMPOSE;
+
mutex_unlock(&fimc->lock);
return 0;
}
@@ -1549,7 +1603,7 @@
struct v4l2_rect *r = &sel->r;
struct v4l2_rect *try_sel;
- if (sel->pad != FIMC_SD_PAD_SINK)
+ if (sel->pad == FIMC_SD_PAD_SOURCE)
return -EINVAL;
mutex_lock(&fimc->lock);
@@ -1605,7 +1659,7 @@
struct v4l2_rect *try_sel;
unsigned long flags;
- if (sel->pad != FIMC_SD_PAD_SINK)
+ if (sel->pad == FIMC_SD_PAD_SOURCE)
return -EINVAL;
mutex_lock(&fimc->lock);
@@ -1809,7 +1863,8 @@
sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->id);
- fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_CAM].flags = MEDIA_PAD_FL_SINK;
+ fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_FIFO].flags = MEDIA_PAD_FL_SINK;
fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
fimc->vid_cap.sd_pads, 0);