linux/drivers/gpu/ipu-v3/ipu-csi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2012-2014 Mentor Graphics Inc.
   4 * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
   5 */
   6#include <linux/export.h>
   7#include <linux/module.h>
   8#include <linux/types.h>
   9#include <linux/errno.h>
  10#include <linux/delay.h>
  11#include <linux/io.h>
  12#include <linux/err.h>
  13#include <linux/platform_device.h>
  14#include <linux/videodev2.h>
  15#include <uapi/linux/v4l2-mediabus.h>
  16#include <linux/clk.h>
  17#include <linux/clk-provider.h>
  18#include <linux/clkdev.h>
  19
  20#include "ipu-prv.h"
  21
  22struct ipu_csi {
  23        void __iomem *base;
  24        int id;
  25        u32 module;
  26        struct clk *clk_ipu;    /* IPU bus clock */
  27        spinlock_t lock;
  28        bool inuse;
  29        struct ipu_soc *ipu;
  30};
  31
  32/* CSI Register Offsets */
  33#define CSI_SENS_CONF           0x0000
  34#define CSI_SENS_FRM_SIZE       0x0004
  35#define CSI_ACT_FRM_SIZE        0x0008
  36#define CSI_OUT_FRM_CTRL        0x000c
  37#define CSI_TST_CTRL            0x0010
  38#define CSI_CCIR_CODE_1         0x0014
  39#define CSI_CCIR_CODE_2         0x0018
  40#define CSI_CCIR_CODE_3         0x001c
  41#define CSI_MIPI_DI             0x0020
  42#define CSI_SKIP                0x0024
  43#define CSI_CPD_CTRL            0x0028
  44#define CSI_CPD_RC(n)           (0x002c + ((n)*4))
  45#define CSI_CPD_RS(n)           (0x004c + ((n)*4))
  46#define CSI_CPD_GRC(n)          (0x005c + ((n)*4))
  47#define CSI_CPD_GRS(n)          (0x007c + ((n)*4))
  48#define CSI_CPD_GBC(n)          (0x008c + ((n)*4))
  49#define CSI_CPD_GBS(n)          (0x00Ac + ((n)*4))
  50#define CSI_CPD_BC(n)           (0x00Bc + ((n)*4))
  51#define CSI_CPD_BS(n)           (0x00Dc + ((n)*4))
  52#define CSI_CPD_OFFSET1         0x00ec
  53#define CSI_CPD_OFFSET2         0x00f0
  54
  55/* CSI Register Fields */
  56#define CSI_SENS_CONF_DATA_FMT_SHIFT            8
  57#define CSI_SENS_CONF_DATA_FMT_MASK             0x00000700
  58#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444       0L
  59#define CSI_SENS_CONF_DATA_FMT_YUV422_YUYV      1L
  60#define CSI_SENS_CONF_DATA_FMT_YUV422_UYVY      2L
  61#define CSI_SENS_CONF_DATA_FMT_BAYER            3L
  62#define CSI_SENS_CONF_DATA_FMT_RGB565           4L
  63#define CSI_SENS_CONF_DATA_FMT_RGB555           5L
  64#define CSI_SENS_CONF_DATA_FMT_RGB444           6L
  65#define CSI_SENS_CONF_DATA_FMT_JPEG             7L
  66
  67#define CSI_SENS_CONF_VSYNC_POL_SHIFT           0
  68#define CSI_SENS_CONF_HSYNC_POL_SHIFT           1
  69#define CSI_SENS_CONF_DATA_POL_SHIFT            2
  70#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT         3
  71#define CSI_SENS_CONF_SENS_PRTCL_MASK           0x00000070
  72#define CSI_SENS_CONF_SENS_PRTCL_SHIFT          4
  73#define CSI_SENS_CONF_PACK_TIGHT_SHIFT          7
  74#define CSI_SENS_CONF_DATA_WIDTH_SHIFT          11
  75#define CSI_SENS_CONF_EXT_VSYNC_SHIFT           15
  76#define CSI_SENS_CONF_DIVRATIO_SHIFT            16
  77
  78#define CSI_SENS_CONF_DIVRATIO_MASK             0x00ff0000
  79#define CSI_SENS_CONF_DATA_DEST_SHIFT           24
  80#define CSI_SENS_CONF_DATA_DEST_MASK            0x07000000
  81#define CSI_SENS_CONF_JPEG8_EN_SHIFT            27
  82#define CSI_SENS_CONF_JPEG_EN_SHIFT             28
  83#define CSI_SENS_CONF_FORCE_EOF_SHIFT           29
  84#define CSI_SENS_CONF_DATA_EN_POL_SHIFT         31
  85
  86#define CSI_DATA_DEST_IC                        2
  87#define CSI_DATA_DEST_IDMAC                     4
  88
  89#define CSI_CCIR_ERR_DET_EN                     0x01000000
  90#define CSI_HORI_DOWNSIZE_EN                    0x80000000
  91#define CSI_VERT_DOWNSIZE_EN                    0x40000000
  92#define CSI_TEST_GEN_MODE_EN                    0x01000000
  93
  94#define CSI_HSC_MASK                            0x1fff0000
  95#define CSI_HSC_SHIFT                           16
  96#define CSI_VSC_MASK                            0x00000fff
  97#define CSI_VSC_SHIFT                           0
  98
  99#define CSI_TEST_GEN_R_MASK                     0x000000ff
 100#define CSI_TEST_GEN_R_SHIFT                    0
 101#define CSI_TEST_GEN_G_MASK                     0x0000ff00
 102#define CSI_TEST_GEN_G_SHIFT                    8
 103#define CSI_TEST_GEN_B_MASK                     0x00ff0000
 104#define CSI_TEST_GEN_B_SHIFT                    16
 105
 106#define CSI_MAX_RATIO_SKIP_SMFC_MASK            0x00000007
 107#define CSI_MAX_RATIO_SKIP_SMFC_SHIFT           0
 108#define CSI_SKIP_SMFC_MASK                      0x000000f8
 109#define CSI_SKIP_SMFC_SHIFT                     3
 110#define CSI_ID_2_SKIP_MASK                      0x00000300
 111#define CSI_ID_2_SKIP_SHIFT                     8
 112
 113#define CSI_COLOR_FIRST_ROW_MASK                0x00000002
 114#define CSI_COLOR_FIRST_COMP_MASK               0x00000001
 115
 116/* MIPI CSI-2 data types */
 117#define MIPI_DT_YUV420          0x18 /* YYY.../UYVY.... */
 118#define MIPI_DT_YUV420_LEGACY   0x1a /* UYY.../VYY...   */
 119#define MIPI_DT_YUV422          0x1e /* UYVY...         */
 120#define MIPI_DT_RGB444          0x20
 121#define MIPI_DT_RGB555          0x21
 122#define MIPI_DT_RGB565          0x22
 123#define MIPI_DT_RGB666          0x23
 124#define MIPI_DT_RGB888          0x24
 125#define MIPI_DT_RAW6            0x28
 126#define MIPI_DT_RAW7            0x29
 127#define MIPI_DT_RAW8            0x2a
 128#define MIPI_DT_RAW10           0x2b
 129#define MIPI_DT_RAW12           0x2c
 130#define MIPI_DT_RAW14           0x2d
 131
 132/*
 133 * Bitfield of CSI bus signal polarities and modes.
 134 */
 135struct ipu_csi_bus_config {
 136        unsigned data_width:4;
 137        unsigned clk_mode:3;
 138        unsigned ext_vsync:1;
 139        unsigned vsync_pol:1;
 140        unsigned hsync_pol:1;
 141        unsigned pixclk_pol:1;
 142        unsigned data_pol:1;
 143        unsigned sens_clksrc:1;
 144        unsigned pack_tight:1;
 145        unsigned force_eof:1;
 146        unsigned data_en_pol:1;
 147
 148        unsigned data_fmt;
 149        unsigned mipi_dt;
 150};
 151
 152/*
 153 * Enumeration of CSI data bus widths.
 154 */
 155enum ipu_csi_data_width {
 156        IPU_CSI_DATA_WIDTH_4   = 0,
 157        IPU_CSI_DATA_WIDTH_8   = 1,
 158        IPU_CSI_DATA_WIDTH_10  = 3,
 159        IPU_CSI_DATA_WIDTH_12  = 5,
 160        IPU_CSI_DATA_WIDTH_16  = 9,
 161};
 162
 163/*
 164 * Enumeration of CSI clock modes.
 165 */
 166enum ipu_csi_clk_mode {
 167        IPU_CSI_CLK_MODE_GATED_CLK,
 168        IPU_CSI_CLK_MODE_NONGATED_CLK,
 169        IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE,
 170        IPU_CSI_CLK_MODE_CCIR656_INTERLACED,
 171        IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR,
 172        IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR,
 173        IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR,
 174        IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR,
 175};
 176
 177static inline u32 ipu_csi_read(struct ipu_csi *csi, unsigned offset)
 178{
 179        return readl(csi->base + offset);
 180}
 181
 182static inline void ipu_csi_write(struct ipu_csi *csi, u32 value,
 183                                 unsigned offset)
 184{
 185        writel(value, csi->base + offset);
 186}
 187
 188/*
 189 * Set mclk division ratio for generating test mode mclk. Only used
 190 * for test generator.
 191 */
 192static int ipu_csi_set_testgen_mclk(struct ipu_csi *csi, u32 pixel_clk,
 193                                        u32 ipu_clk)
 194{
 195        u32 temp;
 196        int div_ratio;
 197
 198        div_ratio = (ipu_clk / pixel_clk) - 1;
 199
 200        if (div_ratio > 0xFF || div_ratio < 0) {
 201                dev_err(csi->ipu->dev,
 202                        "value of pixel_clk extends normal range\n");
 203                return -EINVAL;
 204        }
 205
 206        temp = ipu_csi_read(csi, CSI_SENS_CONF);
 207        temp &= ~CSI_SENS_CONF_DIVRATIO_MASK;
 208        ipu_csi_write(csi, temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT),
 209                          CSI_SENS_CONF);
 210
 211        return 0;
 212}
 213
 214/*
 215 * Find the CSI data format and data width for the given V4L2 media
 216 * bus pixel format code.
 217 */
 218static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code,
 219                                enum v4l2_mbus_type mbus_type)
 220{
 221        switch (mbus_code) {
 222        case MEDIA_BUS_FMT_BGR565_2X8_BE:
 223        case MEDIA_BUS_FMT_BGR565_2X8_LE:
 224        case MEDIA_BUS_FMT_RGB565_2X8_BE:
 225        case MEDIA_BUS_FMT_RGB565_2X8_LE:
 226                if (mbus_type == V4L2_MBUS_CSI2_DPHY)
 227                        cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565;
 228                else
 229                        cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
 230                cfg->mipi_dt = MIPI_DT_RGB565;
 231                cfg->data_width = IPU_CSI_DATA_WIDTH_8;
 232                break;
 233        case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE:
 234        case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE:
 235                cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB444;
 236                cfg->mipi_dt = MIPI_DT_RGB444;
 237                cfg->data_width = IPU_CSI_DATA_WIDTH_8;
 238                break;
 239        case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
 240        case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
 241                cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555;
 242                cfg->mipi_dt = MIPI_DT_RGB555;
 243                cfg->data_width = IPU_CSI_DATA_WIDTH_8;
 244                break;
 245        case MEDIA_BUS_FMT_RGB888_1X24:
 246        case MEDIA_BUS_FMT_BGR888_1X24:
 247                cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444;
 248                cfg->mipi_dt = MIPI_DT_RGB888;
 249                cfg->data_width = IPU_CSI_DATA_WIDTH_8;
 250                break;
 251        case MEDIA_BUS_FMT_UYVY8_2X8:
 252                cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
 253                cfg->mipi_dt = MIPI_DT_YUV422;
 254                cfg->data_width = IPU_CSI_DATA_WIDTH_8;
 255                break;
 256        case MEDIA_BUS_FMT_YUYV8_2X8:
 257                cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
 258                cfg->mipi_dt = MIPI_DT_YUV422;
 259                cfg->data_width = IPU_CSI_DATA_WIDTH_8;
 260                break;
 261        case MEDIA_BUS_FMT_UYVY8_1X16:
 262        case MEDIA_BUS_FMT_YUYV8_1X16:
 263                cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
 264                cfg->mipi_dt = MIPI_DT_YUV422;
 265                cfg->data_width = IPU_CSI_DATA_WIDTH_16;
 266                break;
 267        case MEDIA_BUS_FMT_SBGGR8_1X8:
 268        case MEDIA_BUS_FMT_SGBRG8_1X8:
 269        case MEDIA_BUS_FMT_SGRBG8_1X8:
 270        case MEDIA_BUS_FMT_SRGGB8_1X8:
 271        case MEDIA_BUS_FMT_Y8_1X8:
 272                cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
 273                cfg->mipi_dt = MIPI_DT_RAW8;
 274                cfg->data_width = IPU_CSI_DATA_WIDTH_8;
 275                break;
 276        case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8:
 277        case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8:
 278        case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
 279        case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8:
 280        case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE:
 281        case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE:
 282        case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE:
 283        case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE:
 284                cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
 285                cfg->mipi_dt = MIPI_DT_RAW10;
 286                cfg->data_width = IPU_CSI_DATA_WIDTH_8;
 287                break;
 288        case MEDIA_BUS_FMT_SBGGR10_1X10:
 289        case MEDIA_BUS_FMT_SGBRG10_1X10:
 290        case MEDIA_BUS_FMT_SGRBG10_1X10:
 291        case MEDIA_BUS_FMT_SRGGB10_1X10:
 292        case MEDIA_BUS_FMT_Y10_1X10:
 293                cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
 294                cfg->mipi_dt = MIPI_DT_RAW10;
 295                cfg->data_width = IPU_CSI_DATA_WIDTH_10;
 296                break;
 297        case MEDIA_BUS_FMT_SBGGR12_1X12:
 298        case MEDIA_BUS_FMT_SGBRG12_1X12:
 299        case MEDIA_BUS_FMT_SGRBG12_1X12:
 300        case MEDIA_BUS_FMT_SRGGB12_1X12:
 301        case MEDIA_BUS_FMT_Y12_1X12:
 302                cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
 303                cfg->mipi_dt = MIPI_DT_RAW12;
 304                cfg->data_width = IPU_CSI_DATA_WIDTH_12;
 305                break;
 306        case MEDIA_BUS_FMT_JPEG_1X8:
 307                /* TODO */
 308                cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_JPEG;
 309                cfg->mipi_dt = MIPI_DT_RAW8;
 310                cfg->data_width = IPU_CSI_DATA_WIDTH_8;
 311                break;
 312        default:
 313                return -EINVAL;
 314        }
 315
 316        return 0;
 317}
 318
 319/* translate alternate field mode based on given standard */
 320static inline enum v4l2_field
 321ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std)
 322{
 323        return (field != V4L2_FIELD_ALTERNATE) ? field :
 324                ((std & V4L2_STD_525_60) ?
 325                 V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB);
 326}
 327
 328/*
 329 * Fill a CSI bus config struct from mbus_config and mbus_framefmt.
 330 */
 331static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg,
 332                            const struct v4l2_mbus_config *mbus_cfg,
 333                            const struct v4l2_mbus_framefmt *mbus_fmt)
 334{
 335        int ret;
 336
 337        memset(csicfg, 0, sizeof(*csicfg));
 338
 339        ret = mbus_code_to_bus_cfg(csicfg, mbus_fmt->code, mbus_cfg->type);
 340        if (ret < 0)
 341                return ret;
 342
 343        switch (mbus_cfg->type) {
 344        case V4L2_MBUS_PARALLEL:
 345                csicfg->ext_vsync = 1;
 346                csicfg->vsync_pol = (mbus_cfg->flags &
 347                                     V4L2_MBUS_VSYNC_ACTIVE_LOW) ? 1 : 0;
 348                csicfg->hsync_pol = (mbus_cfg->flags &
 349                                     V4L2_MBUS_HSYNC_ACTIVE_LOW) ? 1 : 0;
 350                csicfg->pixclk_pol = (mbus_cfg->flags &
 351                                      V4L2_MBUS_PCLK_SAMPLE_FALLING) ? 1 : 0;
 352                csicfg->clk_mode = IPU_CSI_CLK_MODE_GATED_CLK;
 353                break;
 354        case V4L2_MBUS_BT656:
 355                csicfg->ext_vsync = 0;
 356                if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field) ||
 357                    mbus_fmt->field == V4L2_FIELD_ALTERNATE)
 358                        csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED;
 359                else
 360                        csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE;
 361                break;
 362        case V4L2_MBUS_CSI2_DPHY:
 363                /*
 364                 * MIPI CSI-2 requires non gated clock mode, all other
 365                 * parameters are not applicable for MIPI CSI-2 bus.
 366                 */
 367                csicfg->clk_mode = IPU_CSI_CLK_MODE_NONGATED_CLK;
 368                break;
 369        default:
 370                /* will never get here, keep compiler quiet */
 371                break;
 372        }
 373
 374        return 0;
 375}
 376
 377static int
 378ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi,
 379                                const struct v4l2_mbus_framefmt *infmt,
 380                                const struct v4l2_mbus_framefmt *outfmt,
 381                                v4l2_std_id std)
 382{
 383        enum v4l2_field infield, outfield;
 384        bool swap_fields;
 385
 386        /* get translated field type of input and output */
 387        infield = ipu_csi_translate_field(infmt->field, std);
 388        outfield = ipu_csi_translate_field(outfmt->field, std);
 389
 390        /*
 391         * Write the H-V-F codes the CSI will match against the
 392         * incoming data for start/end of active and blanking
 393         * field intervals. If input and output field types are
 394         * sequential but not the same (one is SEQ_BT and the other
 395         * is SEQ_TB), swap the F-bit so that the CSI will capture
 396         * field 1 lines before field 0 lines.
 397         */
 398        swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) &&
 399                       V4L2_FIELD_IS_SEQUENTIAL(outfield) &&
 400                       infield != outfield);
 401
 402        if (!swap_fields) {
 403                /*
 404                 * Field0BlankEnd  = 110, Field0BlankStart  = 010
 405                 * Field0ActiveEnd = 100, Field0ActiveStart = 000
 406                 * Field1BlankEnd  = 111, Field1BlankStart  = 011
 407                 * Field1ActiveEnd = 101, Field1ActiveStart = 001
 408                 */
 409                ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN,
 410                              CSI_CCIR_CODE_1);
 411                ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2);
 412        } else {
 413                dev_dbg(csi->ipu->dev, "capture field swap\n");
 414
 415                /* same as above but with F-bit inverted */
 416                ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
 417                              CSI_CCIR_CODE_1);
 418                ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
 419        }
 420
 421        ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
 422
 423        return 0;
 424}
 425
 426
 427int ipu_csi_init_interface(struct ipu_csi *csi,
 428                           const struct v4l2_mbus_config *mbus_cfg,
 429                           const struct v4l2_mbus_framefmt *infmt,
 430                           const struct v4l2_mbus_framefmt *outfmt)
 431{
 432        struct ipu_csi_bus_config cfg;
 433        unsigned long flags;
 434        u32 width, height, data = 0;
 435        v4l2_std_id std;
 436        int ret;
 437
 438        ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt);
 439        if (ret < 0)
 440                return ret;
 441
 442        /* set default sensor frame width and height */
 443        width = infmt->width;
 444        height = infmt->height;
 445        if (infmt->field == V4L2_FIELD_ALTERNATE)
 446                height *= 2;
 447
 448        /* Set the CSI_SENS_CONF register remaining fields */
 449        data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
 450                cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
 451                cfg.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT |
 452                cfg.vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT |
 453                cfg.hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT |
 454                cfg.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT |
 455                cfg.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT |
 456                cfg.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT |
 457                cfg.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT |
 458                cfg.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT |
 459                cfg.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT;
 460
 461        spin_lock_irqsave(&csi->lock, flags);
 462
 463        ipu_csi_write(csi, data, CSI_SENS_CONF);
 464
 465        /* Set CCIR registers */
 466
 467        switch (cfg.clk_mode) {
 468        case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
 469                ipu_csi_write(csi, 0x40030, CSI_CCIR_CODE_1);
 470                ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
 471                break;
 472        case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
 473                if (width == 720 && height == 480) {
 474                        std = V4L2_STD_NTSC;
 475                        height = 525;
 476                } else if (width == 720 && height == 576) {
 477                        std = V4L2_STD_PAL;
 478                        height = 625;
 479                } else {
 480                        dev_err(csi->ipu->dev,
 481                                "Unsupported interlaced video mode\n");
 482                        ret = -EINVAL;
 483                        goto out_unlock;
 484                }
 485
 486                ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std);
 487                if (ret)
 488                        goto out_unlock;
 489                break;
 490        case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
 491        case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
 492        case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
 493        case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
 494                ipu_csi_write(csi, 0x40030 | CSI_CCIR_ERR_DET_EN,
 495                                   CSI_CCIR_CODE_1);
 496                ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
 497                break;
 498        case IPU_CSI_CLK_MODE_GATED_CLK:
 499        case IPU_CSI_CLK_MODE_NONGATED_CLK:
 500                ipu_csi_write(csi, 0, CSI_CCIR_CODE_1);
 501                break;
 502        }
 503
 504        /* Setup sensor frame size */
 505        ipu_csi_write(csi, (width - 1) | ((height - 1) << 16),
 506                      CSI_SENS_FRM_SIZE);
 507
 508        dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
 509                ipu_csi_read(csi, CSI_SENS_CONF));
 510        dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
 511                ipu_csi_read(csi, CSI_ACT_FRM_SIZE));
 512
 513out_unlock:
 514        spin_unlock_irqrestore(&csi->lock, flags);
 515
 516        return ret;
 517}
 518EXPORT_SYMBOL_GPL(ipu_csi_init_interface);
 519
 520bool ipu_csi_is_interlaced(struct ipu_csi *csi)
 521{
 522        unsigned long flags;
 523        u32 sensor_protocol;
 524
 525        spin_lock_irqsave(&csi->lock, flags);
 526        sensor_protocol =
 527                (ipu_csi_read(csi, CSI_SENS_CONF) &
 528                 CSI_SENS_CONF_SENS_PRTCL_MASK) >>
 529                CSI_SENS_CONF_SENS_PRTCL_SHIFT;
 530        spin_unlock_irqrestore(&csi->lock, flags);
 531
 532        switch (sensor_protocol) {
 533        case IPU_CSI_CLK_MODE_GATED_CLK:
 534        case IPU_CSI_CLK_MODE_NONGATED_CLK:
 535        case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
 536        case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
 537        case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
 538                return false;
 539        case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
 540        case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
 541        case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
 542                return true;
 543        default:
 544                dev_err(csi->ipu->dev,
 545                        "CSI %d sensor protocol unsupported\n", csi->id);
 546                return false;
 547        }
 548}
 549EXPORT_SYMBOL_GPL(ipu_csi_is_interlaced);
 550
 551void ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w)
 552{
 553        unsigned long flags;
 554        u32 reg;
 555
 556        spin_lock_irqsave(&csi->lock, flags);
 557
 558        reg = ipu_csi_read(csi, CSI_ACT_FRM_SIZE);
 559        w->width = (reg & 0xFFFF) + 1;
 560        w->height = (reg >> 16 & 0xFFFF) + 1;
 561
 562        reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL);
 563        w->left = (reg & CSI_HSC_MASK) >> CSI_HSC_SHIFT;
 564        w->top = (reg & CSI_VSC_MASK) >> CSI_VSC_SHIFT;
 565
 566        spin_unlock_irqrestore(&csi->lock, flags);
 567}
 568EXPORT_SYMBOL_GPL(ipu_csi_get_window);
 569
 570void ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w)
 571{
 572        unsigned long flags;
 573        u32 reg;
 574
 575        spin_lock_irqsave(&csi->lock, flags);
 576
 577        ipu_csi_write(csi, (w->width - 1) | ((w->height - 1) << 16),
 578                          CSI_ACT_FRM_SIZE);
 579
 580        reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL);
 581        reg &= ~(CSI_HSC_MASK | CSI_VSC_MASK);
 582        reg |= ((w->top << CSI_VSC_SHIFT) | (w->left << CSI_HSC_SHIFT));
 583        ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL);
 584
 585        spin_unlock_irqrestore(&csi->lock, flags);
 586}
 587EXPORT_SYMBOL_GPL(ipu_csi_set_window);
 588
 589void ipu_csi_set_downsize(struct ipu_csi *csi, bool horiz, bool vert)
 590{
 591        unsigned long flags;
 592        u32 reg;
 593
 594        spin_lock_irqsave(&csi->lock, flags);
 595
 596        reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL);
 597        reg &= ~(CSI_HORI_DOWNSIZE_EN | CSI_VERT_DOWNSIZE_EN);
 598        reg |= (horiz ? CSI_HORI_DOWNSIZE_EN : 0) |
 599               (vert ? CSI_VERT_DOWNSIZE_EN : 0);
 600        ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL);
 601
 602        spin_unlock_irqrestore(&csi->lock, flags);
 603}
 604EXPORT_SYMBOL_GPL(ipu_csi_set_downsize);
 605
 606void ipu_csi_set_test_generator(struct ipu_csi *csi, bool active,
 607                                u32 r_value, u32 g_value, u32 b_value,
 608                                u32 pix_clk)
 609{
 610        unsigned long flags;
 611        u32 ipu_clk = clk_get_rate(csi->clk_ipu);
 612        u32 temp;
 613
 614        spin_lock_irqsave(&csi->lock, flags);
 615
 616        temp = ipu_csi_read(csi, CSI_TST_CTRL);
 617
 618        if (!active) {
 619                temp &= ~CSI_TEST_GEN_MODE_EN;
 620                ipu_csi_write(csi, temp, CSI_TST_CTRL);
 621        } else {
 622                /* Set sensb_mclk div_ratio */
 623                ipu_csi_set_testgen_mclk(csi, pix_clk, ipu_clk);
 624
 625                temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK |
 626                          CSI_TEST_GEN_B_MASK);
 627                temp |= CSI_TEST_GEN_MODE_EN;
 628                temp |= (r_value << CSI_TEST_GEN_R_SHIFT) |
 629                        (g_value << CSI_TEST_GEN_G_SHIFT) |
 630                        (b_value << CSI_TEST_GEN_B_SHIFT);
 631                ipu_csi_write(csi, temp, CSI_TST_CTRL);
 632        }
 633
 634        spin_unlock_irqrestore(&csi->lock, flags);
 635}
 636EXPORT_SYMBOL_GPL(ipu_csi_set_test_generator);
 637
 638int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc,
 639                              struct v4l2_mbus_framefmt *mbus_fmt)
 640{
 641        struct ipu_csi_bus_config cfg;
 642        unsigned long flags;
 643        u32 temp;
 644        int ret;
 645
 646        if (vc > 3)
 647                return -EINVAL;
 648
 649        ret = mbus_code_to_bus_cfg(&cfg, mbus_fmt->code, V4L2_MBUS_CSI2_DPHY);
 650        if (ret < 0)
 651                return ret;
 652
 653        spin_lock_irqsave(&csi->lock, flags);
 654
 655        temp = ipu_csi_read(csi, CSI_MIPI_DI);
 656        temp &= ~(0xff << (vc * 8));
 657        temp |= (cfg.mipi_dt << (vc * 8));
 658        ipu_csi_write(csi, temp, CSI_MIPI_DI);
 659
 660        spin_unlock_irqrestore(&csi->lock, flags);
 661
 662        return 0;
 663}
 664EXPORT_SYMBOL_GPL(ipu_csi_set_mipi_datatype);
 665
 666int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip,
 667                          u32 max_ratio, u32 id)
 668{
 669        unsigned long flags;
 670        u32 temp;
 671
 672        if (max_ratio > 5 || id > 3)
 673                return -EINVAL;
 674
 675        spin_lock_irqsave(&csi->lock, flags);
 676
 677        temp = ipu_csi_read(csi, CSI_SKIP);
 678        temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK |
 679                  CSI_SKIP_SMFC_MASK);
 680        temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) |
 681                (id << CSI_ID_2_SKIP_SHIFT) |
 682                (skip << CSI_SKIP_SMFC_SHIFT);
 683        ipu_csi_write(csi, temp, CSI_SKIP);
 684
 685        spin_unlock_irqrestore(&csi->lock, flags);
 686
 687        return 0;
 688}
 689EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc);
 690
 691int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest)
 692{
 693        unsigned long flags;
 694        u32 csi_sens_conf, dest;
 695
 696        if (csi_dest == IPU_CSI_DEST_IDMAC)
 697                dest = CSI_DATA_DEST_IDMAC;
 698        else
 699                dest = CSI_DATA_DEST_IC; /* IC or VDIC */
 700
 701        spin_lock_irqsave(&csi->lock, flags);
 702
 703        csi_sens_conf = ipu_csi_read(csi, CSI_SENS_CONF);
 704        csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK;
 705        csi_sens_conf |= (dest << CSI_SENS_CONF_DATA_DEST_SHIFT);
 706        ipu_csi_write(csi, csi_sens_conf, CSI_SENS_CONF);
 707
 708        spin_unlock_irqrestore(&csi->lock, flags);
 709
 710        return 0;
 711}
 712EXPORT_SYMBOL_GPL(ipu_csi_set_dest);
 713
 714int ipu_csi_enable(struct ipu_csi *csi)
 715{
 716        ipu_module_enable(csi->ipu, csi->module);
 717
 718        return 0;
 719}
 720EXPORT_SYMBOL_GPL(ipu_csi_enable);
 721
 722int ipu_csi_disable(struct ipu_csi *csi)
 723{
 724        ipu_module_disable(csi->ipu, csi->module);
 725
 726        return 0;
 727}
 728EXPORT_SYMBOL_GPL(ipu_csi_disable);
 729
 730struct ipu_csi *ipu_csi_get(struct ipu_soc *ipu, int id)
 731{
 732        unsigned long flags;
 733        struct ipu_csi *csi, *ret;
 734
 735        if (id > 1)
 736                return ERR_PTR(-EINVAL);
 737
 738        csi = ipu->csi_priv[id];
 739        ret = csi;
 740
 741        spin_lock_irqsave(&csi->lock, flags);
 742
 743        if (csi->inuse) {
 744                ret = ERR_PTR(-EBUSY);
 745                goto unlock;
 746        }
 747
 748        csi->inuse = true;
 749unlock:
 750        spin_unlock_irqrestore(&csi->lock, flags);
 751        return ret;
 752}
 753EXPORT_SYMBOL_GPL(ipu_csi_get);
 754
 755void ipu_csi_put(struct ipu_csi *csi)
 756{
 757        unsigned long flags;
 758
 759        spin_lock_irqsave(&csi->lock, flags);
 760        csi->inuse = false;
 761        spin_unlock_irqrestore(&csi->lock, flags);
 762}
 763EXPORT_SYMBOL_GPL(ipu_csi_put);
 764
 765int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id,
 766                 unsigned long base, u32 module, struct clk *clk_ipu)
 767{
 768        struct ipu_csi *csi;
 769
 770        if (id > 1)
 771                return -ENODEV;
 772
 773        csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL);
 774        if (!csi)
 775                return -ENOMEM;
 776
 777        ipu->csi_priv[id] = csi;
 778
 779        spin_lock_init(&csi->lock);
 780        csi->module = module;
 781        csi->id = id;
 782        csi->clk_ipu = clk_ipu;
 783        csi->base = devm_ioremap(dev, base, PAGE_SIZE);
 784        if (!csi->base)
 785                return -ENOMEM;
 786
 787        dev_dbg(dev, "CSI%d base: 0x%08lx remapped to %p\n",
 788                id, base, csi->base);
 789        csi->ipu = ipu;
 790
 791        return 0;
 792}
 793
 794void ipu_csi_exit(struct ipu_soc *ipu, int id)
 795{
 796}
 797
 798void ipu_csi_dump(struct ipu_csi *csi)
 799{
 800        dev_dbg(csi->ipu->dev, "CSI_SENS_CONF:     %08x\n",
 801                ipu_csi_read(csi, CSI_SENS_CONF));
 802        dev_dbg(csi->ipu->dev, "CSI_SENS_FRM_SIZE: %08x\n",
 803                ipu_csi_read(csi, CSI_SENS_FRM_SIZE));
 804        dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE:  %08x\n",
 805                ipu_csi_read(csi, CSI_ACT_FRM_SIZE));
 806        dev_dbg(csi->ipu->dev, "CSI_OUT_FRM_CTRL:  %08x\n",
 807                ipu_csi_read(csi, CSI_OUT_FRM_CTRL));
 808        dev_dbg(csi->ipu->dev, "CSI_TST_CTRL:      %08x\n",
 809                ipu_csi_read(csi, CSI_TST_CTRL));
 810        dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_1:   %08x\n",
 811                ipu_csi_read(csi, CSI_CCIR_CODE_1));
 812        dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_2:   %08x\n",
 813                ipu_csi_read(csi, CSI_CCIR_CODE_2));
 814        dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_3:   %08x\n",
 815                ipu_csi_read(csi, CSI_CCIR_CODE_3));
 816        dev_dbg(csi->ipu->dev, "CSI_MIPI_DI:       %08x\n",
 817                ipu_csi_read(csi, CSI_MIPI_DI));
 818        dev_dbg(csi->ipu->dev, "CSI_SKIP:          %08x\n",
 819                ipu_csi_read(csi, CSI_SKIP));
 820}
 821EXPORT_SYMBOL_GPL(ipu_csi_dump);
 822
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.