Skip to content

Commit

Permalink
[media] s5p-fimc: Add support for ISP Writeback data input bus type
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Sylwester Nawrocki authored and Mauro Carvalho Chehab committed Mar 31, 2013
1 parent 056f4f3 commit 88fa831
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 79 deletions.
1 change: 1 addition & 0 deletions drivers/media/platform/s5p-fimc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
config VIDEO_SAMSUNG_S5P_FIMC
bool "Samsung S5P/EXYNOS SoC camera interface driver (experimental)"
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && PLAT_S5P && PM_RUNTIME
depends on MFD_SYSCON
help
Say Y here to enable camera host interface devices for
Samsung S5P and EXYNOS SoC series.
Expand Down
177 changes: 116 additions & 61 deletions drivers/media/platform/s5p-fimc/fimc-capture.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -606,18 +607,22 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx,
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 */
Expand Down Expand Up @@ -851,7 +856,7 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx,
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)
Expand Down Expand Up @@ -938,7 +943,7 @@ static int fimc_cap_try_fmt_mplane(struct file *file, void *fh,
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;
}
Expand Down Expand Up @@ -992,7 +997,7 @@ static int __fimc_capture_set_format(struct fimc_dev *fimc,
{
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;
Expand All @@ -1004,7 +1009,7 @@ static int __fimc_capture_set_format(struct fimc_dev *fimc,
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;
}
Expand Down Expand Up @@ -1121,44 +1126,51 @@ static int fimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
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)
Expand All @@ -1172,7 +1184,7 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc)
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,
Expand All @@ -1196,6 +1208,8 @@ static int fimc_cap_streamon(struct file *file, void *priv,
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))
Expand All @@ -1205,6 +1219,23 @@ static int fimc_cap_streamon(struct file *file, void *priv,
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)
Expand Down Expand Up @@ -1461,25 +1492,37 @@ static int fimc_subdev_get_fmt(struct v4l2_subdev *sd,
{
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;
}
Expand All @@ -1490,15 +1533,15 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd,
{
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);
Expand All @@ -1520,21 +1563,32 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd,
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;
}
Expand All @@ -1549,7 +1603,7 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd,
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);
Expand Down Expand Up @@ -1605,7 +1659,7 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
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);
Expand Down Expand Up @@ -1809,7 +1863,8 @@ int fimc_initialize_capture_subdev(struct fimc_dev *fimc)
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);
Expand Down
10 changes: 10 additions & 0 deletions drivers/media/platform/s5p-fimc/fimc-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/list.h>
#include <linux/mfd/syscon.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
Expand Down Expand Up @@ -78,6 +79,10 @@ static struct fimc_fmt fimc_formats[] = {
.memplanes = 1,
.colplanes = 1,
.flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA,
}, {
.name = "YUV 4:4:4",
.mbus_code = V4L2_MBUS_FMT_YUV10_1X30,
.flags = FMT_FLAGS_WRITEBACK,
}, {
.name = "YUV 4:2:2 packed, YCbYCr",
.fourcc = V4L2_PIX_FMT_YUYV,
Expand Down Expand Up @@ -959,6 +964,11 @@ static int fimc_probe(struct platform_device *pdev)
spin_lock_init(&fimc->slock);
mutex_init(&fimc->lock);

fimc->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
"samsung,sysreg");
if (IS_ERR(fimc->sysreg))
return PTR_ERR(fimc->sysreg);

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
fimc->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(fimc->regs))
Expand Down
Loading

0 comments on commit 88fa831

Please sign in to comment.