2 * Samsung S5P/EXYNOS4 SoC series FIMC (CAMIF) driver
4 * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd.
5 * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com>
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.
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>
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>
30 #include "fimc-core.h"
31 #include "fimc-mdevice.h"
33 static char *fimc_clocks
[MAX_FIMC_CLOCKS
] = {
37 static struct fimc_fmt fimc_formats
[] = {
40 .fourcc
= V4L2_PIX_FMT_RGB565
,
42 .color
= S5P_FIMC_RGB565
,
45 .flags
= FMT_FLAGS_M2M
,
48 .fourcc
= V4L2_PIX_FMT_BGR666
,
50 .color
= S5P_FIMC_RGB666
,
53 .flags
= FMT_FLAGS_M2M
,
55 .name
= "ARGB8888, 32 bpp",
56 .fourcc
= V4L2_PIX_FMT_RGB32
,
58 .color
= S5P_FIMC_RGB888
,
61 .flags
= FMT_FLAGS_M2M
| FMT_HAS_ALPHA
,
64 .fourcc
= V4L2_PIX_FMT_RGB555
,
66 .color
= S5P_FIMC_RGB555
,
69 .flags
= FMT_FLAGS_M2M_OUT
| FMT_HAS_ALPHA
,
72 .fourcc
= V4L2_PIX_FMT_RGB444
,
74 .color
= S5P_FIMC_RGB444
,
77 .flags
= FMT_FLAGS_M2M_OUT
| FMT_HAS_ALPHA
,
79 .name
= "YUV 4:2:2 packed, YCbYCr",
80 .fourcc
= V4L2_PIX_FMT_YUYV
,
82 .color
= S5P_FIMC_YCBYCR422
,
85 .mbus_code
= V4L2_MBUS_FMT_YUYV8_2X8
,
86 .flags
= FMT_FLAGS_M2M
| FMT_FLAGS_CAM
,
88 .name
= "YUV 4:2:2 packed, CbYCrY",
89 .fourcc
= V4L2_PIX_FMT_UYVY
,
91 .color
= S5P_FIMC_CBYCRY422
,
94 .mbus_code
= V4L2_MBUS_FMT_UYVY8_2X8
,
95 .flags
= FMT_FLAGS_M2M
| FMT_FLAGS_CAM
,
97 .name
= "YUV 4:2:2 packed, CrYCbY",
98 .fourcc
= V4L2_PIX_FMT_VYUY
,
100 .color
= S5P_FIMC_CRYCBY422
,
103 .mbus_code
= V4L2_MBUS_FMT_VYUY8_2X8
,
104 .flags
= FMT_FLAGS_M2M
| FMT_FLAGS_CAM
,
106 .name
= "YUV 4:2:2 packed, YCrYCb",
107 .fourcc
= V4L2_PIX_FMT_YVYU
,
109 .color
= S5P_FIMC_YCRYCB422
,
112 .mbus_code
= V4L2_MBUS_FMT_YVYU8_2X8
,
113 .flags
= FMT_FLAGS_M2M
| FMT_FLAGS_CAM
,
115 .name
= "YUV 4:2:2 planar, Y/Cb/Cr",
116 .fourcc
= V4L2_PIX_FMT_YUV422P
,
118 .color
= S5P_FIMC_YCBYCR422
,
121 .flags
= FMT_FLAGS_M2M
,
123 .name
= "YUV 4:2:2 planar, Y/CbCr",
124 .fourcc
= V4L2_PIX_FMT_NV16
,
126 .color
= S5P_FIMC_YCBYCR422
,
129 .flags
= FMT_FLAGS_M2M
,
131 .name
= "YUV 4:2:2 planar, Y/CrCb",
132 .fourcc
= V4L2_PIX_FMT_NV61
,
134 .color
= S5P_FIMC_YCRYCB422
,
137 .flags
= FMT_FLAGS_M2M
,
139 .name
= "YUV 4:2:0 planar, YCbCr",
140 .fourcc
= V4L2_PIX_FMT_YUV420
,
142 .color
= S5P_FIMC_YCBCR420
,
145 .flags
= FMT_FLAGS_M2M
,
147 .name
= "YUV 4:2:0 planar, Y/CbCr",
148 .fourcc
= V4L2_PIX_FMT_NV12
,
150 .color
= S5P_FIMC_YCBCR420
,
153 .flags
= FMT_FLAGS_M2M
,
155 .name
= "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr",
156 .fourcc
= V4L2_PIX_FMT_NV12M
,
157 .color
= S5P_FIMC_YCBCR420
,
161 .flags
= FMT_FLAGS_M2M
,
163 .name
= "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr",
164 .fourcc
= V4L2_PIX_FMT_YUV420M
,
165 .color
= S5P_FIMC_YCBCR420
,
166 .depth
= { 8, 2, 2 },
169 .flags
= FMT_FLAGS_M2M
,
171 .name
= "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr, tiled",
172 .fourcc
= V4L2_PIX_FMT_NV12MT
,
173 .color
= S5P_FIMC_YCBCR420
,
177 .flags
= FMT_FLAGS_M2M
,
179 .name
= "JPEG encoded data",
180 .fourcc
= V4L2_PIX_FMT_JPEG
,
181 .color
= S5P_FIMC_JPEG
,
185 .mbus_code
= V4L2_MBUS_FMT_JPEG_1X8
,
186 .flags
= FMT_FLAGS_CAM
,
190 struct fimc_fmt
* fimc_get_format(unsigned int index
)
192 if (index
>= ARRAY_SIZE(fimc_formats
))
195 return &fimc_formats
[index
];
198 int fimc_check_scaler_ratio(struct fimc_ctx
*ctx
, int sw
, int sh
,
199 int dw
, int dh
, int rotation
)
201 if (rotation
== 90 || rotation
== 270)
204 if (!ctx
->scaler
.enabled
)
205 return (sw
== dw
&& sh
== dh
) ? 0 : -EINVAL
;
207 if ((sw
>= SCALER_MAX_HRATIO
* dw
) || (sh
>= SCALER_MAX_VRATIO
* dh
))
213 static int fimc_get_scaler_factor(u32 src
, u32 tar
, u32
*ratio
, u32
*shift
)
222 if (src
>= tar
* tmp
) {
223 *shift
= sh
, *ratio
= tmp
;
227 *shift
= 0, *ratio
= 1;
231 int fimc_set_scaler_info(struct fimc_ctx
*ctx
)
233 struct samsung_fimc_variant
*variant
= ctx
->fimc_dev
->variant
;
234 struct device
*dev
= &ctx
->fimc_dev
->pdev
->dev
;
235 struct fimc_scaler
*sc
= &ctx
->scaler
;
236 struct fimc_frame
*s_frame
= &ctx
->s_frame
;
237 struct fimc_frame
*d_frame
= &ctx
->d_frame
;
241 if (ctx
->rotation
== 90 || ctx
->rotation
== 270) {
243 tx
= d_frame
->height
;
246 ty
= d_frame
->height
;
248 if (tx
<= 0 || ty
<= 0) {
249 dev_err(dev
, "Invalid target size: %dx%d", tx
, ty
);
254 sy
= s_frame
->height
;
255 if (sx
<= 0 || sy
<= 0) {
256 dev_err(dev
, "Invalid source size: %dx%d", sx
, sy
);
260 sc
->real_height
= sy
;
262 ret
= fimc_get_scaler_factor(sx
, tx
, &sc
->pre_hratio
, &sc
->hfactor
);
266 ret
= fimc_get_scaler_factor(sy
, ty
, &sc
->pre_vratio
, &sc
->vfactor
);
270 sc
->pre_dst_width
= sx
/ sc
->pre_hratio
;
271 sc
->pre_dst_height
= sy
/ sc
->pre_vratio
;
273 if (variant
->has_mainscaler_ext
) {
274 sc
->main_hratio
= (sx
<< 14) / (tx
<< sc
->hfactor
);
275 sc
->main_vratio
= (sy
<< 14) / (ty
<< sc
->vfactor
);
277 sc
->main_hratio
= (sx
<< 8) / (tx
<< sc
->hfactor
);
278 sc
->main_vratio
= (sy
<< 8) / (ty
<< sc
->vfactor
);
282 sc
->scaleup_h
= (tx
>= sx
) ? 1 : 0;
283 sc
->scaleup_v
= (ty
>= sy
) ? 1 : 0;
285 /* check to see if input and output size/format differ */
286 if (s_frame
->fmt
->color
== d_frame
->fmt
->color
287 && s_frame
->width
== d_frame
->width
288 && s_frame
->height
== d_frame
->height
)
296 static irqreturn_t
fimc_irq_handler(int irq
, void *priv
)
298 struct fimc_dev
*fimc
= priv
;
299 struct fimc_ctx
*ctx
;
301 fimc_hw_clear_irq(fimc
);
303 spin_lock(&fimc
->slock
);
305 if (test_and_clear_bit(ST_M2M_PEND
, &fimc
->state
)) {
306 if (test_and_clear_bit(ST_M2M_SUSPENDING
, &fimc
->state
)) {
307 set_bit(ST_M2M_SUSPENDED
, &fimc
->state
);
308 wake_up(&fimc
->irq_queue
);
311 ctx
= v4l2_m2m_get_curr_priv(fimc
->m2m
.m2m_dev
);
313 spin_unlock(&fimc
->slock
);
314 fimc_m2m_job_finish(ctx
, VB2_BUF_STATE_DONE
);
316 if (ctx
->state
& FIMC_CTX_SHUT
) {
317 ctx
->state
&= ~FIMC_CTX_SHUT
;
318 wake_up(&fimc
->irq_queue
);
322 } else if (test_bit(ST_CAPT_PEND
, &fimc
->state
)) {
323 int last_buf
= test_bit(ST_CAPT_JPEG
, &fimc
->state
) &&
324 fimc
->vid_cap
.reqbufs_count
== 1;
325 fimc_capture_irq_handler(fimc
, !last_buf
);
328 spin_unlock(&fimc
->slock
);
332 /* The color format (colplanes, memplanes) must be already configured. */
333 int fimc_prepare_addr(struct fimc_ctx
*ctx
, struct vb2_buffer
*vb
,
334 struct fimc_frame
*frame
, struct fimc_addr
*paddr
)
339 if (vb
== NULL
|| frame
== NULL
)
342 pix_size
= frame
->width
* frame
->height
;
344 dbg("memplanes= %d, colplanes= %d, pix_size= %d",
345 frame
->fmt
->memplanes
, frame
->fmt
->colplanes
, pix_size
);
347 paddr
->y
= vb2_dma_contig_plane_dma_addr(vb
, 0);
349 if (frame
->fmt
->memplanes
== 1) {
350 switch (frame
->fmt
->colplanes
) {
356 /* decompose Y into Y/Cb */
357 paddr
->cb
= (u32
)(paddr
->y
+ pix_size
);
361 paddr
->cb
= (u32
)(paddr
->y
+ pix_size
);
362 /* decompose Y into Y/Cb/Cr */
363 if (S5P_FIMC_YCBCR420
== frame
->fmt
->color
)
364 paddr
->cr
= (u32
)(paddr
->cb
367 paddr
->cr
= (u32
)(paddr
->cb
374 if (frame
->fmt
->memplanes
>= 2)
375 paddr
->cb
= vb2_dma_contig_plane_dma_addr(vb
, 1);
377 if (frame
->fmt
->memplanes
== 3)
378 paddr
->cr
= vb2_dma_contig_plane_dma_addr(vb
, 2);
381 dbg("PHYS_ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d",
382 paddr
->y
, paddr
->cb
, paddr
->cr
, ret
);
387 /* Set order for 1 and 2 plane YCBCR 4:2:2 formats. */
388 void fimc_set_yuv_order(struct fimc_ctx
*ctx
)
390 /* The one only mode supported in SoC. */
391 ctx
->in_order_2p
= S5P_FIMC_LSB_CRCB
;
392 ctx
->out_order_2p
= S5P_FIMC_LSB_CRCB
;
394 /* Set order for 1 plane input formats. */
395 switch (ctx
->s_frame
.fmt
->color
) {
396 case S5P_FIMC_YCRYCB422
:
397 ctx
->in_order_1p
= S5P_MSCTRL_ORDER422_CBYCRY
;
399 case S5P_FIMC_CBYCRY422
:
400 ctx
->in_order_1p
= S5P_MSCTRL_ORDER422_YCRYCB
;
402 case S5P_FIMC_CRYCBY422
:
403 ctx
->in_order_1p
= S5P_MSCTRL_ORDER422_YCBYCR
;
405 case S5P_FIMC_YCBYCR422
:
407 ctx
->in_order_1p
= S5P_MSCTRL_ORDER422_CRYCBY
;
410 dbg("ctx->in_order_1p= %d", ctx
->in_order_1p
);
412 switch (ctx
->d_frame
.fmt
->color
) {
413 case S5P_FIMC_YCRYCB422
:
414 ctx
->out_order_1p
= S5P_CIOCTRL_ORDER422_CBYCRY
;
416 case S5P_FIMC_CBYCRY422
:
417 ctx
->out_order_1p
= S5P_CIOCTRL_ORDER422_YCRYCB
;
419 case S5P_FIMC_CRYCBY422
:
420 ctx
->out_order_1p
= S5P_CIOCTRL_ORDER422_YCBYCR
;
422 case S5P_FIMC_YCBYCR422
:
424 ctx
->out_order_1p
= S5P_CIOCTRL_ORDER422_CRYCBY
;
427 dbg("ctx->out_order_1p= %d", ctx
->out_order_1p
);
430 void fimc_prepare_dma_offset(struct fimc_ctx
*ctx
, struct fimc_frame
*f
)
432 struct samsung_fimc_variant
*variant
= ctx
->fimc_dev
->variant
;
435 for (i
= 0; i
< f
->fmt
->colplanes
; i
++)
436 depth
+= f
->fmt
->depth
[i
];
438 f
->dma_offset
.y_h
= f
->offs_h
;
439 if (!variant
->pix_hoff
)
440 f
->dma_offset
.y_h
*= (depth
>> 3);
442 f
->dma_offset
.y_v
= f
->offs_v
;
444 f
->dma_offset
.cb_h
= f
->offs_h
;
445 f
->dma_offset
.cb_v
= f
->offs_v
;
447 f
->dma_offset
.cr_h
= f
->offs_h
;
448 f
->dma_offset
.cr_v
= f
->offs_v
;
450 if (!variant
->pix_hoff
) {
451 if (f
->fmt
->colplanes
== 3) {
452 f
->dma_offset
.cb_h
>>= 1;
453 f
->dma_offset
.cr_h
>>= 1;
455 if (f
->fmt
->color
== S5P_FIMC_YCBCR420
) {
456 f
->dma_offset
.cb_v
>>= 1;
457 f
->dma_offset
.cr_v
>>= 1;
461 dbg("in_offset: color= %d, y_h= %d, y_v= %d",
462 f
->fmt
->color
, f
->dma_offset
.y_h
, f
->dma_offset
.y_v
);
466 * V4L2 controls handling
468 #define ctrl_to_ctx(__ctrl) \
469 container_of((__ctrl)->handler, struct fimc_ctx, ctrl_handler)
471 static int __fimc_s_ctrl(struct fimc_ctx
*ctx
, struct v4l2_ctrl
*ctrl
)
473 struct fimc_dev
*fimc
= ctx
->fimc_dev
;
474 struct samsung_fimc_variant
*variant
= fimc
->variant
;
475 unsigned int flags
= FIMC_DST_FMT
| FIMC_SRC_FMT
;
478 if (ctrl
->flags
& V4L2_CTRL_FLAG_INACTIVE
)
483 ctx
->hflip
= ctrl
->val
;
487 ctx
->vflip
= ctrl
->val
;
490 case V4L2_CID_ROTATE
:
491 if (fimc_capture_pending(fimc
) ||
492 (ctx
->state
& flags
) == flags
) {
493 ret
= fimc_check_scaler_ratio(ctx
, ctx
->s_frame
.width
,
494 ctx
->s_frame
.height
, ctx
->d_frame
.width
,
495 ctx
->d_frame
.height
, ctrl
->val
);
499 if ((ctrl
->val
== 90 || ctrl
->val
== 270) &&
500 !variant
->has_out_rot
)
503 ctx
->rotation
= ctrl
->val
;
506 case V4L2_CID_ALPHA_COMPONENT
:
507 ctx
->d_frame
.alpha
= ctrl
->val
;
510 ctx
->state
|= FIMC_PARAMS
;
511 set_bit(ST_CAPT_APPLY_CFG
, &fimc
->state
);
515 static int fimc_s_ctrl(struct v4l2_ctrl
*ctrl
)
517 struct fimc_ctx
*ctx
= ctrl_to_ctx(ctrl
);
521 spin_lock_irqsave(&ctx
->fimc_dev
->slock
, flags
);
522 ret
= __fimc_s_ctrl(ctx
, ctrl
);
523 spin_unlock_irqrestore(&ctx
->fimc_dev
->slock
, flags
);
528 static const struct v4l2_ctrl_ops fimc_ctrl_ops
= {
529 .s_ctrl
= fimc_s_ctrl
,
532 int fimc_ctrls_create(struct fimc_ctx
*ctx
)
534 struct samsung_fimc_variant
*variant
= ctx
->fimc_dev
->variant
;
535 unsigned int max_alpha
= fimc_get_alpha_mask(ctx
->d_frame
.fmt
);
539 v4l2_ctrl_handler_init(&ctx
->ctrl_handler
, 4);
541 ctx
->ctrl_rotate
= v4l2_ctrl_new_std(&ctx
->ctrl_handler
, &fimc_ctrl_ops
,
542 V4L2_CID_ROTATE
, 0, 270, 90, 0);
543 ctx
->ctrl_hflip
= v4l2_ctrl_new_std(&ctx
->ctrl_handler
, &fimc_ctrl_ops
,
544 V4L2_CID_HFLIP
, 0, 1, 1, 0);
545 ctx
->ctrl_vflip
= v4l2_ctrl_new_std(&ctx
->ctrl_handler
, &fimc_ctrl_ops
,
546 V4L2_CID_VFLIP
, 0, 1, 1, 0);
547 if (variant
->has_alpha
)
548 ctx
->ctrl_alpha
= v4l2_ctrl_new_std(&ctx
->ctrl_handler
,
549 &fimc_ctrl_ops
, V4L2_CID_ALPHA_COMPONENT
,
552 ctx
->ctrl_alpha
= NULL
;
554 ctx
->ctrls_rdy
= ctx
->ctrl_handler
.error
== 0;
556 return ctx
->ctrl_handler
.error
;
559 void fimc_ctrls_delete(struct fimc_ctx
*ctx
)
561 if (ctx
->ctrls_rdy
) {
562 v4l2_ctrl_handler_free(&ctx
->ctrl_handler
);
563 ctx
->ctrls_rdy
= false;
564 ctx
->ctrl_alpha
= NULL
;
568 void fimc_ctrls_activate(struct fimc_ctx
*ctx
, bool active
)
570 unsigned int has_alpha
= ctx
->d_frame
.fmt
->flags
& FMT_HAS_ALPHA
;
575 mutex_lock(&ctx
->ctrl_handler
.lock
);
576 v4l2_ctrl_activate(ctx
->ctrl_rotate
, active
);
577 v4l2_ctrl_activate(ctx
->ctrl_hflip
, active
);
578 v4l2_ctrl_activate(ctx
->ctrl_vflip
, active
);
580 v4l2_ctrl_activate(ctx
->ctrl_alpha
, active
&& has_alpha
);
583 ctx
->rotation
= ctx
->ctrl_rotate
->val
;
584 ctx
->hflip
= ctx
->ctrl_hflip
->val
;
585 ctx
->vflip
= ctx
->ctrl_vflip
->val
;
591 mutex_unlock(&ctx
->ctrl_handler
.lock
);
594 /* Update maximum value of the alpha color control */
595 void fimc_alpha_ctrl_update(struct fimc_ctx
*ctx
)
597 struct fimc_dev
*fimc
= ctx
->fimc_dev
;
598 struct v4l2_ctrl
*ctrl
= ctx
->ctrl_alpha
;
600 if (ctrl
== NULL
|| !fimc
->variant
->has_alpha
)
603 v4l2_ctrl_lock(ctrl
);
604 ctrl
->maximum
= fimc_get_alpha_mask(ctx
->d_frame
.fmt
);
606 if (ctrl
->cur
.val
> ctrl
->maximum
)
607 ctrl
->cur
.val
= ctrl
->maximum
;
609 v4l2_ctrl_unlock(ctrl
);
612 int fimc_fill_format(struct fimc_frame
*frame
, struct v4l2_format
*f
)
614 struct v4l2_pix_format_mplane
*pixm
= &f
->fmt
.pix_mp
;
617 pixm
->width
= frame
->o_width
;
618 pixm
->height
= frame
->o_height
;
619 pixm
->field
= V4L2_FIELD_NONE
;
620 pixm
->pixelformat
= frame
->fmt
->fourcc
;
621 pixm
->colorspace
= V4L2_COLORSPACE_JPEG
;
622 pixm
->num_planes
= frame
->fmt
->memplanes
;
624 for (i
= 0; i
< pixm
->num_planes
; ++i
) {
625 int bpl
= frame
->f_width
;
626 if (frame
->fmt
->colplanes
== 1) /* packed formats */
627 bpl
= (bpl
* frame
->fmt
->depth
[0]) / 8;
628 pixm
->plane_fmt
[i
].bytesperline
= bpl
;
629 pixm
->plane_fmt
[i
].sizeimage
= (frame
->o_width
*
630 frame
->o_height
* frame
->fmt
->depth
[i
]) / 8;
635 void fimc_fill_frame(struct fimc_frame
*frame
, struct v4l2_format
*f
)
637 struct v4l2_pix_format_mplane
*pixm
= &f
->fmt
.pix_mp
;
639 frame
->f_width
= pixm
->plane_fmt
[0].bytesperline
;
640 if (frame
->fmt
->colplanes
== 1)
641 frame
->f_width
= (frame
->f_width
* 8) / frame
->fmt
->depth
[0];
642 frame
->f_height
= pixm
->height
;
643 frame
->width
= pixm
->width
;
644 frame
->height
= pixm
->height
;
645 frame
->o_width
= pixm
->width
;
646 frame
->o_height
= pixm
->height
;
652 * fimc_adjust_mplane_format - adjust bytesperline/sizeimage for each plane
653 * @fmt: fimc pixel format description (input)
654 * @width: requested pixel width
655 * @height: requested pixel height
656 * @pix: multi-plane format to adjust
658 void fimc_adjust_mplane_format(struct fimc_fmt
*fmt
, u32 width
, u32 height
,
659 struct v4l2_pix_format_mplane
*pix
)
661 u32 bytesperline
= 0;
664 pix
->colorspace
= V4L2_COLORSPACE_JPEG
;
665 pix
->field
= V4L2_FIELD_NONE
;
666 pix
->num_planes
= fmt
->memplanes
;
667 pix
->pixelformat
= fmt
->fourcc
;
668 pix
->height
= height
;
671 for (i
= 0; i
< pix
->num_planes
; ++i
) {
672 u32 bpl
= pix
->plane_fmt
[i
].bytesperline
;
673 u32
*sizeimage
= &pix
->plane_fmt
[i
].sizeimage
;
675 if (fmt
->colplanes
> 1 && (bpl
== 0 || bpl
< pix
->width
))
676 bpl
= pix
->width
; /* Planar */
678 if (fmt
->colplanes
== 1 && /* Packed */
679 (bpl
== 0 || ((bpl
* 8) / fmt
->depth
[i
]) < pix
->width
))
680 bpl
= (pix
->width
* fmt
->depth
[0]) / 8;
682 if (i
== 0) /* Same bytesperline for each plane. */
685 pix
->plane_fmt
[i
].bytesperline
= bytesperline
;
686 *sizeimage
= (pix
->width
* pix
->height
* fmt
->depth
[i
]) / 8;
691 * fimc_find_format - lookup fimc color format by fourcc or media bus format
692 * @pixelformat: fourcc to match, ignored if null
693 * @mbus_code: media bus code to match, ignored if null
694 * @mask: the color flags to match
695 * @index: offset in the fimc_formats array, ignored if negative
697 struct fimc_fmt
*fimc_find_format(const u32
*pixelformat
, const u32
*mbus_code
,
698 unsigned int mask
, int index
)
700 struct fimc_fmt
*fmt
, *def_fmt
= NULL
;
704 if (index
>= (int)ARRAY_SIZE(fimc_formats
))
707 for (i
= 0; i
< ARRAY_SIZE(fimc_formats
); ++i
) {
708 fmt
= &fimc_formats
[i
];
709 if (!(fmt
->flags
& mask
))
711 if (pixelformat
&& fmt
->fourcc
== *pixelformat
)
713 if (mbus_code
&& fmt
->mbus_code
== *mbus_code
)
722 static void fimc_clk_put(struct fimc_dev
*fimc
)
725 for (i
= 0; i
< MAX_FIMC_CLOCKS
; i
++) {
726 if (IS_ERR_OR_NULL(fimc
->clock
[i
]))
728 clk_unprepare(fimc
->clock
[i
]);
729 clk_put(fimc
->clock
[i
]);
730 fimc
->clock
[i
] = NULL
;
734 static int fimc_clk_get(struct fimc_dev
*fimc
)
738 for (i
= 0; i
< MAX_FIMC_CLOCKS
; i
++) {
739 fimc
->clock
[i
] = clk_get(&fimc
->pdev
->dev
, fimc_clocks
[i
]);
740 if (IS_ERR(fimc
->clock
[i
]))
742 ret
= clk_prepare(fimc
->clock
[i
]);
744 clk_put(fimc
->clock
[i
]);
745 fimc
->clock
[i
] = NULL
;
752 dev_err(&fimc
->pdev
->dev
, "failed to get clock: %s\n",
757 static int fimc_m2m_suspend(struct fimc_dev
*fimc
)
762 spin_lock_irqsave(&fimc
->slock
, flags
);
763 if (!fimc_m2m_pending(fimc
)) {
764 spin_unlock_irqrestore(&fimc
->slock
, flags
);
767 clear_bit(ST_M2M_SUSPENDED
, &fimc
->state
);
768 set_bit(ST_M2M_SUSPENDING
, &fimc
->state
);
769 spin_unlock_irqrestore(&fimc
->slock
, flags
);
771 timeout
= wait_event_timeout(fimc
->irq_queue
,
772 test_bit(ST_M2M_SUSPENDED
, &fimc
->state
),
773 FIMC_SHUTDOWN_TIMEOUT
);
775 clear_bit(ST_M2M_SUSPENDING
, &fimc
->state
);
776 return timeout
== 0 ? -EAGAIN
: 0;
779 static int fimc_m2m_resume(struct fimc_dev
*fimc
)
783 spin_lock_irqsave(&fimc
->slock
, flags
);
784 /* Clear for full H/W setup in first run after resume */
785 fimc
->m2m
.ctx
= NULL
;
786 spin_unlock_irqrestore(&fimc
->slock
, flags
);
788 if (test_and_clear_bit(ST_M2M_SUSPENDED
, &fimc
->state
))
789 fimc_m2m_job_finish(fimc
->m2m
.ctx
,
790 VB2_BUF_STATE_ERROR
);
794 static int fimc_probe(struct platform_device
*pdev
)
796 struct fimc_dev
*fimc
;
797 struct resource
*res
;
798 struct samsung_fimc_driverdata
*drv_data
;
799 struct s5p_platform_fimc
*pdata
;
802 drv_data
= (struct samsung_fimc_driverdata
*)
803 platform_get_device_id(pdev
)->driver_data
;
805 if (pdev
->id
>= drv_data
->num_entities
) {
806 dev_err(&pdev
->dev
, "Invalid platform device id: %d\n",
811 fimc
= devm_kzalloc(&pdev
->dev
, sizeof(*fimc
), GFP_KERNEL
);
817 fimc
->variant
= drv_data
->variant
[fimc
->id
];
819 pdata
= pdev
->dev
.platform_data
;
822 init_waitqueue_head(&fimc
->irq_queue
);
823 spin_lock_init(&fimc
->slock
);
824 mutex_init(&fimc
->lock
);
826 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
827 fimc
->regs
= devm_request_and_ioremap(&pdev
->dev
, res
);
828 if (fimc
->regs
== NULL
) {
829 dev_err(&pdev
->dev
, "Failed to obtain io memory\n");
833 res
= platform_get_resource(pdev
, IORESOURCE_IRQ
, 0);
835 dev_err(&pdev
->dev
, "Failed to get IRQ resource\n");
839 ret
= fimc_clk_get(fimc
);
842 clk_set_rate(fimc
->clock
[CLK_BUS
], drv_data
->lclk_frequency
);
843 clk_enable(fimc
->clock
[CLK_BUS
]);
845 ret
= devm_request_irq(&pdev
->dev
, res
->start
, fimc_irq_handler
,
846 0, pdev
->name
, fimc
);
848 dev_err(&pdev
->dev
, "failed to install irq (%d)\n", ret
);
852 ret
= fimc_initialize_capture_subdev(fimc
);
856 platform_set_drvdata(pdev
, fimc
);
857 pm_runtime_enable(&pdev
->dev
);
858 ret
= pm_runtime_get_sync(&pdev
->dev
);
861 /* Initialize contiguous memory allocator */
862 fimc
->alloc_ctx
= vb2_dma_contig_init_ctx(&pdev
->dev
);
863 if (IS_ERR(fimc
->alloc_ctx
)) {
864 ret
= PTR_ERR(fimc
->alloc_ctx
);
868 dev_dbg(&pdev
->dev
, "FIMC.%d registered successfully\n", fimc
->id
);
870 pm_runtime_put(&pdev
->dev
);
873 pm_runtime_put(&pdev
->dev
);
875 fimc_unregister_capture_subdev(fimc
);
881 static int fimc_runtime_resume(struct device
*dev
)
883 struct fimc_dev
*fimc
= dev_get_drvdata(dev
);
885 dbg("fimc%d: state: 0x%lx", fimc
->id
, fimc
->state
);
887 /* Enable clocks and perform basic initalization */
888 clk_enable(fimc
->clock
[CLK_GATE
]);
891 /* Resume the capture or mem-to-mem device */
892 if (fimc_capture_busy(fimc
))
893 return fimc_capture_resume(fimc
);
895 return fimc_m2m_resume(fimc
);
898 static int fimc_runtime_suspend(struct device
*dev
)
900 struct fimc_dev
*fimc
= dev_get_drvdata(dev
);
903 if (fimc_capture_busy(fimc
))
904 ret
= fimc_capture_suspend(fimc
);
906 ret
= fimc_m2m_suspend(fimc
);
908 clk_disable(fimc
->clock
[CLK_GATE
]);
910 dbg("fimc%d: state: 0x%lx", fimc
->id
, fimc
->state
);
914 #ifdef CONFIG_PM_SLEEP
915 static int fimc_resume(struct device
*dev
)
917 struct fimc_dev
*fimc
= dev_get_drvdata(dev
);
920 dbg("fimc%d: state: 0x%lx", fimc
->id
, fimc
->state
);
922 /* Do not resume if the device was idle before system suspend */
923 spin_lock_irqsave(&fimc
->slock
, flags
);
924 if (!test_and_clear_bit(ST_LPM
, &fimc
->state
) ||
925 (!fimc_m2m_active(fimc
) && !fimc_capture_busy(fimc
))) {
926 spin_unlock_irqrestore(&fimc
->slock
, flags
);
930 spin_unlock_irqrestore(&fimc
->slock
, flags
);
932 if (fimc_capture_busy(fimc
))
933 return fimc_capture_resume(fimc
);
935 return fimc_m2m_resume(fimc
);
938 static int fimc_suspend(struct device
*dev
)
940 struct fimc_dev
*fimc
= dev_get_drvdata(dev
);
942 dbg("fimc%d: state: 0x%lx", fimc
->id
, fimc
->state
);
944 if (test_and_set_bit(ST_LPM
, &fimc
->state
))
946 if (fimc_capture_busy(fimc
))
947 return fimc_capture_suspend(fimc
);
949 return fimc_m2m_suspend(fimc
);
951 #endif /* CONFIG_PM_SLEEP */
953 static int __devexit
fimc_remove(struct platform_device
*pdev
)
955 struct fimc_dev
*fimc
= platform_get_drvdata(pdev
);
957 pm_runtime_disable(&pdev
->dev
);
958 pm_runtime_set_suspended(&pdev
->dev
);
960 fimc_unregister_capture_subdev(fimc
);
961 vb2_dma_contig_cleanup_ctx(fimc
->alloc_ctx
);
963 clk_disable(fimc
->clock
[CLK_BUS
]);
966 dev_info(&pdev
->dev
, "driver unloaded\n");
970 /* Image pixel limits, similar across several FIMC HW revisions. */
971 static struct fimc_pix_limit s5p_pix_limit
[4] = {
974 .scaler_dis_w
= 8192,
976 .in_rot_dis_w
= 8192,
977 .out_rot_en_w
= 1920,
978 .out_rot_dis_w
= 4224,
982 .scaler_dis_w
= 8192,
984 .in_rot_dis_w
= 8192,
985 .out_rot_en_w
= 1920,
986 .out_rot_dis_w
= 4224,
990 .scaler_dis_w
= 8192,
992 .in_rot_dis_w
= 8192,
993 .out_rot_en_w
= 1280,
994 .out_rot_dis_w
= 1920,
998 .scaler_dis_w
= 8192,
1000 .in_rot_dis_w
= 8192,
1001 .out_rot_en_w
= 1366,
1002 .out_rot_dis_w
= 1920,
1006 static struct samsung_fimc_variant fimc0_variant_s5p
= {
1010 .min_inp_pixsize
= 16,
1011 .min_out_pixsize
= 16,
1012 .hor_offs_align
= 8,
1013 .min_vsize_align
= 16,
1015 .pix_limit
= &s5p_pix_limit
[0],
1018 static struct samsung_fimc_variant fimc2_variant_s5p
= {
1020 .min_inp_pixsize
= 16,
1021 .min_out_pixsize
= 16,
1022 .hor_offs_align
= 8,
1023 .min_vsize_align
= 16,
1025 .pix_limit
= &s5p_pix_limit
[1],
1028 static struct samsung_fimc_variant fimc0_variant_s5pv210
= {
1033 .min_inp_pixsize
= 16,
1034 .min_out_pixsize
= 16,
1035 .hor_offs_align
= 8,
1036 .min_vsize_align
= 16,
1038 .pix_limit
= &s5p_pix_limit
[1],
1041 static struct samsung_fimc_variant fimc1_variant_s5pv210
= {
1046 .has_mainscaler_ext
= 1,
1047 .min_inp_pixsize
= 16,
1048 .min_out_pixsize
= 16,
1049 .hor_offs_align
= 1,
1050 .min_vsize_align
= 1,
1052 .pix_limit
= &s5p_pix_limit
[2],
1055 static struct samsung_fimc_variant fimc2_variant_s5pv210
= {
1058 .min_inp_pixsize
= 16,
1059 .min_out_pixsize
= 16,
1060 .hor_offs_align
= 8,
1061 .min_vsize_align
= 16,
1063 .pix_limit
= &s5p_pix_limit
[2],
1066 static struct samsung_fimc_variant fimc0_variant_exynos4
= {
1072 .has_mainscaler_ext
= 1,
1074 .min_inp_pixsize
= 16,
1075 .min_out_pixsize
= 16,
1076 .hor_offs_align
= 2,
1077 .min_vsize_align
= 1,
1078 .out_buf_count
= 32,
1079 .pix_limit
= &s5p_pix_limit
[1],
1082 static struct samsung_fimc_variant fimc3_variant_exynos4
= {
1086 .has_mainscaler_ext
= 1,
1088 .min_inp_pixsize
= 16,
1089 .min_out_pixsize
= 16,
1090 .hor_offs_align
= 2,
1091 .min_vsize_align
= 1,
1092 .out_buf_count
= 32,
1093 .pix_limit
= &s5p_pix_limit
[3],
1097 static struct samsung_fimc_driverdata fimc_drvdata_s5p
= {
1099 [0] = &fimc0_variant_s5p
,
1100 [1] = &fimc0_variant_s5p
,
1101 [2] = &fimc2_variant_s5p
,
1104 .lclk_frequency
= 133000000UL,
1107 /* S5PV210, S5PC110 */
1108 static struct samsung_fimc_driverdata fimc_drvdata_s5pv210
= {
1110 [0] = &fimc0_variant_s5pv210
,
1111 [1] = &fimc1_variant_s5pv210
,
1112 [2] = &fimc2_variant_s5pv210
,
1115 .lclk_frequency
= 166000000UL,
1118 /* S5PV310, S5PC210 */
1119 static struct samsung_fimc_driverdata fimc_drvdata_exynos4
= {
1121 [0] = &fimc0_variant_exynos4
,
1122 [1] = &fimc0_variant_exynos4
,
1123 [2] = &fimc0_variant_exynos4
,
1124 [3] = &fimc3_variant_exynos4
,
1127 .lclk_frequency
= 166000000UL,
1130 static struct platform_device_id fimc_driver_ids
[] = {
1133 .driver_data
= (unsigned long)&fimc_drvdata_s5p
,
1135 .name
= "s5pv210-fimc",
1136 .driver_data
= (unsigned long)&fimc_drvdata_s5pv210
,
1138 .name
= "exynos4-fimc",
1139 .driver_data
= (unsigned long)&fimc_drvdata_exynos4
,
1143 MODULE_DEVICE_TABLE(platform
, fimc_driver_ids
);
1145 static const struct dev_pm_ops fimc_pm_ops
= {
1146 SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend
, fimc_resume
)
1147 SET_RUNTIME_PM_OPS(fimc_runtime_suspend
, fimc_runtime_resume
, NULL
)
1150 static struct platform_driver fimc_driver
= {
1151 .probe
= fimc_probe
,
1152 .remove
= __devexit_p(fimc_remove
),
1153 .id_table
= fimc_driver_ids
,
1155 .name
= FIMC_MODULE_NAME
,
1156 .owner
= THIS_MODULE
,
1161 int __init
fimc_register_driver(void)
1163 return platform_driver_register(&fimc_driver
);
1166 void __exit
fimc_unregister_driver(void)
1168 platform_driver_unregister(&fimc_driver
);