diff options
author | Sylwester Nawrocki <s.nawrocki@samsung.com> | 2012-05-08 14:51:24 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-05-20 08:15:10 -0400 |
commit | 97d974226575227ebafdf3ab009f0212d8a7e223 (patch) | |
tree | 15776cc9f7081d54367b0d2e2e33a32070e0be59 /drivers/media | |
parent | 41df5bf088a10e54c0613ec4d7350b74d5ab8252 (diff) |
[media] s5p-fimc: Move m2m node driver into separate file
Virtually no functional changes, just code reordering. This let us to
clearly separate all logical functions available in the driver: fimc
capture, mem-to-mem, and later fimc-lite capture, ISP features, etc.
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>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/video/s5p-fimc/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/video/s5p-fimc/fimc-capture.c | 68 | ||||
-rw-r--r-- | drivers/media/video/s5p-fimc/fimc-core.c | 859 | ||||
-rw-r--r-- | drivers/media/video/s5p-fimc/fimc-core.h | 10 | ||||
-rw-r--r-- | drivers/media/video/s5p-fimc/fimc-m2m.c | 812 |
5 files changed, 894 insertions, 857 deletions
diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile index 33dec7f890e7..1da3c6ae7fa8 100644 --- a/drivers/media/video/s5p-fimc/Makefile +++ b/drivers/media/video/s5p-fimc/Makefile | |||
@@ -1,4 +1,4 @@ | |||
1 | s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-capture.o fimc-mdevice.o | 1 | s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o fimc-mdevice.o |
2 | s5p-csis-objs := mipi-csis.o | 2 | s5p-csis-objs := mipi-csis.o |
3 | 3 | ||
4 | obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o | 4 | obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o |
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index be5e4e237297..0051d8161c6c 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c | |||
@@ -139,7 +139,7 @@ static int fimc_stop_capture(struct fimc_dev *fimc, bool suspend) | |||
139 | * spinlock held. It updates the camera pixel crop, rotation and | 139 | * spinlock held. It updates the camera pixel crop, rotation and |
140 | * image flip in H/W. | 140 | * image flip in H/W. |
141 | */ | 141 | */ |
142 | int fimc_capture_config_update(struct fimc_ctx *ctx) | 142 | static int fimc_capture_config_update(struct fimc_ctx *ctx) |
143 | { | 143 | { |
144 | struct fimc_dev *fimc = ctx->fimc_dev; | 144 | struct fimc_dev *fimc = ctx->fimc_dev; |
145 | int ret; | 145 | int ret; |
@@ -166,6 +166,70 @@ int fimc_capture_config_update(struct fimc_ctx *ctx) | |||
166 | return ret; | 166 | return ret; |
167 | } | 167 | } |
168 | 168 | ||
169 | void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf) | ||
170 | { | ||
171 | struct fimc_vid_cap *cap = &fimc->vid_cap; | ||
172 | struct fimc_vid_buffer *v_buf; | ||
173 | struct timeval *tv; | ||
174 | struct timespec ts; | ||
175 | |||
176 | if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) { | ||
177 | wake_up(&fimc->irq_queue); | ||
178 | goto done; | ||
179 | } | ||
180 | |||
181 | if (!list_empty(&cap->active_buf_q) && | ||
182 | test_bit(ST_CAPT_RUN, &fimc->state) && deq_buf) { | ||
183 | ktime_get_real_ts(&ts); | ||
184 | |||
185 | v_buf = fimc_active_queue_pop(cap); | ||
186 | |||
187 | tv = &v_buf->vb.v4l2_buf.timestamp; | ||
188 | tv->tv_sec = ts.tv_sec; | ||
189 | tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; | ||
190 | v_buf->vb.v4l2_buf.sequence = cap->frame_count++; | ||
191 | |||
192 | vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE); | ||
193 | } | ||
194 | |||
195 | if (!list_empty(&cap->pending_buf_q)) { | ||
196 | |||
197 | v_buf = fimc_pending_queue_pop(cap); | ||
198 | fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index); | ||
199 | v_buf->index = cap->buf_index; | ||
200 | |||
201 | /* Move the buffer to the capture active queue */ | ||
202 | fimc_active_queue_add(cap, v_buf); | ||
203 | |||
204 | dbg("next frame: %d, done frame: %d", | ||
205 | fimc_hw_get_frame_index(fimc), v_buf->index); | ||
206 | |||
207 | if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) | ||
208 | cap->buf_index = 0; | ||
209 | } | ||
210 | |||
211 | if (cap->active_buf_cnt == 0) { | ||
212 | if (deq_buf) | ||
213 | clear_bit(ST_CAPT_RUN, &fimc->state); | ||
214 | |||
215 | if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) | ||
216 | cap->buf_index = 0; | ||
217 | } else { | ||
218 | set_bit(ST_CAPT_RUN, &fimc->state); | ||
219 | } | ||
220 | |||
221 | fimc_capture_config_update(cap->ctx); | ||
222 | done: | ||
223 | if (cap->active_buf_cnt == 1) { | ||
224 | fimc_deactivate_capture(fimc); | ||
225 | clear_bit(ST_CAPT_STREAM, &fimc->state); | ||
226 | } | ||
227 | |||
228 | dbg("frame: %d, active_buf_cnt: %d", | ||
229 | fimc_hw_get_frame_index(fimc), cap->active_buf_cnt); | ||
230 | } | ||
231 | |||
232 | |||
169 | static int start_streaming(struct vb2_queue *q, unsigned int count) | 233 | static int start_streaming(struct vb2_queue *q, unsigned int count) |
170 | { | 234 | { |
171 | struct fimc_ctx *ctx = q->drv_priv; | 235 | struct fimc_ctx *ctx = q->drv_priv; |
@@ -1245,7 +1309,7 @@ void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, | |||
1245 | struct fimc_vid_buffer, list); | 1309 | struct fimc_vid_buffer, list); |
1246 | vb2_set_plane_payload(&buf->vb, 0, *((u32 *)arg)); | 1310 | vb2_set_plane_payload(&buf->vb, 0, *((u32 *)arg)); |
1247 | } | 1311 | } |
1248 | fimc_capture_irq_handler(fimc, true); | 1312 | fimc_capture_irq_handler(fimc, 1); |
1249 | fimc_deactivate_capture(fimc); | 1313 | fimc_deactivate_capture(fimc); |
1250 | spin_unlock_irqrestore(&fimc->slock, irq_flags); | 1314 | spin_unlock_irqrestore(&fimc->slock, irq_flags); |
1251 | } | 1315 | } |
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index f6b9060a0c0f..749db4deefcb 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Samsung S5P/EXYNOS4 SoC series camera interface (video postprocessor) driver | 2 | * Samsung S5P/EXYNOS4 SoC series FIMC (CAMIF) driver |
3 | * | 3 | * |
4 | * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. | 4 | * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. |
5 | * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com> | 5 | * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com> |
@@ -187,12 +187,12 @@ static struct fimc_fmt fimc_formats[] = { | |||
187 | }, | 187 | }, |
188 | }; | 188 | }; |
189 | 189 | ||
190 | static unsigned int get_m2m_fmt_flags(unsigned int stream_type) | 190 | struct fimc_fmt * fimc_get_format(unsigned int index) |
191 | { | 191 | { |
192 | if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | 192 | if (index >= ARRAY_SIZE(fimc_formats)) |
193 | return FMT_FLAGS_M2M_IN; | 193 | return NULL; |
194 | else | 194 | |
195 | return FMT_FLAGS_M2M_OUT; | 195 | return &fimc_formats[index]; |
196 | } | 196 | } |
197 | 197 | ||
198 | int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, | 198 | int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, |
@@ -293,126 +293,9 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx) | |||
293 | return 0; | 293 | return 0; |
294 | } | 294 | } |
295 | 295 | ||
296 | static void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state) | ||
297 | { | ||
298 | struct vb2_buffer *src_vb, *dst_vb; | ||
299 | |||
300 | if (!ctx || !ctx->m2m_ctx) | ||
301 | return; | ||
302 | |||
303 | src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); | ||
304 | dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); | ||
305 | |||
306 | if (src_vb && dst_vb) { | ||
307 | v4l2_m2m_buf_done(src_vb, vb_state); | ||
308 | v4l2_m2m_buf_done(dst_vb, vb_state); | ||
309 | v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev, | ||
310 | ctx->m2m_ctx); | ||
311 | } | ||
312 | } | ||
313 | |||
314 | /* Complete the transaction which has been scheduled for execution. */ | ||
315 | static int fimc_m2m_shutdown(struct fimc_ctx *ctx) | ||
316 | { | ||
317 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
318 | int ret; | ||
319 | |||
320 | if (!fimc_m2m_pending(fimc)) | ||
321 | return 0; | ||
322 | |||
323 | fimc_ctx_state_set(FIMC_CTX_SHUT, ctx); | ||
324 | |||
325 | ret = wait_event_timeout(fimc->irq_queue, | ||
326 | !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), | ||
327 | FIMC_SHUTDOWN_TIMEOUT); | ||
328 | |||
329 | return ret == 0 ? -ETIMEDOUT : ret; | ||
330 | } | ||
331 | |||
332 | static int start_streaming(struct vb2_queue *q, unsigned int count) | ||
333 | { | ||
334 | struct fimc_ctx *ctx = q->drv_priv; | ||
335 | int ret; | ||
336 | |||
337 | ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev); | ||
338 | return ret > 0 ? 0 : ret; | ||
339 | } | ||
340 | |||
341 | static int stop_streaming(struct vb2_queue *q) | ||
342 | { | ||
343 | struct fimc_ctx *ctx = q->drv_priv; | ||
344 | int ret; | ||
345 | |||
346 | ret = fimc_m2m_shutdown(ctx); | ||
347 | if (ret == -ETIMEDOUT) | ||
348 | fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); | ||
349 | |||
350 | pm_runtime_put(&ctx->fimc_dev->pdev->dev); | ||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | void fimc_capture_irq_handler(struct fimc_dev *fimc, bool final) | ||
355 | { | ||
356 | struct fimc_vid_cap *cap = &fimc->vid_cap; | ||
357 | struct fimc_vid_buffer *v_buf; | ||
358 | struct timeval *tv; | ||
359 | struct timespec ts; | ||
360 | |||
361 | if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) { | ||
362 | wake_up(&fimc->irq_queue); | ||
363 | return; | ||
364 | } | ||
365 | |||
366 | if (!list_empty(&cap->active_buf_q) && | ||
367 | test_bit(ST_CAPT_RUN, &fimc->state) && final) { | ||
368 | ktime_get_real_ts(&ts); | ||
369 | |||
370 | v_buf = fimc_active_queue_pop(cap); | ||
371 | |||
372 | tv = &v_buf->vb.v4l2_buf.timestamp; | ||
373 | tv->tv_sec = ts.tv_sec; | ||
374 | tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; | ||
375 | v_buf->vb.v4l2_buf.sequence = cap->frame_count++; | ||
376 | |||
377 | vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE); | ||
378 | } | ||
379 | |||
380 | if (!list_empty(&cap->pending_buf_q)) { | ||
381 | |||
382 | v_buf = fimc_pending_queue_pop(cap); | ||
383 | fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index); | ||
384 | v_buf->index = cap->buf_index; | ||
385 | |||
386 | /* Move the buffer to the capture active queue */ | ||
387 | fimc_active_queue_add(cap, v_buf); | ||
388 | |||
389 | dbg("next frame: %d, done frame: %d", | ||
390 | fimc_hw_get_frame_index(fimc), v_buf->index); | ||
391 | |||
392 | if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) | ||
393 | cap->buf_index = 0; | ||
394 | } | ||
395 | |||
396 | if (cap->active_buf_cnt == 0) { | ||
397 | if (final) | ||
398 | clear_bit(ST_CAPT_RUN, &fimc->state); | ||
399 | |||
400 | if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) | ||
401 | cap->buf_index = 0; | ||
402 | } else { | ||
403 | set_bit(ST_CAPT_RUN, &fimc->state); | ||
404 | } | ||
405 | |||
406 | fimc_capture_config_update(cap->ctx); | ||
407 | |||
408 | dbg("frame: %d, active_buf_cnt: %d", | ||
409 | fimc_hw_get_frame_index(fimc), cap->active_buf_cnt); | ||
410 | } | ||
411 | |||
412 | static irqreturn_t fimc_irq_handler(int irq, void *priv) | 296 | static irqreturn_t fimc_irq_handler(int irq, void *priv) |
413 | { | 297 | { |
414 | struct fimc_dev *fimc = priv; | 298 | struct fimc_dev *fimc = priv; |
415 | struct fimc_vid_cap *cap = &fimc->vid_cap; | ||
416 | struct fimc_ctx *ctx; | 299 | struct fimc_ctx *ctx; |
417 | 300 | ||
418 | fimc_hw_clear_irq(fimc); | 301 | fimc_hw_clear_irq(fimc); |
@@ -437,12 +320,9 @@ static irqreturn_t fimc_irq_handler(int irq, void *priv) | |||
437 | return IRQ_HANDLED; | 320 | return IRQ_HANDLED; |
438 | } | 321 | } |
439 | } else if (test_bit(ST_CAPT_PEND, &fimc->state)) { | 322 | } else if (test_bit(ST_CAPT_PEND, &fimc->state)) { |
440 | fimc_capture_irq_handler(fimc, | 323 | int last_buf = test_bit(ST_CAPT_JPEG, &fimc->state) && |
441 | !test_bit(ST_CAPT_JPEG, &fimc->state)); | 324 | fimc->vid_cap.reqbufs_count == 1; |
442 | if (cap->active_buf_cnt == 1) { | 325 | fimc_capture_irq_handler(fimc, !last_buf); |
443 | fimc_deactivate_capture(fimc); | ||
444 | clear_bit(ST_CAPT_STREAM, &fimc->state); | ||
445 | } | ||
446 | } | 326 | } |
447 | out: | 327 | out: |
448 | spin_unlock(&fimc->slock); | 328 | spin_unlock(&fimc->slock); |
@@ -582,154 +462,6 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) | |||
582 | f->fmt->color, f->dma_offset.y_h, f->dma_offset.y_v); | 462 | f->fmt->color, f->dma_offset.y_h, f->dma_offset.y_v); |
583 | } | 463 | } |
584 | 464 | ||
585 | static void fimc_dma_run(void *priv) | ||
586 | { | ||
587 | struct vb2_buffer *vb = NULL; | ||
588 | struct fimc_ctx *ctx = priv; | ||
589 | struct fimc_frame *sf, *df; | ||
590 | struct fimc_dev *fimc; | ||
591 | unsigned long flags; | ||
592 | u32 ret; | ||
593 | |||
594 | if (WARN(!ctx, "null hardware context\n")) | ||
595 | return; | ||
596 | |||
597 | fimc = ctx->fimc_dev; | ||
598 | spin_lock_irqsave(&fimc->slock, flags); | ||
599 | set_bit(ST_M2M_PEND, &fimc->state); | ||
600 | sf = &ctx->s_frame; | ||
601 | df = &ctx->d_frame; | ||
602 | |||
603 | if (ctx->state & FIMC_PARAMS) { | ||
604 | /* Prepare the DMA offsets for scaler */ | ||
605 | fimc_prepare_dma_offset(ctx, sf); | ||
606 | fimc_prepare_dma_offset(ctx, df); | ||
607 | } | ||
608 | |||
609 | vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); | ||
610 | ret = fimc_prepare_addr(ctx, vb, sf, &sf->paddr); | ||
611 | if (ret) | ||
612 | goto dma_unlock; | ||
613 | |||
614 | vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); | ||
615 | ret = fimc_prepare_addr(ctx, vb, df, &df->paddr); | ||
616 | if (ret) | ||
617 | goto dma_unlock; | ||
618 | |||
619 | /* Reconfigure hardware if the context has changed. */ | ||
620 | if (fimc->m2m.ctx != ctx) { | ||
621 | ctx->state |= FIMC_PARAMS; | ||
622 | fimc->m2m.ctx = ctx; | ||
623 | } | ||
624 | |||
625 | if (ctx->state & FIMC_PARAMS) { | ||
626 | fimc_set_yuv_order(ctx); | ||
627 | fimc_hw_set_input_path(ctx); | ||
628 | fimc_hw_set_in_dma(ctx); | ||
629 | ret = fimc_set_scaler_info(ctx); | ||
630 | if (ret) | ||
631 | goto dma_unlock; | ||
632 | fimc_hw_set_prescaler(ctx); | ||
633 | fimc_hw_set_mainscaler(ctx); | ||
634 | fimc_hw_set_target_format(ctx); | ||
635 | fimc_hw_set_rotation(ctx); | ||
636 | fimc_hw_set_effect(ctx, false); | ||
637 | fimc_hw_set_out_dma(ctx); | ||
638 | if (fimc->variant->has_alpha) | ||
639 | fimc_hw_set_rgb_alpha(ctx); | ||
640 | fimc_hw_set_output_path(ctx); | ||
641 | } | ||
642 | fimc_hw_set_input_addr(fimc, &sf->paddr); | ||
643 | fimc_hw_set_output_addr(fimc, &df->paddr, -1); | ||
644 | |||
645 | fimc_activate_capture(ctx); | ||
646 | |||
647 | ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP | | ||
648 | FIMC_SRC_FMT | FIMC_DST_FMT); | ||
649 | fimc_hw_activate_input_dma(fimc, true); | ||
650 | dma_unlock: | ||
651 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
652 | } | ||
653 | |||
654 | static void fimc_job_abort(void *priv) | ||
655 | { | ||
656 | fimc_m2m_shutdown(priv); | ||
657 | } | ||
658 | |||
659 | static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, | ||
660 | unsigned int *num_buffers, unsigned int *num_planes, | ||
661 | unsigned int sizes[], void *allocators[]) | ||
662 | { | ||
663 | struct fimc_ctx *ctx = vb2_get_drv_priv(vq); | ||
664 | struct fimc_frame *f; | ||
665 | int i; | ||
666 | |||
667 | f = ctx_get_frame(ctx, vq->type); | ||
668 | if (IS_ERR(f)) | ||
669 | return PTR_ERR(f); | ||
670 | /* | ||
671 | * Return number of non-contigous planes (plane buffers) | ||
672 | * depending on the configured color format. | ||
673 | */ | ||
674 | if (!f->fmt) | ||
675 | return -EINVAL; | ||
676 | |||
677 | *num_planes = f->fmt->memplanes; | ||
678 | for (i = 0; i < f->fmt->memplanes; i++) { | ||
679 | sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8; | ||
680 | allocators[i] = ctx->fimc_dev->alloc_ctx; | ||
681 | } | ||
682 | return 0; | ||
683 | } | ||
684 | |||
685 | static int fimc_buf_prepare(struct vb2_buffer *vb) | ||
686 | { | ||
687 | struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); | ||
688 | struct fimc_frame *frame; | ||
689 | int i; | ||
690 | |||
691 | frame = ctx_get_frame(ctx, vb->vb2_queue->type); | ||
692 | if (IS_ERR(frame)) | ||
693 | return PTR_ERR(frame); | ||
694 | |||
695 | for (i = 0; i < frame->fmt->memplanes; i++) | ||
696 | vb2_set_plane_payload(vb, i, frame->payload[i]); | ||
697 | |||
698 | return 0; | ||
699 | } | ||
700 | |||
701 | static void fimc_buf_queue(struct vb2_buffer *vb) | ||
702 | { | ||
703 | struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); | ||
704 | |||
705 | dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); | ||
706 | |||
707 | if (ctx->m2m_ctx) | ||
708 | v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); | ||
709 | } | ||
710 | |||
711 | static void fimc_lock(struct vb2_queue *vq) | ||
712 | { | ||
713 | struct fimc_ctx *ctx = vb2_get_drv_priv(vq); | ||
714 | mutex_lock(&ctx->fimc_dev->lock); | ||
715 | } | ||
716 | |||
717 | static void fimc_unlock(struct vb2_queue *vq) | ||
718 | { | ||
719 | struct fimc_ctx *ctx = vb2_get_drv_priv(vq); | ||
720 | mutex_unlock(&ctx->fimc_dev->lock); | ||
721 | } | ||
722 | |||
723 | static struct vb2_ops fimc_qops = { | ||
724 | .queue_setup = fimc_queue_setup, | ||
725 | .buf_prepare = fimc_buf_prepare, | ||
726 | .buf_queue = fimc_buf_queue, | ||
727 | .wait_prepare = fimc_unlock, | ||
728 | .wait_finish = fimc_lock, | ||
729 | .stop_streaming = stop_streaming, | ||
730 | .start_streaming = start_streaming, | ||
731 | }; | ||
732 | |||
733 | /* | 465 | /* |
734 | * V4L2 controls handling | 466 | * V4L2 controls handling |
735 | */ | 467 | */ |
@@ -877,39 +609,6 @@ void fimc_alpha_ctrl_update(struct fimc_ctx *ctx) | |||
877 | v4l2_ctrl_unlock(ctrl); | 609 | v4l2_ctrl_unlock(ctrl); |
878 | } | 610 | } |
879 | 611 | ||
880 | /* | ||
881 | * V4L2 ioctl handlers | ||
882 | */ | ||
883 | static int fimc_m2m_querycap(struct file *file, void *fh, | ||
884 | struct v4l2_capability *cap) | ||
885 | { | ||
886 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
887 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
888 | |||
889 | strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); | ||
890 | strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); | ||
891 | cap->bus_info[0] = 0; | ||
892 | cap->capabilities = V4L2_CAP_STREAMING | | ||
893 | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; | ||
894 | |||
895 | return 0; | ||
896 | } | ||
897 | |||
898 | static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv, | ||
899 | struct v4l2_fmtdesc *f) | ||
900 | { | ||
901 | struct fimc_fmt *fmt; | ||
902 | |||
903 | fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type), | ||
904 | f->index); | ||
905 | if (!fmt) | ||
906 | return -EINVAL; | ||
907 | |||
908 | strncpy(f->description, fmt->name, sizeof(f->description) - 1); | ||
909 | f->pixelformat = fmt->fourcc; | ||
910 | return 0; | ||
911 | } | ||
912 | |||
913 | int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f) | 612 | int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f) |
914 | { | 613 | { |
915 | struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; | 614 | struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; |
@@ -988,18 +687,6 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, | |||
988 | } | 687 | } |
989 | } | 688 | } |
990 | 689 | ||
991 | static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, | ||
992 | struct v4l2_format *f) | ||
993 | { | ||
994 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
995 | struct fimc_frame *frame = ctx_get_frame(ctx, f->type); | ||
996 | |||
997 | if (IS_ERR(frame)) | ||
998 | return PTR_ERR(frame); | ||
999 | |||
1000 | return fimc_fill_format(frame, f); | ||
1001 | } | ||
1002 | |||
1003 | /** | 690 | /** |
1004 | * fimc_find_format - lookup fimc color format by fourcc or media bus format | 691 | * fimc_find_format - lookup fimc color format by fourcc or media bus format |
1005 | * @pixelformat: fourcc to match, ignored if null | 692 | * @pixelformat: fourcc to match, ignored if null |
@@ -1032,534 +719,6 @@ struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code, | |||
1032 | return def_fmt; | 719 | return def_fmt; |
1033 | } | 720 | } |
1034 | 721 | ||
1035 | static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) | ||
1036 | { | ||
1037 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
1038 | struct samsung_fimc_variant *variant = fimc->variant; | ||
1039 | struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; | ||
1040 | struct fimc_fmt *fmt; | ||
1041 | u32 max_w, mod_x, mod_y; | ||
1042 | |||
1043 | if (!IS_M2M(f->type)) | ||
1044 | return -EINVAL; | ||
1045 | |||
1046 | dbg("w: %d, h: %d", pix->width, pix->height); | ||
1047 | |||
1048 | fmt = fimc_find_format(&pix->pixelformat, NULL, | ||
1049 | get_m2m_fmt_flags(f->type), 0); | ||
1050 | if (WARN(fmt == NULL, "Pixel format lookup failed")) | ||
1051 | return -EINVAL; | ||
1052 | |||
1053 | if (pix->field == V4L2_FIELD_ANY) | ||
1054 | pix->field = V4L2_FIELD_NONE; | ||
1055 | else if (pix->field != V4L2_FIELD_NONE) | ||
1056 | return -EINVAL; | ||
1057 | |||
1058 | if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { | ||
1059 | max_w = variant->pix_limit->scaler_dis_w; | ||
1060 | mod_x = ffs(variant->min_inp_pixsize) - 1; | ||
1061 | } else { | ||
1062 | max_w = variant->pix_limit->out_rot_dis_w; | ||
1063 | mod_x = ffs(variant->min_out_pixsize) - 1; | ||
1064 | } | ||
1065 | |||
1066 | if (tiled_fmt(fmt)) { | ||
1067 | mod_x = 6; /* 64 x 32 pixels tile */ | ||
1068 | mod_y = 5; | ||
1069 | } else { | ||
1070 | if (variant->min_vsize_align == 1) | ||
1071 | mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1; | ||
1072 | else | ||
1073 | mod_y = ffs(variant->min_vsize_align) - 1; | ||
1074 | } | ||
1075 | |||
1076 | v4l_bound_align_image(&pix->width, 16, max_w, mod_x, | ||
1077 | &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0); | ||
1078 | |||
1079 | fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp); | ||
1080 | return 0; | ||
1081 | } | ||
1082 | |||
1083 | static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh, | ||
1084 | struct v4l2_format *f) | ||
1085 | { | ||
1086 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
1087 | |||
1088 | return fimc_try_fmt_mplane(ctx, f); | ||
1089 | } | ||
1090 | |||
1091 | static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, | ||
1092 | struct v4l2_format *f) | ||
1093 | { | ||
1094 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
1095 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
1096 | struct vb2_queue *vq; | ||
1097 | struct fimc_frame *frame; | ||
1098 | struct v4l2_pix_format_mplane *pix; | ||
1099 | int i, ret = 0; | ||
1100 | |||
1101 | ret = fimc_try_fmt_mplane(ctx, f); | ||
1102 | if (ret) | ||
1103 | return ret; | ||
1104 | |||
1105 | vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); | ||
1106 | |||
1107 | if (vb2_is_busy(vq)) { | ||
1108 | v4l2_err(fimc->m2m.vfd, "queue (%d) busy\n", f->type); | ||
1109 | return -EBUSY; | ||
1110 | } | ||
1111 | |||
1112 | if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | ||
1113 | frame = &ctx->s_frame; | ||
1114 | else | ||
1115 | frame = &ctx->d_frame; | ||
1116 | |||
1117 | pix = &f->fmt.pix_mp; | ||
1118 | frame->fmt = fimc_find_format(&pix->pixelformat, NULL, | ||
1119 | get_m2m_fmt_flags(f->type), 0); | ||
1120 | if (!frame->fmt) | ||
1121 | return -EINVAL; | ||
1122 | |||
1123 | /* Update RGB Alpha control state and value range */ | ||
1124 | fimc_alpha_ctrl_update(ctx); | ||
1125 | |||
1126 | for (i = 0; i < frame->fmt->colplanes; i++) { | ||
1127 | frame->payload[i] = | ||
1128 | (pix->width * pix->height * frame->fmt->depth[i]) / 8; | ||
1129 | } | ||
1130 | |||
1131 | fimc_fill_frame(frame, f); | ||
1132 | |||
1133 | ctx->scaler.enabled = 1; | ||
1134 | |||
1135 | if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | ||
1136 | fimc_ctx_state_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); | ||
1137 | else | ||
1138 | fimc_ctx_state_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); | ||
1139 | |||
1140 | dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height); | ||
1141 | |||
1142 | return 0; | ||
1143 | } | ||
1144 | |||
1145 | static int fimc_m2m_reqbufs(struct file *file, void *fh, | ||
1146 | struct v4l2_requestbuffers *reqbufs) | ||
1147 | { | ||
1148 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
1149 | |||
1150 | return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); | ||
1151 | } | ||
1152 | |||
1153 | static int fimc_m2m_querybuf(struct file *file, void *fh, | ||
1154 | struct v4l2_buffer *buf) | ||
1155 | { | ||
1156 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
1157 | |||
1158 | return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); | ||
1159 | } | ||
1160 | |||
1161 | static int fimc_m2m_qbuf(struct file *file, void *fh, | ||
1162 | struct v4l2_buffer *buf) | ||
1163 | { | ||
1164 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
1165 | |||
1166 | return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); | ||
1167 | } | ||
1168 | |||
1169 | static int fimc_m2m_dqbuf(struct file *file, void *fh, | ||
1170 | struct v4l2_buffer *buf) | ||
1171 | { | ||
1172 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
1173 | |||
1174 | return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); | ||
1175 | } | ||
1176 | |||
1177 | static int fimc_m2m_streamon(struct file *file, void *fh, | ||
1178 | enum v4l2_buf_type type) | ||
1179 | { | ||
1180 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
1181 | |||
1182 | /* The source and target color format need to be set */ | ||
1183 | if (V4L2_TYPE_IS_OUTPUT(type)) { | ||
1184 | if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx)) | ||
1185 | return -EINVAL; | ||
1186 | } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) { | ||
1187 | return -EINVAL; | ||
1188 | } | ||
1189 | |||
1190 | return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); | ||
1191 | } | ||
1192 | |||
1193 | static int fimc_m2m_streamoff(struct file *file, void *fh, | ||
1194 | enum v4l2_buf_type type) | ||
1195 | { | ||
1196 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
1197 | |||
1198 | return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); | ||
1199 | } | ||
1200 | |||
1201 | static int fimc_m2m_cropcap(struct file *file, void *fh, | ||
1202 | struct v4l2_cropcap *cr) | ||
1203 | { | ||
1204 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
1205 | struct fimc_frame *frame; | ||
1206 | |||
1207 | frame = ctx_get_frame(ctx, cr->type); | ||
1208 | if (IS_ERR(frame)) | ||
1209 | return PTR_ERR(frame); | ||
1210 | |||
1211 | cr->bounds.left = 0; | ||
1212 | cr->bounds.top = 0; | ||
1213 | cr->bounds.width = frame->o_width; | ||
1214 | cr->bounds.height = frame->o_height; | ||
1215 | cr->defrect = cr->bounds; | ||
1216 | |||
1217 | return 0; | ||
1218 | } | ||
1219 | |||
1220 | static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) | ||
1221 | { | ||
1222 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
1223 | struct fimc_frame *frame; | ||
1224 | |||
1225 | frame = ctx_get_frame(ctx, cr->type); | ||
1226 | if (IS_ERR(frame)) | ||
1227 | return PTR_ERR(frame); | ||
1228 | |||
1229 | cr->c.left = frame->offs_h; | ||
1230 | cr->c.top = frame->offs_v; | ||
1231 | cr->c.width = frame->width; | ||
1232 | cr->c.height = frame->height; | ||
1233 | |||
1234 | return 0; | ||
1235 | } | ||
1236 | |||
1237 | static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) | ||
1238 | { | ||
1239 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
1240 | struct fimc_frame *f; | ||
1241 | u32 min_size, halign, depth = 0; | ||
1242 | int i; | ||
1243 | |||
1244 | if (cr->c.top < 0 || cr->c.left < 0) { | ||
1245 | v4l2_err(fimc->m2m.vfd, | ||
1246 | "doesn't support negative values for top & left\n"); | ||
1247 | return -EINVAL; | ||
1248 | } | ||
1249 | if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | ||
1250 | f = &ctx->d_frame; | ||
1251 | else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | ||
1252 | f = &ctx->s_frame; | ||
1253 | else | ||
1254 | return -EINVAL; | ||
1255 | |||
1256 | min_size = (f == &ctx->s_frame) ? | ||
1257 | fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize; | ||
1258 | |||
1259 | /* Get pixel alignment constraints. */ | ||
1260 | if (fimc->variant->min_vsize_align == 1) | ||
1261 | halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1; | ||
1262 | else | ||
1263 | halign = ffs(fimc->variant->min_vsize_align) - 1; | ||
1264 | |||
1265 | for (i = 0; i < f->fmt->colplanes; i++) | ||
1266 | depth += f->fmt->depth[i]; | ||
1267 | |||
1268 | v4l_bound_align_image(&cr->c.width, min_size, f->o_width, | ||
1269 | ffs(min_size) - 1, | ||
1270 | &cr->c.height, min_size, f->o_height, | ||
1271 | halign, 64/(ALIGN(depth, 8))); | ||
1272 | |||
1273 | /* adjust left/top if cropping rectangle is out of bounds */ | ||
1274 | if (cr->c.left + cr->c.width > f->o_width) | ||
1275 | cr->c.left = f->o_width - cr->c.width; | ||
1276 | if (cr->c.top + cr->c.height > f->o_height) | ||
1277 | cr->c.top = f->o_height - cr->c.height; | ||
1278 | |||
1279 | cr->c.left = round_down(cr->c.left, min_size); | ||
1280 | cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align); | ||
1281 | |||
1282 | dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", | ||
1283 | cr->c.left, cr->c.top, cr->c.width, cr->c.height, | ||
1284 | f->f_width, f->f_height); | ||
1285 | |||
1286 | return 0; | ||
1287 | } | ||
1288 | |||
1289 | static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) | ||
1290 | { | ||
1291 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
1292 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
1293 | struct fimc_frame *f; | ||
1294 | int ret; | ||
1295 | |||
1296 | ret = fimc_m2m_try_crop(ctx, cr); | ||
1297 | if (ret) | ||
1298 | return ret; | ||
1299 | |||
1300 | f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? | ||
1301 | &ctx->s_frame : &ctx->d_frame; | ||
1302 | |||
1303 | /* Check to see if scaling ratio is within supported range */ | ||
1304 | if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) { | ||
1305 | if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { | ||
1306 | ret = fimc_check_scaler_ratio(ctx, cr->c.width, | ||
1307 | cr->c.height, ctx->d_frame.width, | ||
1308 | ctx->d_frame.height, ctx->rotation); | ||
1309 | } else { | ||
1310 | ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, | ||
1311 | ctx->s_frame.height, cr->c.width, | ||
1312 | cr->c.height, ctx->rotation); | ||
1313 | } | ||
1314 | if (ret) { | ||
1315 | v4l2_err(fimc->m2m.vfd, "Out of scaler range\n"); | ||
1316 | return -EINVAL; | ||
1317 | } | ||
1318 | } | ||
1319 | |||
1320 | f->offs_h = cr->c.left; | ||
1321 | f->offs_v = cr->c.top; | ||
1322 | f->width = cr->c.width; | ||
1323 | f->height = cr->c.height; | ||
1324 | |||
1325 | fimc_ctx_state_set(FIMC_PARAMS, ctx); | ||
1326 | |||
1327 | return 0; | ||
1328 | } | ||
1329 | |||
1330 | static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { | ||
1331 | .vidioc_querycap = fimc_m2m_querycap, | ||
1332 | |||
1333 | .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane, | ||
1334 | .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane, | ||
1335 | |||
1336 | .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane, | ||
1337 | .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane, | ||
1338 | |||
1339 | .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane, | ||
1340 | .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane, | ||
1341 | |||
1342 | .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane, | ||
1343 | .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane, | ||
1344 | |||
1345 | .vidioc_reqbufs = fimc_m2m_reqbufs, | ||
1346 | .vidioc_querybuf = fimc_m2m_querybuf, | ||
1347 | |||
1348 | .vidioc_qbuf = fimc_m2m_qbuf, | ||
1349 | .vidioc_dqbuf = fimc_m2m_dqbuf, | ||
1350 | |||
1351 | .vidioc_streamon = fimc_m2m_streamon, | ||
1352 | .vidioc_streamoff = fimc_m2m_streamoff, | ||
1353 | |||
1354 | .vidioc_g_crop = fimc_m2m_g_crop, | ||
1355 | .vidioc_s_crop = fimc_m2m_s_crop, | ||
1356 | .vidioc_cropcap = fimc_m2m_cropcap | ||
1357 | |||
1358 | }; | ||
1359 | |||
1360 | static int queue_init(void *priv, struct vb2_queue *src_vq, | ||
1361 | struct vb2_queue *dst_vq) | ||
1362 | { | ||
1363 | struct fimc_ctx *ctx = priv; | ||
1364 | int ret; | ||
1365 | |||
1366 | memset(src_vq, 0, sizeof(*src_vq)); | ||
1367 | src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | ||
1368 | src_vq->io_modes = VB2_MMAP | VB2_USERPTR; | ||
1369 | src_vq->drv_priv = ctx; | ||
1370 | src_vq->ops = &fimc_qops; | ||
1371 | src_vq->mem_ops = &vb2_dma_contig_memops; | ||
1372 | src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); | ||
1373 | |||
1374 | ret = vb2_queue_init(src_vq); | ||
1375 | if (ret) | ||
1376 | return ret; | ||
1377 | |||
1378 | memset(dst_vq, 0, sizeof(*dst_vq)); | ||
1379 | dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | ||
1380 | dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; | ||
1381 | dst_vq->drv_priv = ctx; | ||
1382 | dst_vq->ops = &fimc_qops; | ||
1383 | dst_vq->mem_ops = &vb2_dma_contig_memops; | ||
1384 | dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); | ||
1385 | |||
1386 | return vb2_queue_init(dst_vq); | ||
1387 | } | ||
1388 | |||
1389 | static int fimc_m2m_open(struct file *file) | ||
1390 | { | ||
1391 | struct fimc_dev *fimc = video_drvdata(file); | ||
1392 | struct fimc_ctx *ctx; | ||
1393 | int ret; | ||
1394 | |||
1395 | dbg("pid: %d, state: 0x%lx, refcnt: %d", | ||
1396 | task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt); | ||
1397 | |||
1398 | /* | ||
1399 | * Return if the corresponding video capture node | ||
1400 | * is already opened. | ||
1401 | */ | ||
1402 | if (fimc->vid_cap.refcnt > 0) | ||
1403 | return -EBUSY; | ||
1404 | |||
1405 | ctx = kzalloc(sizeof *ctx, GFP_KERNEL); | ||
1406 | if (!ctx) | ||
1407 | return -ENOMEM; | ||
1408 | v4l2_fh_init(&ctx->fh, fimc->m2m.vfd); | ||
1409 | ctx->fimc_dev = fimc; | ||
1410 | |||
1411 | /* Default color format */ | ||
1412 | ctx->s_frame.fmt = &fimc_formats[0]; | ||
1413 | ctx->d_frame.fmt = &fimc_formats[0]; | ||
1414 | |||
1415 | ret = fimc_ctrls_create(ctx); | ||
1416 | if (ret) | ||
1417 | goto error_fh; | ||
1418 | |||
1419 | /* Use separate control handler per file handle */ | ||
1420 | ctx->fh.ctrl_handler = &ctx->ctrl_handler; | ||
1421 | file->private_data = &ctx->fh; | ||
1422 | v4l2_fh_add(&ctx->fh); | ||
1423 | |||
1424 | /* Setup the device context for memory-to-memory mode */ | ||
1425 | ctx->state = FIMC_CTX_M2M; | ||
1426 | ctx->flags = 0; | ||
1427 | ctx->in_path = FIMC_DMA; | ||
1428 | ctx->out_path = FIMC_DMA; | ||
1429 | |||
1430 | ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); | ||
1431 | if (IS_ERR(ctx->m2m_ctx)) { | ||
1432 | ret = PTR_ERR(ctx->m2m_ctx); | ||
1433 | goto error_c; | ||
1434 | } | ||
1435 | |||
1436 | if (fimc->m2m.refcnt++ == 0) | ||
1437 | set_bit(ST_M2M_RUN, &fimc->state); | ||
1438 | return 0; | ||
1439 | |||
1440 | error_c: | ||
1441 | fimc_ctrls_delete(ctx); | ||
1442 | error_fh: | ||
1443 | v4l2_fh_del(&ctx->fh); | ||
1444 | v4l2_fh_exit(&ctx->fh); | ||
1445 | kfree(ctx); | ||
1446 | return ret; | ||
1447 | } | ||
1448 | |||
1449 | static int fimc_m2m_release(struct file *file) | ||
1450 | { | ||
1451 | struct fimc_ctx *ctx = fh_to_ctx(file->private_data); | ||
1452 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
1453 | |||
1454 | dbg("pid: %d, state: 0x%lx, refcnt= %d", | ||
1455 | task_pid_nr(current), fimc->state, fimc->m2m.refcnt); | ||
1456 | |||
1457 | v4l2_m2m_ctx_release(ctx->m2m_ctx); | ||
1458 | fimc_ctrls_delete(ctx); | ||
1459 | v4l2_fh_del(&ctx->fh); | ||
1460 | v4l2_fh_exit(&ctx->fh); | ||
1461 | |||
1462 | if (--fimc->m2m.refcnt <= 0) | ||
1463 | clear_bit(ST_M2M_RUN, &fimc->state); | ||
1464 | kfree(ctx); | ||
1465 | return 0; | ||
1466 | } | ||
1467 | |||
1468 | static unsigned int fimc_m2m_poll(struct file *file, | ||
1469 | struct poll_table_struct *wait) | ||
1470 | { | ||
1471 | struct fimc_ctx *ctx = fh_to_ctx(file->private_data); | ||
1472 | |||
1473 | return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); | ||
1474 | } | ||
1475 | |||
1476 | |||
1477 | static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma) | ||
1478 | { | ||
1479 | struct fimc_ctx *ctx = fh_to_ctx(file->private_data); | ||
1480 | |||
1481 | return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); | ||
1482 | } | ||
1483 | |||
1484 | static const struct v4l2_file_operations fimc_m2m_fops = { | ||
1485 | .owner = THIS_MODULE, | ||
1486 | .open = fimc_m2m_open, | ||
1487 | .release = fimc_m2m_release, | ||
1488 | .poll = fimc_m2m_poll, | ||
1489 | .unlocked_ioctl = video_ioctl2, | ||
1490 | .mmap = fimc_m2m_mmap, | ||
1491 | }; | ||
1492 | |||
1493 | static struct v4l2_m2m_ops m2m_ops = { | ||
1494 | .device_run = fimc_dma_run, | ||
1495 | .job_abort = fimc_job_abort, | ||
1496 | }; | ||
1497 | |||
1498 | int fimc_register_m2m_device(struct fimc_dev *fimc, | ||
1499 | struct v4l2_device *v4l2_dev) | ||
1500 | { | ||
1501 | struct video_device *vfd; | ||
1502 | struct platform_device *pdev; | ||
1503 | int ret = 0; | ||
1504 | |||
1505 | if (!fimc) | ||
1506 | return -ENODEV; | ||
1507 | |||
1508 | pdev = fimc->pdev; | ||
1509 | fimc->v4l2_dev = v4l2_dev; | ||
1510 | |||
1511 | vfd = video_device_alloc(); | ||
1512 | if (!vfd) { | ||
1513 | v4l2_err(v4l2_dev, "Failed to allocate video device\n"); | ||
1514 | return -ENOMEM; | ||
1515 | } | ||
1516 | |||
1517 | vfd->fops = &fimc_m2m_fops; | ||
1518 | vfd->ioctl_ops = &fimc_m2m_ioctl_ops; | ||
1519 | vfd->v4l2_dev = v4l2_dev; | ||
1520 | vfd->minor = -1; | ||
1521 | vfd->release = video_device_release; | ||
1522 | vfd->lock = &fimc->lock; | ||
1523 | /* Locking in file operations other than ioctl should be done | ||
1524 | by the driver, not the V4L2 core. | ||
1525 | This driver needs auditing so that this flag can be removed. */ | ||
1526 | set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); | ||
1527 | |||
1528 | snprintf(vfd->name, sizeof(vfd->name), "%s.m2m", dev_name(&pdev->dev)); | ||
1529 | video_set_drvdata(vfd, fimc); | ||
1530 | |||
1531 | fimc->m2m.vfd = vfd; | ||
1532 | fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops); | ||
1533 | if (IS_ERR(fimc->m2m.m2m_dev)) { | ||
1534 | v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n"); | ||
1535 | ret = PTR_ERR(fimc->m2m.m2m_dev); | ||
1536 | goto err_init; | ||
1537 | } | ||
1538 | |||
1539 | ret = media_entity_init(&vfd->entity, 0, NULL, 0); | ||
1540 | if (!ret) | ||
1541 | return 0; | ||
1542 | |||
1543 | v4l2_m2m_release(fimc->m2m.m2m_dev); | ||
1544 | err_init: | ||
1545 | video_device_release(fimc->m2m.vfd); | ||
1546 | return ret; | ||
1547 | } | ||
1548 | |||
1549 | void fimc_unregister_m2m_device(struct fimc_dev *fimc) | ||
1550 | { | ||
1551 | if (!fimc) | ||
1552 | return; | ||
1553 | |||
1554 | if (fimc->m2m.m2m_dev) | ||
1555 | v4l2_m2m_release(fimc->m2m.m2m_dev); | ||
1556 | if (fimc->m2m.vfd) { | ||
1557 | media_entity_cleanup(&fimc->m2m.vfd->entity); | ||
1558 | /* Can also be called if video device wasn't registered */ | ||
1559 | video_unregister_device(fimc->m2m.vfd); | ||
1560 | } | ||
1561 | } | ||
1562 | |||
1563 | static void fimc_clk_put(struct fimc_dev *fimc) | 722 | static void fimc_clk_put(struct fimc_dev *fimc) |
1564 | { | 723 | { |
1565 | int i; | 724 | int i; |
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 7afabb0e1907..288631a86e53 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h | |||
@@ -712,6 +712,7 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, | |||
712 | struct v4l2_pix_format_mplane *pix); | 712 | struct v4l2_pix_format_mplane *pix); |
713 | struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code, | 713 | struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code, |
714 | unsigned int mask, int index); | 714 | unsigned int mask, int index); |
715 | struct fimc_fmt *fimc_get_format(unsigned int index); | ||
715 | 716 | ||
716 | int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, | 717 | int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, |
717 | int dw, int dh, int rotation); | 718 | int dw, int dh, int rotation); |
@@ -722,7 +723,7 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, | |||
722 | void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f); | 723 | void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f); |
723 | void fimc_set_yuv_order(struct fimc_ctx *ctx); | 724 | void fimc_set_yuv_order(struct fimc_ctx *ctx); |
724 | void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f); | 725 | void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f); |
725 | void fimc_capture_irq_handler(struct fimc_dev *fimc, bool done); | 726 | void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf); |
726 | 727 | ||
727 | int fimc_register_m2m_device(struct fimc_dev *fimc, | 728 | int fimc_register_m2m_device(struct fimc_dev *fimc, |
728 | struct v4l2_device *v4l2_dev); | 729 | struct v4l2_device *v4l2_dev); |
@@ -731,18 +732,19 @@ int fimc_register_driver(void); | |||
731 | void fimc_unregister_driver(void); | 732 | void fimc_unregister_driver(void); |
732 | 733 | ||
733 | /* -----------------------------------------------------*/ | 734 | /* -----------------------------------------------------*/ |
735 | /* fimc-m2m.c */ | ||
736 | void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state); | ||
737 | |||
738 | /* -----------------------------------------------------*/ | ||
734 | /* fimc-capture.c */ | 739 | /* fimc-capture.c */ |
735 | int fimc_register_capture_device(struct fimc_dev *fimc, | 740 | int fimc_register_capture_device(struct fimc_dev *fimc, |
736 | struct v4l2_device *v4l2_dev); | 741 | struct v4l2_device *v4l2_dev); |
737 | void fimc_unregister_capture_device(struct fimc_dev *fimc); | 742 | void fimc_unregister_capture_device(struct fimc_dev *fimc); |
738 | int fimc_capture_ctrls_create(struct fimc_dev *fimc); | 743 | int fimc_capture_ctrls_create(struct fimc_dev *fimc); |
739 | int fimc_vid_cap_buf_queue(struct fimc_dev *fimc, | ||
740 | struct fimc_vid_buffer *fimc_vb); | ||
741 | void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, | 744 | void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, |
742 | void *arg); | 745 | void *arg); |
743 | int fimc_capture_suspend(struct fimc_dev *fimc); | 746 | int fimc_capture_suspend(struct fimc_dev *fimc); |
744 | int fimc_capture_resume(struct fimc_dev *fimc); | 747 | int fimc_capture_resume(struct fimc_dev *fimc); |
745 | int fimc_capture_config_update(struct fimc_ctx *ctx); | ||
746 | 748 | ||
747 | /* Locking: the caller holds fimc->slock */ | 749 | /* Locking: the caller holds fimc->slock */ |
748 | static inline void fimc_activate_capture(struct fimc_ctx *ctx) | 750 | static inline void fimc_activate_capture(struct fimc_ctx *ctx) |
diff --git a/drivers/media/video/s5p-fimc/fimc-m2m.c b/drivers/media/video/s5p-fimc/fimc-m2m.c new file mode 100644 index 000000000000..9935cc4bd0cb --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-m2m.c | |||
@@ -0,0 +1,812 @@ | |||
1 | /* | ||
2 | * Samsung S5P/EXYNOS4 SoC series FIMC (video postprocessor) driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Samsung Electronics Co., Ltd. | ||
5 | * Sylwester Nawrocki, <s.nawrocki@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published | ||
9 | * by the Free Software Foundation, either version 2 of the License, | ||
10 | * or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/bug.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/device.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/pm_runtime.h> | ||
22 | #include <linux/list.h> | ||
23 | #include <linux/io.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/clk.h> | ||
26 | #include <media/v4l2-ioctl.h> | ||
27 | #include <media/videobuf2-core.h> | ||
28 | #include <media/videobuf2-dma-contig.h> | ||
29 | |||
30 | #include "fimc-core.h" | ||
31 | #include "fimc-mdevice.h" | ||
32 | |||
33 | |||
34 | static unsigned int get_m2m_fmt_flags(unsigned int stream_type) | ||
35 | { | ||
36 | if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | ||
37 | return FMT_FLAGS_M2M_IN; | ||
38 | else | ||
39 | return FMT_FLAGS_M2M_OUT; | ||
40 | } | ||
41 | |||
42 | void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state) | ||
43 | { | ||
44 | struct vb2_buffer *src_vb, *dst_vb; | ||
45 | |||
46 | if (!ctx || !ctx->m2m_ctx) | ||
47 | return; | ||
48 | |||
49 | src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); | ||
50 | dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); | ||
51 | |||
52 | if (src_vb && dst_vb) { | ||
53 | v4l2_m2m_buf_done(src_vb, vb_state); | ||
54 | v4l2_m2m_buf_done(dst_vb, vb_state); | ||
55 | v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev, | ||
56 | ctx->m2m_ctx); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | /* Complete the transaction which has been scheduled for execution. */ | ||
61 | static int fimc_m2m_shutdown(struct fimc_ctx *ctx) | ||
62 | { | ||
63 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
64 | int ret; | ||
65 | |||
66 | if (!fimc_m2m_pending(fimc)) | ||
67 | return 0; | ||
68 | |||
69 | fimc_ctx_state_set(FIMC_CTX_SHUT, ctx); | ||
70 | |||
71 | ret = wait_event_timeout(fimc->irq_queue, | ||
72 | !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), | ||
73 | FIMC_SHUTDOWN_TIMEOUT); | ||
74 | |||
75 | return ret == 0 ? -ETIMEDOUT : ret; | ||
76 | } | ||
77 | |||
78 | static int start_streaming(struct vb2_queue *q, unsigned int count) | ||
79 | { | ||
80 | struct fimc_ctx *ctx = q->drv_priv; | ||
81 | int ret; | ||
82 | |||
83 | ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev); | ||
84 | return ret > 0 ? 0 : ret; | ||
85 | } | ||
86 | |||
87 | static int stop_streaming(struct vb2_queue *q) | ||
88 | { | ||
89 | struct fimc_ctx *ctx = q->drv_priv; | ||
90 | int ret; | ||
91 | |||
92 | ret = fimc_m2m_shutdown(ctx); | ||
93 | if (ret == -ETIMEDOUT) | ||
94 | fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); | ||
95 | |||
96 | pm_runtime_put(&ctx->fimc_dev->pdev->dev); | ||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static void fimc_device_run(void *priv) | ||
101 | { | ||
102 | struct vb2_buffer *vb = NULL; | ||
103 | struct fimc_ctx *ctx = priv; | ||
104 | struct fimc_frame *sf, *df; | ||
105 | struct fimc_dev *fimc; | ||
106 | unsigned long flags; | ||
107 | u32 ret; | ||
108 | |||
109 | if (WARN(!ctx, "Null context\n")) | ||
110 | return; | ||
111 | |||
112 | fimc = ctx->fimc_dev; | ||
113 | spin_lock_irqsave(&fimc->slock, flags); | ||
114 | |||
115 | set_bit(ST_M2M_PEND, &fimc->state); | ||
116 | sf = &ctx->s_frame; | ||
117 | df = &ctx->d_frame; | ||
118 | |||
119 | if (ctx->state & FIMC_PARAMS) { | ||
120 | /* Prepare the DMA offsets for scaler */ | ||
121 | fimc_prepare_dma_offset(ctx, sf); | ||
122 | fimc_prepare_dma_offset(ctx, df); | ||
123 | } | ||
124 | |||
125 | vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); | ||
126 | ret = fimc_prepare_addr(ctx, vb, sf, &sf->paddr); | ||
127 | if (ret) | ||
128 | goto dma_unlock; | ||
129 | |||
130 | vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); | ||
131 | ret = fimc_prepare_addr(ctx, vb, df, &df->paddr); | ||
132 | if (ret) | ||
133 | goto dma_unlock; | ||
134 | |||
135 | /* Reconfigure hardware if the context has changed. */ | ||
136 | if (fimc->m2m.ctx != ctx) { | ||
137 | ctx->state |= FIMC_PARAMS; | ||
138 | fimc->m2m.ctx = ctx; | ||
139 | } | ||
140 | |||
141 | if (ctx->state & FIMC_PARAMS) { | ||
142 | fimc_set_yuv_order(ctx); | ||
143 | fimc_hw_set_input_path(ctx); | ||
144 | fimc_hw_set_in_dma(ctx); | ||
145 | ret = fimc_set_scaler_info(ctx); | ||
146 | if (ret) | ||
147 | goto dma_unlock; | ||
148 | fimc_hw_set_prescaler(ctx); | ||
149 | fimc_hw_set_mainscaler(ctx); | ||
150 | fimc_hw_set_target_format(ctx); | ||
151 | fimc_hw_set_rotation(ctx); | ||
152 | fimc_hw_set_effect(ctx, false); | ||
153 | fimc_hw_set_out_dma(ctx); | ||
154 | if (fimc->variant->has_alpha) | ||
155 | fimc_hw_set_rgb_alpha(ctx); | ||
156 | fimc_hw_set_output_path(ctx); | ||
157 | } | ||
158 | fimc_hw_set_input_addr(fimc, &sf->paddr); | ||
159 | fimc_hw_set_output_addr(fimc, &df->paddr, -1); | ||
160 | |||
161 | fimc_activate_capture(ctx); | ||
162 | ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP | | ||
163 | FIMC_SRC_FMT | FIMC_DST_FMT); | ||
164 | fimc_hw_activate_input_dma(fimc, true); | ||
165 | |||
166 | dma_unlock: | ||
167 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
168 | } | ||
169 | |||
170 | static void fimc_job_abort(void *priv) | ||
171 | { | ||
172 | fimc_m2m_shutdown(priv); | ||
173 | } | ||
174 | |||
175 | static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, | ||
176 | unsigned int *num_buffers, unsigned int *num_planes, | ||
177 | unsigned int sizes[], void *allocators[]) | ||
178 | { | ||
179 | struct fimc_ctx *ctx = vb2_get_drv_priv(vq); | ||
180 | struct fimc_frame *f; | ||
181 | int i; | ||
182 | |||
183 | f = ctx_get_frame(ctx, vq->type); | ||
184 | if (IS_ERR(f)) | ||
185 | return PTR_ERR(f); | ||
186 | /* | ||
187 | * Return number of non-contigous planes (plane buffers) | ||
188 | * depending on the configured color format. | ||
189 | */ | ||
190 | if (!f->fmt) | ||
191 | return -EINVAL; | ||
192 | |||
193 | *num_planes = f->fmt->memplanes; | ||
194 | for (i = 0; i < f->fmt->memplanes; i++) { | ||
195 | sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8; | ||
196 | allocators[i] = ctx->fimc_dev->alloc_ctx; | ||
197 | } | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static int fimc_buf_prepare(struct vb2_buffer *vb) | ||
202 | { | ||
203 | struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); | ||
204 | struct fimc_frame *frame; | ||
205 | int i; | ||
206 | |||
207 | frame = ctx_get_frame(ctx, vb->vb2_queue->type); | ||
208 | if (IS_ERR(frame)) | ||
209 | return PTR_ERR(frame); | ||
210 | |||
211 | for (i = 0; i < frame->fmt->memplanes; i++) | ||
212 | vb2_set_plane_payload(vb, i, frame->payload[i]); | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static void fimc_buf_queue(struct vb2_buffer *vb) | ||
218 | { | ||
219 | struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); | ||
220 | |||
221 | dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); | ||
222 | |||
223 | if (ctx->m2m_ctx) | ||
224 | v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); | ||
225 | } | ||
226 | |||
227 | static void fimc_lock(struct vb2_queue *vq) | ||
228 | { | ||
229 | struct fimc_ctx *ctx = vb2_get_drv_priv(vq); | ||
230 | mutex_lock(&ctx->fimc_dev->lock); | ||
231 | } | ||
232 | |||
233 | static void fimc_unlock(struct vb2_queue *vq) | ||
234 | { | ||
235 | struct fimc_ctx *ctx = vb2_get_drv_priv(vq); | ||
236 | mutex_unlock(&ctx->fimc_dev->lock); | ||
237 | } | ||
238 | |||
239 | static struct vb2_ops fimc_qops = { | ||
240 | .queue_setup = fimc_queue_setup, | ||
241 | .buf_prepare = fimc_buf_prepare, | ||
242 | .buf_queue = fimc_buf_queue, | ||
243 | .wait_prepare = fimc_unlock, | ||
244 | .wait_finish = fimc_lock, | ||
245 | .stop_streaming = stop_streaming, | ||
246 | .start_streaming = start_streaming, | ||
247 | }; | ||
248 | |||
249 | /* | ||
250 | * V4L2 ioctl handlers | ||
251 | */ | ||
252 | static int fimc_m2m_querycap(struct file *file, void *fh, | ||
253 | struct v4l2_capability *cap) | ||
254 | { | ||
255 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
256 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
257 | |||
258 | strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); | ||
259 | strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); | ||
260 | cap->bus_info[0] = 0; | ||
261 | cap->capabilities = V4L2_CAP_STREAMING | | ||
262 | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv, | ||
268 | struct v4l2_fmtdesc *f) | ||
269 | { | ||
270 | struct fimc_fmt *fmt; | ||
271 | |||
272 | fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type), | ||
273 | f->index); | ||
274 | if (!fmt) | ||
275 | return -EINVAL; | ||
276 | |||
277 | strncpy(f->description, fmt->name, sizeof(f->description) - 1); | ||
278 | f->pixelformat = fmt->fourcc; | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, | ||
283 | struct v4l2_format *f) | ||
284 | { | ||
285 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
286 | struct fimc_frame *frame = ctx_get_frame(ctx, f->type); | ||
287 | |||
288 | if (IS_ERR(frame)) | ||
289 | return PTR_ERR(frame); | ||
290 | |||
291 | return fimc_fill_format(frame, f); | ||
292 | } | ||
293 | |||
294 | static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) | ||
295 | { | ||
296 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
297 | struct samsung_fimc_variant *variant = fimc->variant; | ||
298 | struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; | ||
299 | struct fimc_fmt *fmt; | ||
300 | u32 max_w, mod_x, mod_y; | ||
301 | |||
302 | if (!IS_M2M(f->type)) | ||
303 | return -EINVAL; | ||
304 | |||
305 | dbg("w: %d, h: %d", pix->width, pix->height); | ||
306 | |||
307 | fmt = fimc_find_format(&pix->pixelformat, NULL, | ||
308 | get_m2m_fmt_flags(f->type), 0); | ||
309 | if (WARN(fmt == NULL, "Pixel format lookup failed")) | ||
310 | return -EINVAL; | ||
311 | |||
312 | if (pix->field == V4L2_FIELD_ANY) | ||
313 | pix->field = V4L2_FIELD_NONE; | ||
314 | else if (pix->field != V4L2_FIELD_NONE) | ||
315 | return -EINVAL; | ||
316 | |||
317 | if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { | ||
318 | max_w = variant->pix_limit->scaler_dis_w; | ||
319 | mod_x = ffs(variant->min_inp_pixsize) - 1; | ||
320 | } else { | ||
321 | max_w = variant->pix_limit->out_rot_dis_w; | ||
322 | mod_x = ffs(variant->min_out_pixsize) - 1; | ||
323 | } | ||
324 | |||
325 | if (tiled_fmt(fmt)) { | ||
326 | mod_x = 6; /* 64 x 32 pixels tile */ | ||
327 | mod_y = 5; | ||
328 | } else { | ||
329 | if (variant->min_vsize_align == 1) | ||
330 | mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1; | ||
331 | else | ||
332 | mod_y = ffs(variant->min_vsize_align) - 1; | ||
333 | } | ||
334 | |||
335 | v4l_bound_align_image(&pix->width, 16, max_w, mod_x, | ||
336 | &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0); | ||
337 | |||
338 | fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp); | ||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh, | ||
343 | struct v4l2_format *f) | ||
344 | { | ||
345 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
346 | |||
347 | return fimc_try_fmt_mplane(ctx, f); | ||
348 | } | ||
349 | |||
350 | static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, | ||
351 | struct v4l2_format *f) | ||
352 | { | ||
353 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
354 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
355 | struct vb2_queue *vq; | ||
356 | struct fimc_frame *frame; | ||
357 | struct v4l2_pix_format_mplane *pix; | ||
358 | int i, ret = 0; | ||
359 | |||
360 | ret = fimc_try_fmt_mplane(ctx, f); | ||
361 | if (ret) | ||
362 | return ret; | ||
363 | |||
364 | vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); | ||
365 | |||
366 | if (vb2_is_busy(vq)) { | ||
367 | v4l2_err(fimc->m2m.vfd, "queue (%d) busy\n", f->type); | ||
368 | return -EBUSY; | ||
369 | } | ||
370 | |||
371 | if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | ||
372 | frame = &ctx->s_frame; | ||
373 | else | ||
374 | frame = &ctx->d_frame; | ||
375 | |||
376 | pix = &f->fmt.pix_mp; | ||
377 | frame->fmt = fimc_find_format(&pix->pixelformat, NULL, | ||
378 | get_m2m_fmt_flags(f->type), 0); | ||
379 | if (!frame->fmt) | ||
380 | return -EINVAL; | ||
381 | |||
382 | /* Update RGB Alpha control state and value range */ | ||
383 | fimc_alpha_ctrl_update(ctx); | ||
384 | |||
385 | for (i = 0; i < frame->fmt->colplanes; i++) { | ||
386 | frame->payload[i] = | ||
387 | (pix->width * pix->height * frame->fmt->depth[i]) / 8; | ||
388 | } | ||
389 | |||
390 | fimc_fill_frame(frame, f); | ||
391 | |||
392 | ctx->scaler.enabled = 1; | ||
393 | |||
394 | if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | ||
395 | fimc_ctx_state_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); | ||
396 | else | ||
397 | fimc_ctx_state_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); | ||
398 | |||
399 | dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height); | ||
400 | |||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static int fimc_m2m_reqbufs(struct file *file, void *fh, | ||
405 | struct v4l2_requestbuffers *reqbufs) | ||
406 | { | ||
407 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
408 | |||
409 | return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); | ||
410 | } | ||
411 | |||
412 | static int fimc_m2m_querybuf(struct file *file, void *fh, | ||
413 | struct v4l2_buffer *buf) | ||
414 | { | ||
415 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
416 | |||
417 | return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); | ||
418 | } | ||
419 | |||
420 | static int fimc_m2m_qbuf(struct file *file, void *fh, | ||
421 | struct v4l2_buffer *buf) | ||
422 | { | ||
423 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
424 | |||
425 | return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); | ||
426 | } | ||
427 | |||
428 | static int fimc_m2m_dqbuf(struct file *file, void *fh, | ||
429 | struct v4l2_buffer *buf) | ||
430 | { | ||
431 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
432 | |||
433 | return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); | ||
434 | } | ||
435 | |||
436 | static int fimc_m2m_streamon(struct file *file, void *fh, | ||
437 | enum v4l2_buf_type type) | ||
438 | { | ||
439 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
440 | |||
441 | /* The source and target color format need to be set */ | ||
442 | if (V4L2_TYPE_IS_OUTPUT(type)) { | ||
443 | if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx)) | ||
444 | return -EINVAL; | ||
445 | } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) { | ||
446 | return -EINVAL; | ||
447 | } | ||
448 | |||
449 | return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); | ||
450 | } | ||
451 | |||
452 | static int fimc_m2m_streamoff(struct file *file, void *fh, | ||
453 | enum v4l2_buf_type type) | ||
454 | { | ||
455 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
456 | |||
457 | return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); | ||
458 | } | ||
459 | |||
460 | static int fimc_m2m_cropcap(struct file *file, void *fh, | ||
461 | struct v4l2_cropcap *cr) | ||
462 | { | ||
463 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
464 | struct fimc_frame *frame; | ||
465 | |||
466 | frame = ctx_get_frame(ctx, cr->type); | ||
467 | if (IS_ERR(frame)) | ||
468 | return PTR_ERR(frame); | ||
469 | |||
470 | cr->bounds.left = 0; | ||
471 | cr->bounds.top = 0; | ||
472 | cr->bounds.width = frame->o_width; | ||
473 | cr->bounds.height = frame->o_height; | ||
474 | cr->defrect = cr->bounds; | ||
475 | |||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) | ||
480 | { | ||
481 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
482 | struct fimc_frame *frame; | ||
483 | |||
484 | frame = ctx_get_frame(ctx, cr->type); | ||
485 | if (IS_ERR(frame)) | ||
486 | return PTR_ERR(frame); | ||
487 | |||
488 | cr->c.left = frame->offs_h; | ||
489 | cr->c.top = frame->offs_v; | ||
490 | cr->c.width = frame->width; | ||
491 | cr->c.height = frame->height; | ||
492 | |||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) | ||
497 | { | ||
498 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
499 | struct fimc_frame *f; | ||
500 | u32 min_size, halign, depth = 0; | ||
501 | int i; | ||
502 | |||
503 | if (cr->c.top < 0 || cr->c.left < 0) { | ||
504 | v4l2_err(fimc->m2m.vfd, | ||
505 | "doesn't support negative values for top & left\n"); | ||
506 | return -EINVAL; | ||
507 | } | ||
508 | if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | ||
509 | f = &ctx->d_frame; | ||
510 | else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | ||
511 | f = &ctx->s_frame; | ||
512 | else | ||
513 | return -EINVAL; | ||
514 | |||
515 | min_size = (f == &ctx->s_frame) ? | ||
516 | fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize; | ||
517 | |||
518 | /* Get pixel alignment constraints. */ | ||
519 | if (fimc->variant->min_vsize_align == 1) | ||
520 | halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1; | ||
521 | else | ||
522 | halign = ffs(fimc->variant->min_vsize_align) - 1; | ||
523 | |||
524 | for (i = 0; i < f->fmt->colplanes; i++) | ||
525 | depth += f->fmt->depth[i]; | ||
526 | |||
527 | v4l_bound_align_image(&cr->c.width, min_size, f->o_width, | ||
528 | ffs(min_size) - 1, | ||
529 | &cr->c.height, min_size, f->o_height, | ||
530 | halign, 64/(ALIGN(depth, 8))); | ||
531 | |||
532 | /* adjust left/top if cropping rectangle is out of bounds */ | ||
533 | if (cr->c.left + cr->c.width > f->o_width) | ||
534 | cr->c.left = f->o_width - cr->c.width; | ||
535 | if (cr->c.top + cr->c.height > f->o_height) | ||
536 | cr->c.top = f->o_height - cr->c.height; | ||
537 | |||
538 | cr->c.left = round_down(cr->c.left, min_size); | ||
539 | cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align); | ||
540 | |||
541 | dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", | ||
542 | cr->c.left, cr->c.top, cr->c.width, cr->c.height, | ||
543 | f->f_width, f->f_height); | ||
544 | |||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) | ||
549 | { | ||
550 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
551 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
552 | struct fimc_frame *f; | ||
553 | int ret; | ||
554 | |||
555 | ret = fimc_m2m_try_crop(ctx, cr); | ||
556 | if (ret) | ||
557 | return ret; | ||
558 | |||
559 | f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? | ||
560 | &ctx->s_frame : &ctx->d_frame; | ||
561 | |||
562 | /* Check to see if scaling ratio is within supported range */ | ||
563 | if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) { | ||
564 | if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { | ||
565 | ret = fimc_check_scaler_ratio(ctx, cr->c.width, | ||
566 | cr->c.height, ctx->d_frame.width, | ||
567 | ctx->d_frame.height, ctx->rotation); | ||
568 | } else { | ||
569 | ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, | ||
570 | ctx->s_frame.height, cr->c.width, | ||
571 | cr->c.height, ctx->rotation); | ||
572 | } | ||
573 | if (ret) { | ||
574 | v4l2_err(fimc->m2m.vfd, "Out of scaler range\n"); | ||
575 | return -EINVAL; | ||
576 | } | ||
577 | } | ||
578 | |||
579 | f->offs_h = cr->c.left; | ||
580 | f->offs_v = cr->c.top; | ||
581 | f->width = cr->c.width; | ||
582 | f->height = cr->c.height; | ||
583 | |||
584 | fimc_ctx_state_set(FIMC_PARAMS, ctx); | ||
585 | |||
586 | return 0; | ||
587 | } | ||
588 | |||
589 | static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { | ||
590 | .vidioc_querycap = fimc_m2m_querycap, | ||
591 | .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane, | ||
592 | .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane, | ||
593 | .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane, | ||
594 | .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane, | ||
595 | .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane, | ||
596 | .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane, | ||
597 | .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane, | ||
598 | .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane, | ||
599 | .vidioc_reqbufs = fimc_m2m_reqbufs, | ||
600 | .vidioc_querybuf = fimc_m2m_querybuf, | ||
601 | .vidioc_qbuf = fimc_m2m_qbuf, | ||
602 | .vidioc_dqbuf = fimc_m2m_dqbuf, | ||
603 | .vidioc_streamon = fimc_m2m_streamon, | ||
604 | .vidioc_streamoff = fimc_m2m_streamoff, | ||
605 | .vidioc_g_crop = fimc_m2m_g_crop, | ||
606 | .vidioc_s_crop = fimc_m2m_s_crop, | ||
607 | .vidioc_cropcap = fimc_m2m_cropcap | ||
608 | |||
609 | }; | ||
610 | |||
611 | static int queue_init(void *priv, struct vb2_queue *src_vq, | ||
612 | struct vb2_queue *dst_vq) | ||
613 | { | ||
614 | struct fimc_ctx *ctx = priv; | ||
615 | int ret; | ||
616 | |||
617 | memset(src_vq, 0, sizeof(*src_vq)); | ||
618 | src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | ||
619 | src_vq->io_modes = VB2_MMAP | VB2_USERPTR; | ||
620 | src_vq->drv_priv = ctx; | ||
621 | src_vq->ops = &fimc_qops; | ||
622 | src_vq->mem_ops = &vb2_dma_contig_memops; | ||
623 | src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); | ||
624 | |||
625 | ret = vb2_queue_init(src_vq); | ||
626 | if (ret) | ||
627 | return ret; | ||
628 | |||
629 | memset(dst_vq, 0, sizeof(*dst_vq)); | ||
630 | dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | ||
631 | dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; | ||
632 | dst_vq->drv_priv = ctx; | ||
633 | dst_vq->ops = &fimc_qops; | ||
634 | dst_vq->mem_ops = &vb2_dma_contig_memops; | ||
635 | dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); | ||
636 | |||
637 | return vb2_queue_init(dst_vq); | ||
638 | } | ||
639 | |||
640 | static int fimc_m2m_open(struct file *file) | ||
641 | { | ||
642 | struct fimc_dev *fimc = video_drvdata(file); | ||
643 | struct fimc_ctx *ctx; | ||
644 | int ret; | ||
645 | |||
646 | dbg("pid: %d, state: 0x%lx, refcnt: %d", | ||
647 | task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt); | ||
648 | |||
649 | /* | ||
650 | * Return if the corresponding video capture node | ||
651 | * is already opened. | ||
652 | */ | ||
653 | if (fimc->vid_cap.refcnt > 0) | ||
654 | return -EBUSY; | ||
655 | |||
656 | ctx = kzalloc(sizeof *ctx, GFP_KERNEL); | ||
657 | if (!ctx) | ||
658 | return -ENOMEM; | ||
659 | v4l2_fh_init(&ctx->fh, fimc->m2m.vfd); | ||
660 | ctx->fimc_dev = fimc; | ||
661 | |||
662 | /* Default color format */ | ||
663 | ctx->s_frame.fmt = fimc_get_format(0); | ||
664 | ctx->d_frame.fmt = fimc_get_format(0); | ||
665 | |||
666 | ret = fimc_ctrls_create(ctx); | ||
667 | if (ret) | ||
668 | goto error_fh; | ||
669 | |||
670 | /* Use separate control handler per file handle */ | ||
671 | ctx->fh.ctrl_handler = &ctx->ctrl_handler; | ||
672 | file->private_data = &ctx->fh; | ||
673 | v4l2_fh_add(&ctx->fh); | ||
674 | |||
675 | /* Setup the device context for memory-to-memory mode */ | ||
676 | ctx->state = FIMC_CTX_M2M; | ||
677 | ctx->flags = 0; | ||
678 | ctx->in_path = FIMC_DMA; | ||
679 | ctx->out_path = FIMC_DMA; | ||
680 | |||
681 | ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); | ||
682 | if (IS_ERR(ctx->m2m_ctx)) { | ||
683 | ret = PTR_ERR(ctx->m2m_ctx); | ||
684 | goto error_c; | ||
685 | } | ||
686 | |||
687 | if (fimc->m2m.refcnt++ == 0) | ||
688 | set_bit(ST_M2M_RUN, &fimc->state); | ||
689 | return 0; | ||
690 | |||
691 | error_c: | ||
692 | fimc_ctrls_delete(ctx); | ||
693 | error_fh: | ||
694 | v4l2_fh_del(&ctx->fh); | ||
695 | v4l2_fh_exit(&ctx->fh); | ||
696 | kfree(ctx); | ||
697 | return ret; | ||
698 | } | ||
699 | |||
700 | static int fimc_m2m_release(struct file *file) | ||
701 | { | ||
702 | struct fimc_ctx *ctx = fh_to_ctx(file->private_data); | ||
703 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
704 | |||
705 | dbg("pid: %d, state: 0x%lx, refcnt= %d", | ||
706 | task_pid_nr(current), fimc->state, fimc->m2m.refcnt); | ||
707 | |||
708 | v4l2_m2m_ctx_release(ctx->m2m_ctx); | ||
709 | fimc_ctrls_delete(ctx); | ||
710 | v4l2_fh_del(&ctx->fh); | ||
711 | v4l2_fh_exit(&ctx->fh); | ||
712 | |||
713 | if (--fimc->m2m.refcnt <= 0) | ||
714 | clear_bit(ST_M2M_RUN, &fimc->state); | ||
715 | kfree(ctx); | ||
716 | return 0; | ||
717 | } | ||
718 | |||
719 | static unsigned int fimc_m2m_poll(struct file *file, | ||
720 | struct poll_table_struct *wait) | ||
721 | { | ||
722 | struct fimc_ctx *ctx = fh_to_ctx(file->private_data); | ||
723 | |||
724 | return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); | ||
725 | } | ||
726 | |||
727 | |||
728 | static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma) | ||
729 | { | ||
730 | struct fimc_ctx *ctx = fh_to_ctx(file->private_data); | ||
731 | |||
732 | return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); | ||
733 | } | ||
734 | |||
735 | static const struct v4l2_file_operations fimc_m2m_fops = { | ||
736 | .owner = THIS_MODULE, | ||
737 | .open = fimc_m2m_open, | ||
738 | .release = fimc_m2m_release, | ||
739 | .poll = fimc_m2m_poll, | ||
740 | .unlocked_ioctl = video_ioctl2, | ||
741 | .mmap = fimc_m2m_mmap, | ||
742 | }; | ||
743 | |||
744 | static struct v4l2_m2m_ops m2m_ops = { | ||
745 | .device_run = fimc_device_run, | ||
746 | .job_abort = fimc_job_abort, | ||
747 | }; | ||
748 | |||
749 | int fimc_register_m2m_device(struct fimc_dev *fimc, | ||
750 | struct v4l2_device *v4l2_dev) | ||
751 | { | ||
752 | struct video_device *vfd; | ||
753 | struct platform_device *pdev; | ||
754 | int ret = 0; | ||
755 | |||
756 | if (!fimc) | ||
757 | return -ENODEV; | ||
758 | |||
759 | pdev = fimc->pdev; | ||
760 | fimc->v4l2_dev = v4l2_dev; | ||
761 | |||
762 | vfd = video_device_alloc(); | ||
763 | if (!vfd) { | ||
764 | v4l2_err(v4l2_dev, "Failed to allocate video device\n"); | ||
765 | return -ENOMEM; | ||
766 | } | ||
767 | |||
768 | vfd->fops = &fimc_m2m_fops; | ||
769 | vfd->ioctl_ops = &fimc_m2m_ioctl_ops; | ||
770 | vfd->v4l2_dev = v4l2_dev; | ||
771 | vfd->minor = -1; | ||
772 | vfd->release = video_device_release; | ||
773 | vfd->lock = &fimc->lock; | ||
774 | /* Locking in file operations other than ioctl should be done | ||
775 | by the driver, not the V4L2 core. | ||
776 | This driver needs auditing so that this flag can be removed. */ | ||
777 | set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); | ||
778 | |||
779 | snprintf(vfd->name, sizeof(vfd->name), "%s.m2m", dev_name(&pdev->dev)); | ||
780 | video_set_drvdata(vfd, fimc); | ||
781 | |||
782 | fimc->m2m.vfd = vfd; | ||
783 | fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops); | ||
784 | if (IS_ERR(fimc->m2m.m2m_dev)) { | ||
785 | v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n"); | ||
786 | ret = PTR_ERR(fimc->m2m.m2m_dev); | ||
787 | goto err_init; | ||
788 | } | ||
789 | |||
790 | ret = media_entity_init(&vfd->entity, 0, NULL, 0); | ||
791 | if (!ret) | ||
792 | return 0; | ||
793 | |||
794 | v4l2_m2m_release(fimc->m2m.m2m_dev); | ||
795 | err_init: | ||
796 | video_device_release(fimc->m2m.vfd); | ||
797 | return ret; | ||
798 | } | ||
799 | |||
800 | void fimc_unregister_m2m_device(struct fimc_dev *fimc) | ||
801 | { | ||
802 | if (!fimc) | ||
803 | return; | ||
804 | |||
805 | if (fimc->m2m.m2m_dev) | ||
806 | v4l2_m2m_release(fimc->m2m.m2m_dev); | ||
807 | if (fimc->m2m.vfd) { | ||
808 | media_entity_cleanup(&fimc->m2m.vfd->entity); | ||
809 | /* Can also be called if video device wasn't registered */ | ||
810 | video_unregister_device(fimc->m2m.vfd); | ||
811 | } | ||
812 | } | ||