return 0;
}
-static int stop_streaming(struct vb2_queue *q)
+static void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
{
- struct fimc_ctx *ctx = q->drv_priv;
+ struct vb2_buffer *src_vb, *dst_vb;
struct fimc_dev *fimc = ctx->fimc_dev;
+ if (!ctx || !ctx->m2m_ctx)
+ return;
+
+ src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+
+ if (src_vb && dst_vb) {
+ v4l2_m2m_buf_done(src_vb, vb_state);
+ v4l2_m2m_buf_done(dst_vb, vb_state);
+ v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx);
+ }
+}
+
+/* Complete the transaction which has been scheduled for execution. */
+static void fimc_m2m_shutdown(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ int ret;
+
if (!fimc_m2m_pending(fimc))
- return 0;
+ return;
- set_bit(ST_M2M_SHUT, &fimc->state);
+ fimc_ctx_state_lock_set(FIMC_CTX_SHUT, ctx);
- wait_event_timeout(fimc->irq_queue,
- !test_bit(ST_M2M_SHUT, &fimc->state),
+ ret = wait_event_timeout(fimc->irq_queue,
+ !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx),
FIMC_SHUTDOWN_TIMEOUT);
+ /*
+ * In case of a timeout the buffers are not released in the interrupt
+ * handler so return them here with the error flag set, if there are
+ * any on the queue.
+ */
+ if (ret == 0)
+ fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+}
+
+static int stop_streaming(struct vb2_queue *q)
+{
+ struct fimc_ctx *ctx = q->drv_priv;
+
+ fimc_m2m_shutdown(ctx);
return 0;
}
-static void fimc_capture_handler(struct fimc_dev *fimc)
+static void fimc_capture_irq_handler(struct fimc_dev *fimc)
{
struct fimc_vid_cap *cap = &fimc->vid_cap;
struct fimc_vid_buffer *v_buf;
{
struct fimc_dev *fimc = priv;
struct fimc_vid_cap *cap = &fimc->vid_cap;
+ struct fimc_ctx *ctx;
- BUG_ON(!fimc);
fimc_hw_clear_irq(fimc);
- spin_lock(&fimc->slock);
+ if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) {
+ ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev);
+ if (ctx != NULL) {
+ fimc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE);
- if (test_and_clear_bit(ST_M2M_SHUT, &fimc->state)) {
- wake_up(&fimc->irq_queue);
- goto isr_unlock;
- } else if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) {
- struct vb2_buffer *src_vb, *dst_vb;
- struct fimc_ctx *ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev);
-
- if (!ctx || !ctx->m2m_ctx)
- goto isr_unlock;
-
- src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
- dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
- if (src_vb && dst_vb) {
- v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
- v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
- v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx);
+ spin_lock(&ctx->slock);
+ if (ctx->state & FIMC_CTX_SHUT) {
+ ctx->state &= ~FIMC_CTX_SHUT;
+ wake_up(&fimc->irq_queue);
+ }
+ spin_unlock(&ctx->slock);
}
- goto isr_unlock;
+ return IRQ_HANDLED;
}
+ spin_lock(&fimc->slock);
+
if (test_bit(ST_CAPT_PEND, &fimc->state)) {
fimc_capture_irq_handler(fimc);
}
}
-isr_unlock:
spin_unlock(&fimc->slock);
return IRQ_HANDLED;
}
ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR);
ret = fimc_prepare_config(ctx, ctx->state);
- if (ret) {
- err("Wrong parameters");
+ if (ret)
goto dma_unlock;
- }
+
/* Reconfigure hardware if the context has changed. */
if (fimc->m2m.ctx != ctx) {
ctx->state |= FIMC_PARAMS;
fimc->m2m.ctx = ctx;
}
+ spin_lock(&fimc->slock);
fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr);
if (ctx->state & FIMC_PARAMS) {
fimc_hw_set_input_path(ctx);
fimc_hw_set_in_dma(ctx);
- if (fimc_set_scaler_info(ctx)) {
- err("Scaler setup error");
+ ret = fimc_set_scaler_info(ctx);
+ if (ret) {
+ spin_unlock(&fimc->slock);
goto dma_unlock;
}
-
fimc_hw_set_prescaler(ctx);
fimc_hw_set_mainscaler(ctx);
fimc_hw_set_target_format(ctx);
ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP |
FIMC_SRC_FMT | FIMC_DST_FMT);
fimc_hw_activate_input_dma(fimc, true);
+ spin_unlock(&fimc->slock);
dma_unlock:
spin_unlock_irqrestore(&ctx->slock, flags);
static void fimc_job_abort(void *priv)
{
- struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
-
- if (!fimc_m2m_pending(fimc))
- return;
-
- set_bit(ST_M2M_SHUT, &fimc->state);
-
- wait_event_timeout(fimc->irq_queue,
- !test_bit(ST_M2M_SHUT, &fimc->state),
- FIMC_SHUTDOWN_TIMEOUT);
+ fimc_m2m_shutdown(priv);
}
static int fimc_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- if (ctx->state & FIMC_CTX_CAP)
+ if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx))
return -EINVAL;
is_output = 1;
} else if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
struct vb2_queue *vq;
struct fimc_frame *frame;
struct v4l2_pix_format_mplane *pix;
- unsigned long flags;
int i, ret = 0;
- u32 tmp;
ret = fimc_vidioc_try_fmt_mplane(file, priv, f);
if (ret)
frame->offs_h = 0;
frame->offs_v = 0;
- spin_lock_irqsave(&ctx->slock, flags);
- tmp = (frame == &ctx->d_frame) ? FIMC_DST_FMT : FIMC_SRC_FMT;
- ctx->state |= FIMC_PARAMS | tmp;
- spin_unlock_irqrestore(&ctx->slock, flags);
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_DST_FMT, ctx);
+ else
+ fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx);
dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
/* The source and target color format need to be set */
if (V4L2_TYPE_IS_OUTPUT(type)) {
- if (~ctx->state & FIMC_SRC_FMT)
+ if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx))
return -EINVAL;
- } else if (~ctx->state & FIMC_DST_FMT) {
+ } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) {
return -EINVAL;
}
return 0;
}
- if (ctx->state & FIMC_CTX_CAP) {
+ if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx)) {
return v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd,
core, queryctrl, qc);
}
ctrl->value = ctx->rotation;
break;
default:
- if (ctx->state & FIMC_CTX_CAP) {
+ if (fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx)) {
return v4l2_subdev_call(fimc->vid_cap.sd, core,
g_ctrl, ctrl);
} else {
- v4l2_err(&fimc->m2m.v4l2_dev,
- "Invalid control\n");
+ v4l2_err(&fimc->m2m.v4l2_dev, "Invalid control\n");
return -EINVAL;
}
}
{
struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
struct fimc_dev *fimc = ctx->fimc_dev;
- unsigned long flags;
int ret = 0;
- spin_lock_irqsave(&ctx->slock, flags);
-
switch (ctrl->id) {
case V4L2_CID_HFLIP:
if (ctrl->value)
break;
case V4L2_CID_ROTATE:
- if (!(~ctx->state & (FIMC_DST_FMT | FIMC_SRC_FMT))) {
+ if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
ret = fimc_check_scaler_ratio(ctx->s_frame.width,
- ctx->s_frame.height,
- ctx->d_frame.width,
- ctx->d_frame.height,
- ctrl->value);
- if (ret) {
- v4l2_err(&fimc->m2m.v4l2_dev,
- "Out of scaler range");
- spin_unlock_irqrestore(&ctx->slock, flags);
- return -EINVAL;
- }
+ ctx->s_frame.height, ctx->d_frame.width,
+ ctx->d_frame.height, ctrl->value);
+ }
+
+ if (ret) {
+ v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range\n");
+ return -EINVAL;
}
/* Check for the output rotator availability */
if ((ctrl->value == 90 || ctrl->value == 270) &&
- (ctx->in_path == FIMC_DMA && !variant->has_out_rot)) {
- spin_unlock_irqrestore(&ctx->slock, flags);
+ (ctx->in_path == FIMC_DMA && !variant->has_out_rot))
return -EINVAL;
- } else {
- ctx->rotation = ctrl->value;
- }
+ ctx->rotation = ctrl->value;
break;
default:
- spin_unlock_irqrestore(&ctx->slock, flags);
v4l2_err(&fimc->m2m.v4l2_dev, "Invalid control\n");
return -EINVAL;
}
- ctx->state |= FIMC_PARAMS;
- spin_unlock_irqrestore(&ctx->slock, flags);
+
+ fimc_ctx_state_lock_set(FIMC_PARAMS, ctx);
return 0;
}
struct fimc_dev *fimc = ctx->fimc_dev;
struct fimc_frame *f;
u32 min_size, halign, depth = 0;
+ bool is_capture_ctx;
int i;
if (cr->c.top < 0 || cr->c.left < 0) {
return -EINVAL;
}
+ is_capture_ctx = fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx);
+
if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- f = (ctx->state & FIMC_CTX_CAP) ? &ctx->s_frame : &ctx->d_frame;
+ f = is_capture_ctx ? &ctx->s_frame : &ctx->d_frame;
else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
- ctx->state & FIMC_CTX_M2M)
+ !is_capture_ctx)
f = &ctx->s_frame;
else
return -EINVAL;
min_size = (f == &ctx->s_frame) ?
fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize;
- if (ctx->state & FIMC_CTX_M2M) {
+ /* Get pixel alignment constraints. */
+ if (is_capture_ctx) {
+ min_size = 16;
+ halign = 4;
+ } else {
if (fimc->id == 1 && fimc->variant->pix_hoff)
halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1;
else
halign = ffs(min_size) - 1;
- /* there are more strict aligment requirements at camera interface */
- } else {
- min_size = 16;
- halign = 4;
}
for (i = 0; i < f->fmt->colplanes; i++)
cr->c.top = f->o_height - cr->c.height;
cr->c.left = round_down(cr->c.left, min_size);
- cr->c.top = round_down(cr->c.top,
- ctx->state & FIMC_CTX_M2M ? 8 : 16);
+ cr->c.top = round_down(cr->c.top, is_capture_ctx ? 16 : 8);
dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
cr->c.left, cr->c.top, cr->c.width, cr->c.height,
return 0;
}
-
static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
{
struct fimc_ctx *ctx = file->private_data;
struct fimc_dev *fimc = ctx->fimc_dev;
- unsigned long flags;
struct fimc_frame *f;
int ret;
f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
&ctx->s_frame : &ctx->d_frame;
- spin_lock_irqsave(&ctx->slock, flags);
/* Check to see if scaling ratio is within supported range */
- if (!(~ctx->state & (FIMC_DST_FMT | FIMC_SRC_FMT))) {
+ if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
ret = fimc_check_scaler_ratio(cr->c.width, cr->c.height,
ctx->d_frame.width,
cr->c.width, cr->c.height,
ctx->rotation);
}
-
if (ret) {
- v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range");
- spin_unlock_irqrestore(&ctx->slock, flags);
+ v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range\n");
return -EINVAL;
}
}
- ctx->state |= FIMC_PARAMS;
-
f->offs_h = cr->c.left;
f->offs_v = cr->c.top;
f->width = cr->c.width;
f->height = cr->c.height;
- spin_unlock_irqrestore(&ctx->slock, flags);
+ fimc_ctx_state_lock_set(FIMC_PARAMS, ctx);
+
return 0;
}