fimc_hw_set_effect(ctx, false);
fimc_hw_set_output_path(ctx);
fimc_hw_set_out_dma(ctx);
+ if (fimc->variant->has_alpha)
+ fimc_hw_set_rgb_alpha(ctx);
clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
}
spin_unlock_irqrestore(&fimc->slock, flags);
return ret;
}
-static int fimc_capture_state_cleanup(struct fimc_dev *fimc)
+static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend)
{
struct fimc_vid_cap *cap = &fimc->vid_cap;
struct fimc_vid_buffer *buf;
unsigned long flags;
+ bool streaming;
spin_lock_irqsave(&fimc->slock, flags);
- fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_PEND |
- 1 << ST_CAPT_SHUT | 1 << ST_CAPT_STREAM |
- 1 << ST_CAPT_ISP_STREAM);
+ streaming = fimc->state & (1 << ST_CAPT_ISP_STREAM);
- fimc->vid_cap.active_buf_cnt = 0;
+ fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_SHUT |
+ 1 << ST_CAPT_STREAM | 1 << ST_CAPT_ISP_STREAM);
+ if (!suspend)
+ fimc->state &= ~(1 << ST_CAPT_PEND | 1 << ST_CAPT_SUSPENDED);
- /* Release buffers that were enqueued in the driver by videobuf2. */
- while (!list_empty(&cap->pending_buf_q)) {
- buf = pending_queue_pop(cap);
+ /* Release unused buffers */
+ while (!suspend && !list_empty(&cap->pending_buf_q)) {
+ buf = fimc_pending_queue_pop(cap);
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
-
+ /* If suspending put unused buffers onto pending queue */
while (!list_empty(&cap->active_buf_q)) {
- buf = active_queue_pop(cap);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ buf = fimc_active_queue_pop(cap);
+ if (suspend)
+ fimc_pending_queue_add(cap, buf);
+ else
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
+ set_bit(ST_CAPT_SUSPENDED, &fimc->state);
+
+ fimc_hw_reset(fimc);
+ cap->buf_index = 0;
spin_unlock_irqrestore(&fimc->slock, flags);
- if (test_bit(ST_CAPT_ISP_STREAM, &fimc->state))
+ if (streaming)
return fimc_pipeline_s_stream(fimc, 0);
else
return 0;
}
-static int fimc_stop_capture(struct fimc_dev *fimc)
+static int fimc_stop_capture(struct fimc_dev *fimc, bool suspend)
{
- struct fimc_vid_cap *cap = &fimc->vid_cap;
unsigned long flags;
if (!fimc_capture_active(fimc))
wait_event_timeout(fimc->irq_queue,
!test_bit(ST_CAPT_SHUT, &fimc->state),
- FIMC_SHUTDOWN_TIMEOUT);
+ (2*HZ/10)); /* 200 ms */
- return fimc_capture_state_cleanup(fimc);
+ return fimc_capture_state_cleanup(fimc, suspend);
}
/**
struct fimc_dev *fimc = ctx->fimc_dev;
int ret;
- if (test_bit(ST_CAPT_APPLY_CFG, &fimc->state))
+ if (!test_bit(ST_CAPT_APPLY_CFG, &fimc->state))
return 0;
spin_lock(&ctx->slock);
fimc_hw_set_rotation(ctx);
fimc_prepare_dma_offset(ctx, &ctx->d_frame);
fimc_hw_set_out_dma(ctx);
- set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ if (fimc->variant->has_alpha)
+ fimc_hw_set_rgb_alpha(ctx);
+ clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
}
spin_unlock(&ctx->slock);
return ret;
int min_bufs;
int ret;
- fimc_hw_reset(fimc);
vid_cap->frame_count = 0;
ret = fimc_init_capture(fimc);
return 0;
error:
- fimc_capture_state_cleanup(fimc);
+ fimc_capture_state_cleanup(fimc, false);
return ret;
}
if (!fimc_capture_active(fimc))
return -EINVAL;
- return fimc_stop_capture(fimc);
+ return fimc_stop_capture(fimc, false);
}
int fimc_capture_suspend(struct fimc_dev *fimc)
{
- return -EBUSY;
+ bool suspend = fimc_capture_busy(fimc);
+
+ int ret = fimc_stop_capture(fimc, suspend);
+ if (ret)
+ return ret;
+ return fimc_pipeline_shutdown(fimc);
}
+static void buffer_queue(struct vb2_buffer *vb);
+
int fimc_capture_resume(struct fimc_dev *fimc)
{
- return 0;
-}
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ struct fimc_vid_buffer *buf;
+ int i;
-static unsigned int get_plane_size(struct fimc_frame *fr, unsigned int plane)
-{
- if (!fr || plane >= fr->fmt->memplanes)
+ if (!test_and_clear_bit(ST_CAPT_SUSPENDED, &fimc->state))
return 0;
- return fr->f_width * fr->f_height * fr->fmt->depth[plane] / 8;
+
+ INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
+ vid_cap->buf_index = 0;
+ fimc_pipeline_initialize(fimc, &fimc->vid_cap.vfd->entity,
+ false);
+ fimc_init_capture(fimc);
+
+ clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
+
+ for (i = 0; i < vid_cap->reqbufs_count; i++) {
+ if (list_empty(&vid_cap->pending_buf_q))
+ break;
+ buf = fimc_pending_queue_pop(vid_cap);
+ buffer_queue(&buf->vb);
+ }
+ return 0;
+
}
-static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
- unsigned int *num_planes, unsigned int sizes[],
- void *allocators[])
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
{
+ const struct v4l2_pix_format_mplane *pixm = NULL;
struct fimc_ctx *ctx = vq->drv_priv;
- struct fimc_fmt *fmt = ctx->d_frame.fmt;
+ struct fimc_frame *frame = &ctx->d_frame;
+ struct fimc_fmt *fmt = frame->fmt;
+ unsigned long wh;
int i;
- if (!fmt)
+ if (pfmt) {
+ pixm = &pfmt->fmt.pix_mp;
+ fmt = fimc_find_format(&pixm->pixelformat, NULL,
+ FMT_FLAGS_CAM | FMT_FLAGS_M2M, -1);
+ wh = pixm->width * pixm->height;
+ } else {
+ wh = frame->f_width * frame->f_height;
+ }
+
+ if (fmt == NULL)
return -EINVAL;
*num_planes = fmt->memplanes;
for (i = 0; i < fmt->memplanes; i++) {
- sizes[i] = get_plane_size(&ctx->d_frame, i);
+ unsigned int size = (wh * fmt->depth[i]) / 8;
+ if (pixm)
+ sizes[i] = max(size, pixm->plane_fmt[i].sizeimage);
+ else
+ sizes[i] = size;
allocators[i] = ctx->fimc_dev->alloc_ctx;
}
spin_lock_irqsave(&fimc->slock, flags);
fimc_prepare_addr(ctx, &buf->vb, &ctx->d_frame, &buf->paddr);
- if (!test_bit(ST_CAPT_STREAM, &fimc->state)
- && vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) {
+ if (!test_bit(ST_CAPT_SUSPENDED, &fimc->state) &&
+ !test_bit(ST_CAPT_STREAM, &fimc->state) &&
+ vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) {
/* Setup the buffer directly for processing. */
int buf_id = (vid_cap->reqbufs_count == 1) ? -1 :
vid_cap->buf_index;
fimc_hw_set_output_addr(fimc, &buf->paddr, buf_id);
buf->index = vid_cap->buf_index;
- active_queue_add(vid_cap, buf);
+ fimc_active_queue_add(vid_cap, buf);
if (++vid_cap->buf_index >= FIMC_MAX_OUT_BUFS)
vid_cap->buf_index = 0;
if (fimc_m2m_active(fimc))
return -EBUSY;
+ set_bit(ST_CAPT_BUSY, &fimc->state);
pm_runtime_get_sync(&fimc->pdev->dev);
if (++fimc->vid_cap.refcnt == 1) {
pm_runtime_put_sync(&fimc->pdev->dev);
fimc->vid_cap.refcnt--;
v4l2_fh_release(file);
+ clear_bit(ST_CAPT_BUSY, &fimc->state);
return ret;
}
ret = fimc_capture_ctrls_create(fimc);
dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
if (--fimc->vid_cap.refcnt == 0) {
- fimc_stop_capture(fimc);
+ clear_bit(ST_CAPT_BUSY, &fimc->state);
+ fimc_stop_capture(fimc, false);
fimc_pipeline_shutdown(fimc);
- fimc_ctrls_delete(fimc->vid_cap.ctx);
- vb2_queue_release(&fimc->vid_cap.vbq);
+ clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
}
pm_runtime_put(&fimc->pdev->dev);
+ if (fimc->vid_cap.refcnt == 0) {
+ vb2_queue_release(&fimc->vid_cap.vbq);
+ fimc_ctrls_delete(fimc->vid_cap.ctx);
+ }
return v4l2_fh_release(file);
}
max_w = rotation ? pl->out_rot_en_w : pl->out_rot_dis_w;
min_w = ctx->state & FIMC_DST_CROP ? dst->width : var->min_out_pixsize;
min_h = ctx->state & FIMC_DST_CROP ? dst->height : var->min_out_pixsize;
- if (fimc->id == 1 && var->pix_hoff)
+ if (var->min_vsize_align == 1 && !rotation)
align_h = fimc_fmt_is_rgb(ffmt->color) ? 0 : 1;
depth = fimc_get_format_depth(ffmt);
strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
cap->bus_info[0] = 0;
- cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VIDEO_CAPTURE_MPLANE;
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE;
return 0;
}
mf->code = 0;
continue;
}
- if (mf->width != tfmt->width || mf->width != tfmt->width) {
+ if (mf->width != tfmt->width || mf->height != tfmt->height) {
u32 fcc = ffmt->fourcc;
tfmt->width = mf->width;
tfmt->height = mf->height;
NULL, &fcc, FIMC_SD_PAD_SOURCE);
if (ffmt && ffmt->mbus_code)
mf->code = ffmt->mbus_code;
- if (mf->width != tfmt->width || mf->width != tfmt->width)
+ if (mf->width != tfmt->width ||
+ mf->height != tfmt->height)
continue;
tfmt->code = mf->code;
}
ret = v4l2_subdev_call(csis, pad, set_fmt, NULL, &sfmt);
if (mf->code == tfmt->code &&
- mf->width == tfmt->width && mf->width == tfmt->width)
+ mf->width == tfmt->width && mf->height == tfmt->height)
break;
}
FIMC_SD_PAD_SOURCE);
if (!ff->fmt)
return -EINVAL;
+
+ /* Update RGB Alpha control state and value range */
+ fimc_alpha_ctrl_update(ctx);
+
/* Try to match format at the host and the sensor */
if (!fimc->vid_cap.user_subdev_api) {
mf->code = ff->fmt->mbus_code;
return vb2_dqbuf(&fimc->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK);
}
-static int fimc_cap_cropcap(struct file *file, void *fh,
- struct v4l2_cropcap *cr)
+static int fimc_cap_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *create)
{
struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame;
- if (cr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- return -EINVAL;
+ return vb2_create_bufs(&fimc->vid_cap.vbq, create);
+}
- cr->bounds.left = 0;
- cr->bounds.top = 0;
- cr->bounds.width = f->o_width;
- cr->bounds.height = f->o_height;
- cr->defrect = cr->bounds;
+static int fimc_cap_prepare_buf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
- return 0;
+ return vb2_prepare_buf(&fimc->vid_cap.vbq, b);
}
-static int fimc_cap_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+static int fimc_cap_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
{
struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame;
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_frame *f = &ctx->s_frame;
- cr->c.left = f->offs_h;
- cr->c.top = f->offs_v;
- cr->c.width = f->width;
- cr->c.height = f->height;
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
- return 0;
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ f = &ctx->d_frame;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = f->o_width;
+ s->r.height = f->o_height;
+ return 0;
+
+ case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ f = &ctx->d_frame;
+ case V4L2_SEL_TGT_CROP_ACTIVE:
+ s->r.left = f->offs_h;
+ s->r.top = f->offs_v;
+ s->r.width = f->width;
+ s->r.height = f->height;
+ return 0;
+ }
+
+ return -EINVAL;
}
-static int fimc_cap_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
+int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
+{
+ if (a->left < b->left || a->top < b->top)
+ return 0;
+ if (a->left + a->width > b->left + b->width)
+ return 0;
+ if (a->top + a->height > b->top + b->height)
+ return 0;
+
+ return 1;
+}
+
+static int fimc_cap_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
{
struct fimc_dev *fimc = video_drvdata(file);
struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct fimc_frame *ff;
+ struct v4l2_rect rect = s->r;
+ struct fimc_frame *f;
unsigned long flags;
+ unsigned int pad;
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ f = &ctx->d_frame;
+ pad = FIMC_SD_PAD_SOURCE;
+ break;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_ACTIVE:
+ f = &ctx->s_frame;
+ pad = FIMC_SD_PAD_SINK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ fimc_capture_try_crop(ctx, &rect, pad);
- fimc_capture_try_crop(ctx, &cr->c, FIMC_SD_PAD_SINK);
- ff = &ctx->s_frame;
+ if (s->flags & V4L2_SEL_FLAG_LE &&
+ !enclosed_rectangle(&rect, &s->r))
+ return -ERANGE;
+ if (s->flags & V4L2_SEL_FLAG_GE &&
+ !enclosed_rectangle(&s->r, &rect))
+ return -ERANGE;
+
+ s->r = rect;
spin_lock_irqsave(&fimc->slock, flags);
- set_frame_crop(ff, cr->c.left, cr->c.top, cr->c.width, cr->c.height);
- set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ set_frame_crop(f, s->r.left, s->r.top, s->r.width,
+ s->r.height);
spin_unlock_irqrestore(&fimc->slock, flags);
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
return 0;
}
.vidioc_qbuf = fimc_cap_qbuf,
.vidioc_dqbuf = fimc_cap_dqbuf,
+ .vidioc_prepare_buf = fimc_cap_prepare_buf,
+ .vidioc_create_bufs = fimc_cap_create_bufs,
+
.vidioc_streamon = fimc_cap_streamon,
.vidioc_streamoff = fimc_cap_streamoff,
- .vidioc_g_crop = fimc_cap_g_crop,
- .vidioc_s_crop = fimc_cap_s_crop,
- .vidioc_cropcap = fimc_cap_cropcap,
+ .vidioc_g_selection = fimc_cap_g_selection,
+ .vidioc_s_selection = fimc_cap_s_selection,
.vidioc_enum_input = fimc_cap_enum_input,
.vidioc_s_input = fimc_cap_s_input,
.link_setup = fimc_link_setup,
};
+/**
+ * fimc_sensor_notify - v4l2_device notification from a sensor subdev
+ * @sd: pointer to a subdev generating the notification
+ * @notification: the notification type, must be S5P_FIMC_TX_END_NOTIFY
+ * @arg: pointer to an u32 type integer that stores the frame payload value
+ *
+ * The End Of Frame notification sent by sensor subdev in its still capture
+ * mode. If there is only a single VSYNC generated by the sensor at the
+ * beginning of a frame transmission, FIMC does not issue the LastIrq
+ * (end of frame) interrupt. And this notification is used to complete the
+ * frame capture and returning a buffer to user-space. Subdev drivers should
+ * call this notification from their last 'End of frame capture' interrupt.
+ */
+void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
+ void *arg)
+{
+ struct fimc_sensor_info *sensor;
+ struct fimc_vid_buffer *buf;
+ struct fimc_md *fmd;
+ struct fimc_dev *fimc;
+ unsigned long flags;
+
+ if (sd == NULL)
+ return;
+
+ sensor = v4l2_get_subdev_hostdata(sd);
+ fmd = entity_to_fimc_mdev(&sd->entity);
+
+ spin_lock_irqsave(&fmd->slock, flags);
+ fimc = sensor ? sensor->host : NULL;
+
+ if (fimc && arg && notification == S5P_FIMC_TX_END_NOTIFY &&
+ test_bit(ST_CAPT_PEND, &fimc->state)) {
+ unsigned long irq_flags;
+ spin_lock_irqsave(&fimc->slock, irq_flags);
+ if (!list_empty(&fimc->vid_cap.active_buf_q)) {
+ buf = list_entry(fimc->vid_cap.active_buf_q.next,
+ struct fimc_vid_buffer, list);
+ vb2_set_plane_payload(&buf->vb, 0, *((u32 *)arg));
+ }
+ fimc_capture_irq_handler(fimc, true);
+ fimc_deactivate_capture(fimc);
+ spin_unlock_irqrestore(&fimc->slock, irq_flags);
+ }
+ spin_unlock_irqrestore(&fmd->slock, flags);
+}
+
static int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code)
*mf = fmt->format;
return 0;
}
+ /* Update RGB Alpha control state and value range */
+ fimc_alpha_ctrl_update(ctx);
+
fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ffmt->color));
ff = fmt->pad == FIMC_SD_PAD_SINK ?
mutex_lock(&fimc->lock);
set_frame_bounds(ff, mf->width, mf->height);
+ fimc->vid_cap.mf = *mf;
ff->fmt = ffmt;
/* Reset the crop rectangle if required. */
fimc_capture_try_crop(ctx, r, crop->pad);
if (crop->which == V4L2_SUBDEV_FORMAT_TRY) {
- mutex_lock(&fimc->lock);
+ mutex_unlock(&fimc->lock);
*v4l2_subdev_get_try_crop(fh, crop->pad) = *r;
return 0;
}
media_entity_cleanup(&sd->entity);
v4l2_device_unregister_subdev(sd);
kfree(sd);
- sd = NULL;
+ fimc->vid_cap.subdev = NULL;
}
/* Set default format at the sensor and host interface */