1177 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Samsung S5P/EXYNOS4 SoC series FIMC (CAMIF) driver4 *5 * Copyright (C) 2010-2012 Samsung Electronics Co., Ltd.6 * Sylwester Nawrocki <s.nawrocki@samsung.com>7 */8 9#include <linux/module.h>10#include <linux/kernel.h>11#include <linux/types.h>12#include <linux/errno.h>13#include <linux/bug.h>14#include <linux/interrupt.h>15#include <linux/device.h>16#include <linux/platform_device.h>17#include <linux/pm_runtime.h>18#include <linux/list.h>19#include <linux/mfd/syscon.h>20#include <linux/io.h>21#include <linux/of.h>22#include <linux/slab.h>23#include <linux/clk.h>24#include <media/v4l2-ioctl.h>25#include <media/videobuf2-v4l2.h>26#include <media/videobuf2-dma-contig.h>27 28#include "fimc-core.h"29#include "fimc-reg.h"30#include "media-dev.h"31 32static const char *fimc_clocks[MAX_FIMC_CLOCKS] = {33 "sclk_fimc", "fimc"34};35 36static const struct fimc_fmt fimc_formats[] = {37 {38 .fourcc = V4L2_PIX_FMT_RGB565,39 .depth = { 16 },40 .color = FIMC_FMT_RGB565,41 .memplanes = 1,42 .colplanes = 1,43 .flags = FMT_FLAGS_M2M,44 }, {45 .fourcc = V4L2_PIX_FMT_BGR666,46 .depth = { 32 },47 .color = FIMC_FMT_RGB666,48 .memplanes = 1,49 .colplanes = 1,50 .flags = FMT_FLAGS_M2M,51 }, {52 .fourcc = V4L2_PIX_FMT_BGR32,53 .depth = { 32 },54 .color = FIMC_FMT_RGB888,55 .memplanes = 1,56 .colplanes = 1,57 .flags = FMT_FLAGS_M2M | FMT_HAS_ALPHA,58 }, {59 .fourcc = V4L2_PIX_FMT_RGB555,60 .depth = { 16 },61 .color = FIMC_FMT_RGB555,62 .memplanes = 1,63 .colplanes = 1,64 .flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA,65 }, {66 .fourcc = V4L2_PIX_FMT_RGB444,67 .depth = { 16 },68 .color = FIMC_FMT_RGB444,69 .memplanes = 1,70 .colplanes = 1,71 .flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA,72 }, {73 .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,74 .flags = FMT_FLAGS_WRITEBACK,75 }, {76 .fourcc = V4L2_PIX_FMT_YUYV,77 .depth = { 16 },78 .color = FIMC_FMT_YCBYCR422,79 .memplanes = 1,80 .colplanes = 1,81 .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,82 .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,83 }, {84 .fourcc = V4L2_PIX_FMT_UYVY,85 .depth = { 16 },86 .color = FIMC_FMT_CBYCRY422,87 .memplanes = 1,88 .colplanes = 1,89 .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,90 .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,91 }, {92 .fourcc = V4L2_PIX_FMT_VYUY,93 .depth = { 16 },94 .color = FIMC_FMT_CRYCBY422,95 .memplanes = 1,96 .colplanes = 1,97 .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,98 .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,99 }, {100 .fourcc = V4L2_PIX_FMT_YVYU,101 .depth = { 16 },102 .color = FIMC_FMT_YCRYCB422,103 .memplanes = 1,104 .colplanes = 1,105 .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,106 .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,107 }, {108 .fourcc = V4L2_PIX_FMT_YUV422P,109 .depth = { 16 },110 .color = FIMC_FMT_YCBYCR422,111 .memplanes = 1,112 .colplanes = 3,113 .flags = FMT_FLAGS_M2M,114 }, {115 .fourcc = V4L2_PIX_FMT_NV16,116 .depth = { 16 },117 .color = FIMC_FMT_YCBYCR422,118 .memplanes = 1,119 .colplanes = 2,120 .flags = FMT_FLAGS_M2M,121 }, {122 .fourcc = V4L2_PIX_FMT_NV61,123 .depth = { 16 },124 .color = FIMC_FMT_YCRYCB422,125 .memplanes = 1,126 .colplanes = 2,127 .flags = FMT_FLAGS_M2M,128 }, {129 .fourcc = V4L2_PIX_FMT_YUV420,130 .depth = { 12 },131 .color = FIMC_FMT_YCBCR420,132 .memplanes = 1,133 .colplanes = 3,134 .flags = FMT_FLAGS_M2M,135 }, {136 .fourcc = V4L2_PIX_FMT_NV12,137 .depth = { 12 },138 .color = FIMC_FMT_YCBCR420,139 .memplanes = 1,140 .colplanes = 2,141 .flags = FMT_FLAGS_M2M,142 }, {143 .fourcc = V4L2_PIX_FMT_NV12M,144 .color = FIMC_FMT_YCBCR420,145 .depth = { 8, 4 },146 .memplanes = 2,147 .colplanes = 2,148 .flags = FMT_FLAGS_M2M,149 }, {150 .fourcc = V4L2_PIX_FMT_YUV420M,151 .color = FIMC_FMT_YCBCR420,152 .depth = { 8, 2, 2 },153 .memplanes = 3,154 .colplanes = 3,155 .flags = FMT_FLAGS_M2M,156 }, {157 .fourcc = V4L2_PIX_FMT_NV12MT,158 .color = FIMC_FMT_YCBCR420,159 .depth = { 8, 4 },160 .memplanes = 2,161 .colplanes = 2,162 .flags = FMT_FLAGS_M2M,163 }, {164 .fourcc = V4L2_PIX_FMT_JPEG,165 .color = FIMC_FMT_JPEG,166 .depth = { 8 },167 .memplanes = 1,168 .colplanes = 1,169 .mbus_code = MEDIA_BUS_FMT_JPEG_1X8,170 .flags = FMT_FLAGS_CAM | FMT_FLAGS_COMPRESSED,171 }, {172 .fourcc = V4L2_PIX_FMT_S5C_UYVY_JPG,173 .color = FIMC_FMT_YUYV_JPEG,174 .depth = { 8 },175 .memplanes = 2,176 .colplanes = 1,177 .mdataplanes = 0x2, /* plane 1 holds frame meta data */178 .mbus_code = MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8,179 .flags = FMT_FLAGS_CAM | FMT_FLAGS_COMPRESSED,180 },181};182 183const struct fimc_fmt *fimc_get_format(unsigned int index)184{185 if (index >= ARRAY_SIZE(fimc_formats))186 return NULL;187 188 return &fimc_formats[index];189}190 191int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh,192 int dw, int dh, int rotation)193{194 if (rotation == 90 || rotation == 270)195 swap(dw, dh);196 197 if (!ctx->scaler.enabled)198 return (sw == dw && sh == dh) ? 0 : -EINVAL;199 200 if ((sw >= SCALER_MAX_HRATIO * dw) || (sh >= SCALER_MAX_VRATIO * dh))201 return -EINVAL;202 203 return 0;204}205 206static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)207{208 u32 sh = 6;209 210 if (src >= 64 * tar)211 return -EINVAL;212 213 while (sh--) {214 u32 tmp = 1 << sh;215 if (src >= tar * tmp) {216 *shift = sh;217 *ratio = tmp;218 return 0;219 }220 }221 *shift = 0;222 *ratio = 1;223 return 0;224}225 226int fimc_set_scaler_info(struct fimc_ctx *ctx)227{228 const struct fimc_variant *variant = ctx->fimc_dev->variant;229 struct device *dev = &ctx->fimc_dev->pdev->dev;230 struct fimc_scaler *sc = &ctx->scaler;231 const struct fimc_frame *s_frame = &ctx->s_frame;232 const struct fimc_frame *d_frame = &ctx->d_frame;233 int tx, ty, sx, sy;234 int ret;235 236 if (ctx->rotation == 90 || ctx->rotation == 270) {237 ty = d_frame->width;238 tx = d_frame->height;239 } else {240 tx = d_frame->width;241 ty = d_frame->height;242 }243 if (tx <= 0 || ty <= 0) {244 dev_err(dev, "Invalid target size: %dx%d\n", tx, ty);245 return -EINVAL;246 }247 248 sx = s_frame->width;249 sy = s_frame->height;250 if (sx <= 0 || sy <= 0) {251 dev_err(dev, "Invalid source size: %dx%d\n", sx, sy);252 return -EINVAL;253 }254 sc->real_width = sx;255 sc->real_height = sy;256 257 ret = fimc_get_scaler_factor(sx, tx, &sc->pre_hratio, &sc->hfactor);258 if (ret)259 return ret;260 261 ret = fimc_get_scaler_factor(sy, ty, &sc->pre_vratio, &sc->vfactor);262 if (ret)263 return ret;264 265 sc->pre_dst_width = sx / sc->pre_hratio;266 sc->pre_dst_height = sy / sc->pre_vratio;267 268 if (variant->has_mainscaler_ext) {269 sc->main_hratio = (sx << 14) / (tx << sc->hfactor);270 sc->main_vratio = (sy << 14) / (ty << sc->vfactor);271 } else {272 sc->main_hratio = (sx << 8) / (tx << sc->hfactor);273 sc->main_vratio = (sy << 8) / (ty << sc->vfactor);274 275 }276 277 sc->scaleup_h = (tx >= sx) ? 1 : 0;278 sc->scaleup_v = (ty >= sy) ? 1 : 0;279 280 /* check to see if input and output size/format differ */281 if (s_frame->fmt->color == d_frame->fmt->color282 && s_frame->width == d_frame->width283 && s_frame->height == d_frame->height)284 sc->copy_mode = 1;285 else286 sc->copy_mode = 0;287 288 return 0;289}290 291static irqreturn_t fimc_irq_handler(int irq, void *priv)292{293 struct fimc_dev *fimc = priv;294 struct fimc_ctx *ctx;295 296 fimc_hw_clear_irq(fimc);297 298 spin_lock(&fimc->slock);299 300 if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) {301 if (test_and_clear_bit(ST_M2M_SUSPENDING, &fimc->state)) {302 set_bit(ST_M2M_SUSPENDED, &fimc->state);303 wake_up(&fimc->irq_queue);304 goto out;305 }306 ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev);307 if (ctx != NULL) {308 spin_unlock(&fimc->slock);309 fimc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE);310 311 if (ctx->state & FIMC_CTX_SHUT) {312 ctx->state &= ~FIMC_CTX_SHUT;313 wake_up(&fimc->irq_queue);314 }315 return IRQ_HANDLED;316 }317 } else if (test_bit(ST_CAPT_PEND, &fimc->state)) {318 int last_buf = test_bit(ST_CAPT_JPEG, &fimc->state) &&319 fimc->vid_cap.reqbufs_count == 1;320 fimc_capture_irq_handler(fimc, !last_buf);321 }322out:323 spin_unlock(&fimc->slock);324 return IRQ_HANDLED;325}326 327/* The color format (colplanes, memplanes) must be already configured. */328int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,329 const struct fimc_frame *frame, struct fimc_addr *addr)330{331 int ret = 0;332 u32 pix_size;333 334 if (vb == NULL || frame == NULL)335 return -EINVAL;336 337 pix_size = frame->width * frame->height;338 339 dbg("memplanes= %d, colplanes= %d, pix_size= %d",340 frame->fmt->memplanes, frame->fmt->colplanes, pix_size);341 342 addr->y = vb2_dma_contig_plane_dma_addr(vb, 0);343 344 if (frame->fmt->memplanes == 1) {345 switch (frame->fmt->colplanes) {346 case 1:347 addr->cb = 0;348 addr->cr = 0;349 break;350 case 2:351 /* decompose Y into Y/Cb */352 addr->cb = (u32)(addr->y + pix_size);353 addr->cr = 0;354 break;355 case 3:356 addr->cb = (u32)(addr->y + pix_size);357 /* decompose Y into Y/Cb/Cr */358 if (FIMC_FMT_YCBCR420 == frame->fmt->color)359 addr->cr = (u32)(addr->cb + (pix_size >> 2));360 else /* 422 */361 addr->cr = (u32)(addr->cb + (pix_size >> 1));362 break;363 default:364 return -EINVAL;365 }366 } else if (!frame->fmt->mdataplanes) {367 if (frame->fmt->memplanes >= 2)368 addr->cb = vb2_dma_contig_plane_dma_addr(vb, 1);369 370 if (frame->fmt->memplanes == 3)371 addr->cr = vb2_dma_contig_plane_dma_addr(vb, 2);372 }373 374 dbg("DMA ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d",375 addr->y, addr->cb, addr->cr, ret);376 377 return ret;378}379 380/* Set order for 1 and 2 plane YCBCR 4:2:2 formats. */381void fimc_set_yuv_order(struct fimc_ctx *ctx)382{383 /* The one only mode supported in SoC. */384 ctx->in_order_2p = FIMC_REG_CIOCTRL_ORDER422_2P_LSB_CRCB;385 ctx->out_order_2p = FIMC_REG_CIOCTRL_ORDER422_2P_LSB_CRCB;386 387 /* Set order for 1 plane input formats. */388 switch (ctx->s_frame.fmt->color) {389 case FIMC_FMT_YCRYCB422:390 ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCRYCB;391 break;392 case FIMC_FMT_CBYCRY422:393 ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CBYCRY;394 break;395 case FIMC_FMT_CRYCBY422:396 ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CRYCBY;397 break;398 case FIMC_FMT_YCBYCR422:399 default:400 ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCBYCR;401 break;402 }403 dbg("ctx->in_order_1p= %d", ctx->in_order_1p);404 405 switch (ctx->d_frame.fmt->color) {406 case FIMC_FMT_YCRYCB422:407 ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCRYCB;408 break;409 case FIMC_FMT_CBYCRY422:410 ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CBYCRY;411 break;412 case FIMC_FMT_CRYCBY422:413 ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CRYCBY;414 break;415 case FIMC_FMT_YCBYCR422:416 default:417 ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCBYCR;418 break;419 }420 dbg("ctx->out_order_1p= %d", ctx->out_order_1p);421}422 423void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)424{425 bool pix_hoff = ctx->fimc_dev->drv_data->dma_pix_hoff;426 u32 i, depth = 0;427 428 for (i = 0; i < f->fmt->memplanes; i++)429 depth += f->fmt->depth[i];430 431 f->dma_offset.y_h = f->offs_h;432 if (!pix_hoff)433 f->dma_offset.y_h *= (depth >> 3);434 435 f->dma_offset.y_v = f->offs_v;436 437 f->dma_offset.cb_h = f->offs_h;438 f->dma_offset.cb_v = f->offs_v;439 440 f->dma_offset.cr_h = f->offs_h;441 f->dma_offset.cr_v = f->offs_v;442 443 if (!pix_hoff) {444 if (f->fmt->colplanes == 3) {445 f->dma_offset.cb_h >>= 1;446 f->dma_offset.cr_h >>= 1;447 }448 if (f->fmt->color == FIMC_FMT_YCBCR420) {449 f->dma_offset.cb_v >>= 1;450 f->dma_offset.cr_v >>= 1;451 }452 }453 454 dbg("in_offset: color= %d, y_h= %d, y_v= %d",455 f->fmt->color, f->dma_offset.y_h, f->dma_offset.y_v);456}457 458static int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx)459{460 struct fimc_effect *effect = &ctx->effect;461 462 switch (colorfx) {463 case V4L2_COLORFX_NONE:464 effect->type = FIMC_REG_CIIMGEFF_FIN_BYPASS;465 break;466 case V4L2_COLORFX_BW:467 effect->type = FIMC_REG_CIIMGEFF_FIN_ARBITRARY;468 effect->pat_cb = 128;469 effect->pat_cr = 128;470 break;471 case V4L2_COLORFX_SEPIA:472 effect->type = FIMC_REG_CIIMGEFF_FIN_ARBITRARY;473 effect->pat_cb = 115;474 effect->pat_cr = 145;475 break;476 case V4L2_COLORFX_NEGATIVE:477 effect->type = FIMC_REG_CIIMGEFF_FIN_NEGATIVE;478 break;479 case V4L2_COLORFX_EMBOSS:480 effect->type = FIMC_REG_CIIMGEFF_FIN_EMBOSSING;481 break;482 case V4L2_COLORFX_ART_FREEZE:483 effect->type = FIMC_REG_CIIMGEFF_FIN_ARTFREEZE;484 break;485 case V4L2_COLORFX_SILHOUETTE:486 effect->type = FIMC_REG_CIIMGEFF_FIN_SILHOUETTE;487 break;488 case V4L2_COLORFX_SET_CBCR:489 effect->type = FIMC_REG_CIIMGEFF_FIN_ARBITRARY;490 effect->pat_cb = ctx->ctrls.colorfx_cbcr->val >> 8;491 effect->pat_cr = ctx->ctrls.colorfx_cbcr->val & 0xff;492 break;493 default:494 return -EINVAL;495 }496 497 return 0;498}499 500/*501 * V4L2 controls handling502 */503#define ctrl_to_ctx(__ctrl) \504 container_of((__ctrl)->handler, struct fimc_ctx, ctrls.handler)505 506static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl)507{508 struct fimc_dev *fimc = ctx->fimc_dev;509 const struct fimc_variant *variant = fimc->variant;510 int ret = 0;511 512 if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)513 return 0;514 515 switch (ctrl->id) {516 case V4L2_CID_HFLIP:517 ctx->hflip = ctrl->val;518 break;519 520 case V4L2_CID_VFLIP:521 ctx->vflip = ctrl->val;522 break;523 524 case V4L2_CID_ROTATE:525 if (fimc_capture_pending(fimc)) {526 ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,527 ctx->s_frame.height, ctx->d_frame.width,528 ctx->d_frame.height, ctrl->val);529 if (ret)530 return -EINVAL;531 }532 if ((ctrl->val == 90 || ctrl->val == 270) &&533 !variant->has_out_rot)534 return -EINVAL;535 536 ctx->rotation = ctrl->val;537 break;538 539 case V4L2_CID_ALPHA_COMPONENT:540 ctx->d_frame.alpha = ctrl->val;541 break;542 543 case V4L2_CID_COLORFX:544 ret = fimc_set_color_effect(ctx, ctrl->val);545 if (ret)546 return ret;547 break;548 }549 550 ctx->state |= FIMC_PARAMS;551 set_bit(ST_CAPT_APPLY_CFG, &fimc->state);552 return 0;553}554 555static int fimc_s_ctrl(struct v4l2_ctrl *ctrl)556{557 struct fimc_ctx *ctx = ctrl_to_ctx(ctrl);558 unsigned long flags;559 int ret;560 561 spin_lock_irqsave(&ctx->fimc_dev->slock, flags);562 ret = __fimc_s_ctrl(ctx, ctrl);563 spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags);564 565 return ret;566}567 568static const struct v4l2_ctrl_ops fimc_ctrl_ops = {569 .s_ctrl = fimc_s_ctrl,570};571 572int fimc_ctrls_create(struct fimc_ctx *ctx)573{574 unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt);575 struct fimc_ctrls *ctrls = &ctx->ctrls;576 struct v4l2_ctrl_handler *handler = &ctrls->handler;577 578 if (ctx->ctrls.ready)579 return 0;580 581 v4l2_ctrl_handler_init(handler, 6);582 583 ctrls->rotate = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops,584 V4L2_CID_ROTATE, 0, 270, 90, 0);585 ctrls->hflip = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops,586 V4L2_CID_HFLIP, 0, 1, 1, 0);587 ctrls->vflip = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops,588 V4L2_CID_VFLIP, 0, 1, 1, 0);589 590 if (ctx->fimc_dev->drv_data->alpha_color)591 ctrls->alpha = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops,592 V4L2_CID_ALPHA_COMPONENT,593 0, max_alpha, 1, 0);594 else595 ctrls->alpha = NULL;596 597 ctrls->colorfx = v4l2_ctrl_new_std_menu(handler, &fimc_ctrl_ops,598 V4L2_CID_COLORFX, V4L2_COLORFX_SET_CBCR,599 ~0x983f, V4L2_COLORFX_NONE);600 601 ctrls->colorfx_cbcr = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops,602 V4L2_CID_COLORFX_CBCR, 0, 0xffff, 1, 0);603 604 ctx->effect.type = FIMC_REG_CIIMGEFF_FIN_BYPASS;605 606 if (!handler->error) {607 v4l2_ctrl_cluster(2, &ctrls->colorfx);608 ctrls->ready = true;609 }610 611 return handler->error;612}613 614void fimc_ctrls_delete(struct fimc_ctx *ctx)615{616 struct fimc_ctrls *ctrls = &ctx->ctrls;617 618 if (ctrls->ready) {619 v4l2_ctrl_handler_free(&ctrls->handler);620 ctrls->ready = false;621 ctrls->alpha = NULL;622 }623}624 625void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active)626{627 unsigned int has_alpha = ctx->d_frame.fmt->flags & FMT_HAS_ALPHA;628 struct fimc_ctrls *ctrls = &ctx->ctrls;629 630 if (!ctrls->ready)631 return;632 633 mutex_lock(ctrls->handler.lock);634 v4l2_ctrl_activate(ctrls->rotate, active);635 v4l2_ctrl_activate(ctrls->hflip, active);636 v4l2_ctrl_activate(ctrls->vflip, active);637 v4l2_ctrl_activate(ctrls->colorfx, active);638 if (ctrls->alpha)639 v4l2_ctrl_activate(ctrls->alpha, active && has_alpha);640 641 if (active) {642 fimc_set_color_effect(ctx, ctrls->colorfx->cur.val);643 ctx->rotation = ctrls->rotate->val;644 ctx->hflip = ctrls->hflip->val;645 ctx->vflip = ctrls->vflip->val;646 } else {647 ctx->effect.type = FIMC_REG_CIIMGEFF_FIN_BYPASS;648 ctx->rotation = 0;649 ctx->hflip = 0;650 ctx->vflip = 0;651 }652 mutex_unlock(ctrls->handler.lock);653}654 655/* Update maximum value of the alpha color control */656void fimc_alpha_ctrl_update(struct fimc_ctx *ctx)657{658 struct fimc_dev *fimc = ctx->fimc_dev;659 struct v4l2_ctrl *ctrl = ctx->ctrls.alpha;660 661 if (ctrl == NULL || !fimc->drv_data->alpha_color)662 return;663 664 v4l2_ctrl_lock(ctrl);665 ctrl->maximum = fimc_get_alpha_mask(ctx->d_frame.fmt);666 667 if (ctrl->cur.val > ctrl->maximum)668 ctrl->cur.val = ctrl->maximum;669 670 v4l2_ctrl_unlock(ctrl);671}672 673void __fimc_get_format(const struct fimc_frame *frame, struct v4l2_format *f)674{675 struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;676 int i;677 678 pixm->width = frame->o_width;679 pixm->height = frame->o_height;680 pixm->field = V4L2_FIELD_NONE;681 pixm->pixelformat = frame->fmt->fourcc;682 pixm->colorspace = V4L2_COLORSPACE_JPEG;683 pixm->num_planes = frame->fmt->memplanes;684 685 for (i = 0; i < pixm->num_planes; ++i) {686 pixm->plane_fmt[i].bytesperline = frame->bytesperline[i];687 pixm->plane_fmt[i].sizeimage = frame->payload[i];688 }689}690 691/**692 * fimc_adjust_mplane_format - adjust bytesperline/sizeimage for each plane693 * @fmt: fimc pixel format description (input)694 * @width: requested pixel width695 * @height: requested pixel height696 * @pix: multi-plane format to adjust697 */698void fimc_adjust_mplane_format(const struct fimc_fmt *fmt, u32 width, u32 height,699 struct v4l2_pix_format_mplane *pix)700{701 u32 bytesperline = 0;702 int i;703 704 pix->colorspace = V4L2_COLORSPACE_JPEG;705 pix->field = V4L2_FIELD_NONE;706 pix->num_planes = fmt->memplanes;707 pix->pixelformat = fmt->fourcc;708 pix->height = height;709 pix->width = width;710 711 for (i = 0; i < pix->num_planes; ++i) {712 struct v4l2_plane_pix_format *plane_fmt = &pix->plane_fmt[i];713 u32 bpl = plane_fmt->bytesperline;714 u32 sizeimage;715 716 if (fmt->colplanes > 1 && (bpl == 0 || bpl < pix->width))717 bpl = pix->width; /* Planar */718 719 if (fmt->colplanes == 1 && /* Packed */720 (bpl == 0 || ((bpl * 8) / fmt->depth[i]) < pix->width))721 bpl = (pix->width * fmt->depth[0]) / 8;722 /*723 * Currently bytesperline for each plane is same, except724 * V4L2_PIX_FMT_YUV420M format. This calculation may need725 * to be changed when other multi-planar formats are added726 * to the fimc_formats[] array.727 */728 if (i == 0)729 bytesperline = bpl;730 else if (i == 1 && fmt->memplanes == 3)731 bytesperline /= 2;732 733 plane_fmt->bytesperline = bytesperline;734 sizeimage = pix->width * pix->height * fmt->depth[i] / 8;735 736 /* Ensure full last row for tiled formats */737 if (tiled_fmt(fmt)) {738 /* 64 * 32 * plane_fmt->bytesperline / 64 */739 u32 row_size = plane_fmt->bytesperline * 32;740 741 sizeimage = roundup(sizeimage, row_size);742 }743 744 plane_fmt->sizeimage = max(sizeimage, plane_fmt->sizeimage);745 }746}747 748/**749 * fimc_find_format - lookup fimc color format by fourcc or media bus format750 * @pixelformat: fourcc to match, ignored if null751 * @mbus_code: media bus code to match, ignored if null752 * @mask: the color flags to match753 * @index: offset in the fimc_formats array, ignored if negative754 */755const struct fimc_fmt *fimc_find_format(const u32 *pixelformat,756 const u32 *mbus_code,757 unsigned int mask, int index)758{759 const struct fimc_fmt *fmt, *def_fmt = NULL;760 unsigned int i;761 int id = 0;762 763 if (index >= (int)ARRAY_SIZE(fimc_formats))764 return NULL;765 766 for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) {767 fmt = &fimc_formats[i];768 if (!(fmt->flags & mask))769 continue;770 if (pixelformat && fmt->fourcc == *pixelformat)771 return fmt;772 if (mbus_code && fmt->mbus_code == *mbus_code)773 return fmt;774 if (index == id)775 def_fmt = fmt;776 id++;777 }778 return def_fmt;779}780 781static void fimc_clk_put(struct fimc_dev *fimc)782{783 int i;784 for (i = 0; i < MAX_FIMC_CLOCKS; i++) {785 if (IS_ERR(fimc->clock[i]))786 continue;787 clk_unprepare(fimc->clock[i]);788 clk_put(fimc->clock[i]);789 fimc->clock[i] = ERR_PTR(-EINVAL);790 }791}792 793static int fimc_clk_get(struct fimc_dev *fimc)794{795 int i, ret;796 797 for (i = 0; i < MAX_FIMC_CLOCKS; i++)798 fimc->clock[i] = ERR_PTR(-EINVAL);799 800 for (i = 0; i < MAX_FIMC_CLOCKS; i++) {801 fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]);802 if (IS_ERR(fimc->clock[i])) {803 ret = PTR_ERR(fimc->clock[i]);804 goto err;805 }806 ret = clk_prepare(fimc->clock[i]);807 if (ret < 0) {808 clk_put(fimc->clock[i]);809 fimc->clock[i] = ERR_PTR(-EINVAL);810 goto err;811 }812 }813 return 0;814err:815 fimc_clk_put(fimc);816 dev_err(&fimc->pdev->dev, "failed to get clock: %s\n",817 fimc_clocks[i]);818 return ret;819}820 821#ifdef CONFIG_PM822static int fimc_m2m_suspend(struct fimc_dev *fimc)823{824 unsigned long flags;825 long time_left;826 827 spin_lock_irqsave(&fimc->slock, flags);828 if (!fimc_m2m_pending(fimc)) {829 spin_unlock_irqrestore(&fimc->slock, flags);830 return 0;831 }832 clear_bit(ST_M2M_SUSPENDED, &fimc->state);833 set_bit(ST_M2M_SUSPENDING, &fimc->state);834 spin_unlock_irqrestore(&fimc->slock, flags);835 836 time_left = wait_event_timeout(fimc->irq_queue,837 test_bit(ST_M2M_SUSPENDED, &fimc->state),838 FIMC_SHUTDOWN_TIMEOUT);839 840 clear_bit(ST_M2M_SUSPENDING, &fimc->state);841 return time_left == 0 ? -EAGAIN : 0;842}843 844static int fimc_m2m_resume(struct fimc_dev *fimc)845{846 struct fimc_ctx *ctx;847 unsigned long flags;848 849 spin_lock_irqsave(&fimc->slock, flags);850 /* Clear for full H/W setup in first run after resume */851 ctx = fimc->m2m.ctx;852 fimc->m2m.ctx = NULL;853 spin_unlock_irqrestore(&fimc->slock, flags);854 855 if (test_and_clear_bit(ST_M2M_SUSPENDED, &fimc->state))856 fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);857 858 return 0;859}860#endif /* CONFIG_PM */861 862static const struct of_device_id fimc_of_match[];863 864static int fimc_parse_dt(struct fimc_dev *fimc, u32 *clk_freq)865{866 struct device *dev = &fimc->pdev->dev;867 struct device_node *node = dev->of_node;868 const struct of_device_id *of_id;869 struct fimc_variant *v;870 struct fimc_pix_limit *lim;871 u32 args[FIMC_PIX_LIMITS_MAX];872 int ret;873 874 if (of_property_read_bool(node, "samsung,lcd-wb"))875 return -ENODEV;876 877 v = devm_kzalloc(dev, sizeof(*v) + sizeof(*lim), GFP_KERNEL);878 if (!v)879 return -ENOMEM;880 881 of_id = of_match_node(fimc_of_match, node);882 if (!of_id)883 return -EINVAL;884 fimc->drv_data = of_id->data;885 ret = of_property_read_u32_array(node, "samsung,pix-limits",886 args, FIMC_PIX_LIMITS_MAX);887 if (ret < 0)888 return ret;889 890 lim = (struct fimc_pix_limit *)&v[1];891 892 lim->scaler_en_w = args[0];893 lim->scaler_dis_w = args[1];894 lim->out_rot_en_w = args[2];895 lim->out_rot_dis_w = args[3];896 v->pix_limit = lim;897 898 ret = of_property_read_u32_array(node, "samsung,min-pix-sizes",899 args, 2);900 v->min_inp_pixsize = ret ? FIMC_DEF_MIN_SIZE : args[0];901 v->min_out_pixsize = ret ? FIMC_DEF_MIN_SIZE : args[1];902 ret = of_property_read_u32_array(node, "samsung,min-pix-alignment",903 args, 2);904 v->min_vsize_align = ret ? FIMC_DEF_HEIGHT_ALIGN : args[0];905 v->hor_offs_align = ret ? FIMC_DEF_HOR_OFFS_ALIGN : args[1];906 907 ret = of_property_read_u32(node, "samsung,rotators", &args[1]);908 v->has_inp_rot = ret ? 1 : args[1] & 0x01;909 v->has_out_rot = ret ? 1 : args[1] & 0x10;910 v->has_mainscaler_ext = of_property_read_bool(node,911 "samsung,mainscaler-ext");912 913 v->has_isp_wb = of_property_read_bool(node, "samsung,isp-wb");914 v->has_cam_if = of_property_read_bool(node, "samsung,cam-if");915 of_property_read_u32(node, "clock-frequency", clk_freq);916 fimc->id = of_alias_get_id(node, "fimc");917 918 fimc->variant = v;919 return 0;920}921 922static int fimc_probe(struct platform_device *pdev)923{924 struct device *dev = &pdev->dev;925 u32 lclk_freq = 0;926 struct fimc_dev *fimc;927 int ret = 0;928 int irq;929 930 fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL);931 if (!fimc)932 return -ENOMEM;933 934 fimc->pdev = pdev;935 936 if (dev->of_node) {937 ret = fimc_parse_dt(fimc, &lclk_freq);938 if (ret < 0)939 return ret;940 } else {941 fimc->drv_data = fimc_get_drvdata(pdev);942 fimc->id = pdev->id;943 }944 if (!fimc->drv_data || fimc->id >= fimc->drv_data->num_entities ||945 fimc->id < 0) {946 dev_err(dev, "Invalid driver data or device id (%d)\n",947 fimc->id);948 return -EINVAL;949 }950 if (!dev->of_node)951 fimc->variant = fimc->drv_data->variant[fimc->id];952 953 init_waitqueue_head(&fimc->irq_queue);954 spin_lock_init(&fimc->slock);955 mutex_init(&fimc->lock);956 957 if (fimc->variant->has_isp_wb) {958 fimc->sysreg = fimc_get_sysreg_regmap(dev->of_node);959 if (IS_ERR(fimc->sysreg))960 return PTR_ERR(fimc->sysreg);961 }962 963 fimc->regs = devm_platform_ioremap_resource(pdev, 0);964 if (IS_ERR(fimc->regs))965 return PTR_ERR(fimc->regs);966 967 irq = platform_get_irq(pdev, 0);968 if (irq < 0)969 return irq;970 971 ret = fimc_clk_get(fimc);972 if (ret)973 return ret;974 975 if (lclk_freq == 0)976 lclk_freq = fimc->drv_data->lclk_frequency;977 978 ret = clk_set_rate(fimc->clock[CLK_BUS], lclk_freq);979 if (ret < 0)980 return ret;981 982 ret = clk_enable(fimc->clock[CLK_BUS]);983 if (ret < 0)984 return ret;985 986 ret = devm_request_irq(dev, irq, fimc_irq_handler,987 0, dev_name(dev), fimc);988 if (ret < 0) {989 dev_err(dev, "failed to install irq (%d)\n", ret);990 goto err_sclk;991 }992 993 ret = fimc_initialize_capture_subdev(fimc);994 if (ret < 0)995 goto err_sclk;996 997 platform_set_drvdata(pdev, fimc);998 pm_runtime_enable(dev);999 1000 if (!pm_runtime_enabled(dev)) {1001 ret = clk_enable(fimc->clock[CLK_GATE]);1002 if (ret < 0)1003 goto err_sd;1004 }1005 1006 vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));1007 1008 dev_dbg(dev, "FIMC.%d registered successfully\n", fimc->id);1009 return 0;1010 1011err_sd:1012 fimc_unregister_capture_subdev(fimc);1013err_sclk:1014 clk_disable(fimc->clock[CLK_BUS]);1015 fimc_clk_put(fimc);1016 return ret;1017}1018 1019#ifdef CONFIG_PM1020static int fimc_runtime_resume(struct device *dev)1021{1022 struct fimc_dev *fimc = dev_get_drvdata(dev);1023 1024 dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);1025 1026 /* Enable clocks and perform basic initialization */1027 clk_enable(fimc->clock[CLK_GATE]);1028 fimc_hw_reset(fimc);1029 1030 /* Resume the capture or mem-to-mem device */1031 if (fimc_capture_busy(fimc))1032 return fimc_capture_resume(fimc);1033 1034 return fimc_m2m_resume(fimc);1035}1036 1037static int fimc_runtime_suspend(struct device *dev)1038{1039 struct fimc_dev *fimc = dev_get_drvdata(dev);1040 int ret = 0;1041 1042 if (fimc_capture_busy(fimc))1043 ret = fimc_capture_suspend(fimc);1044 else1045 ret = fimc_m2m_suspend(fimc);1046 if (!ret)1047 clk_disable(fimc->clock[CLK_GATE]);1048 1049 dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);1050 return ret;1051}1052#endif1053 1054#ifdef CONFIG_PM_SLEEP1055static int fimc_resume(struct device *dev)1056{1057 struct fimc_dev *fimc = dev_get_drvdata(dev);1058 unsigned long flags;1059 1060 dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);1061 1062 /* Do not resume if the device was idle before system suspend */1063 spin_lock_irqsave(&fimc->slock, flags);1064 if (!test_and_clear_bit(ST_LPM, &fimc->state) ||1065 (!fimc_m2m_active(fimc) && !fimc_capture_busy(fimc))) {1066 spin_unlock_irqrestore(&fimc->slock, flags);1067 return 0;1068 }1069 fimc_hw_reset(fimc);1070 spin_unlock_irqrestore(&fimc->slock, flags);1071 1072 if (fimc_capture_busy(fimc))1073 return fimc_capture_resume(fimc);1074 1075 return fimc_m2m_resume(fimc);1076}1077 1078static int fimc_suspend(struct device *dev)1079{1080 struct fimc_dev *fimc = dev_get_drvdata(dev);1081 1082 dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);1083 1084 if (test_and_set_bit(ST_LPM, &fimc->state))1085 return 0;1086 if (fimc_capture_busy(fimc))1087 return fimc_capture_suspend(fimc);1088 1089 return fimc_m2m_suspend(fimc);1090}1091#endif /* CONFIG_PM_SLEEP */1092 1093static void fimc_remove(struct platform_device *pdev)1094{1095 struct fimc_dev *fimc = platform_get_drvdata(pdev);1096 1097 pm_runtime_disable(&pdev->dev);1098 if (!pm_runtime_status_suspended(&pdev->dev))1099 clk_disable(fimc->clock[CLK_GATE]);1100 pm_runtime_set_suspended(&pdev->dev);1101 1102 fimc_unregister_capture_subdev(fimc);1103 vb2_dma_contig_clear_max_seg_size(&pdev->dev);1104 1105 clk_disable(fimc->clock[CLK_BUS]);1106 fimc_clk_put(fimc);1107 1108 dev_info(&pdev->dev, "driver unloaded\n");1109}1110 1111/* S5PV210, S5PC110 */1112static const struct fimc_drvdata fimc_drvdata_s5pv210 = {1113 .num_entities = 3,1114 .lclk_frequency = 166000000UL,1115 .out_buf_count = 4,1116 .dma_pix_hoff = 1,1117};1118 1119/* EXYNOS4210, S5PV310, S5PC210 */1120static const struct fimc_drvdata fimc_drvdata_exynos4210 = {1121 .num_entities = 4,1122 .lclk_frequency = 166000000UL,1123 .dma_pix_hoff = 1,1124 .cistatus2 = 1,1125 .alpha_color = 1,1126 .out_buf_count = 32,1127};1128 1129/* EXYNOS4212, EXYNOS4412 */1130static const struct fimc_drvdata fimc_drvdata_exynos4x12 = {1131 .num_entities = 4,1132 .lclk_frequency = 166000000UL,1133 .dma_pix_hoff = 1,1134 .cistatus2 = 1,1135 .alpha_color = 1,1136 .out_buf_count = 32,1137};1138 1139static const struct of_device_id fimc_of_match[] = {1140 {1141 .compatible = "samsung,s5pv210-fimc",1142 .data = &fimc_drvdata_s5pv210,1143 }, {1144 .compatible = "samsung,exynos4210-fimc",1145 .data = &fimc_drvdata_exynos4210,1146 }, {1147 .compatible = "samsung,exynos4212-fimc",1148 .data = &fimc_drvdata_exynos4x12,1149 },1150 { /* sentinel */ },1151};1152 1153static const struct dev_pm_ops fimc_pm_ops = {1154 SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume)1155 SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL)1156};1157 1158static struct platform_driver fimc_driver = {1159 .probe = fimc_probe,1160 .remove_new = fimc_remove,1161 .driver = {1162 .of_match_table = fimc_of_match,1163 .name = FIMC_DRIVER_NAME,1164 .pm = &fimc_pm_ops,1165 }1166};1167 1168int __init fimc_register_driver(void)1169{1170 return platform_driver_register(&fimc_driver);1171}1172 1173void fimc_unregister_driver(void)1174{1175 platform_driver_unregister(&fimc_driver);1176}1177