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