Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2012-2014 Mentor Graphics Inc.
0004  * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
0005  */
0006 #include <linux/export.h>
0007 #include <linux/module.h>
0008 #include <linux/types.h>
0009 #include <linux/errno.h>
0010 #include <linux/delay.h>
0011 #include <linux/io.h>
0012 #include <linux/err.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/videodev2.h>
0015 #include <uapi/linux/v4l2-mediabus.h>
0016 #include <linux/clk.h>
0017 #include <linux/clk-provider.h>
0018 #include <linux/clkdev.h>
0019 
0020 #include "ipu-prv.h"
0021 
0022 struct ipu_csi {
0023     void __iomem *base;
0024     int id;
0025     u32 module;
0026     struct clk *clk_ipu;    /* IPU bus clock */
0027     spinlock_t lock;
0028     bool inuse;
0029     struct ipu_soc *ipu;
0030 };
0031 
0032 /* CSI Register Offsets */
0033 #define CSI_SENS_CONF       0x0000
0034 #define CSI_SENS_FRM_SIZE   0x0004
0035 #define CSI_ACT_FRM_SIZE    0x0008
0036 #define CSI_OUT_FRM_CTRL    0x000c
0037 #define CSI_TST_CTRL        0x0010
0038 #define CSI_CCIR_CODE_1     0x0014
0039 #define CSI_CCIR_CODE_2     0x0018
0040 #define CSI_CCIR_CODE_3     0x001c
0041 #define CSI_MIPI_DI     0x0020
0042 #define CSI_SKIP        0x0024
0043 #define CSI_CPD_CTRL        0x0028
0044 #define CSI_CPD_RC(n)       (0x002c + ((n)*4))
0045 #define CSI_CPD_RS(n)       (0x004c + ((n)*4))
0046 #define CSI_CPD_GRC(n)      (0x005c + ((n)*4))
0047 #define CSI_CPD_GRS(n)      (0x007c + ((n)*4))
0048 #define CSI_CPD_GBC(n)      (0x008c + ((n)*4))
0049 #define CSI_CPD_GBS(n)      (0x00Ac + ((n)*4))
0050 #define CSI_CPD_BC(n)       (0x00Bc + ((n)*4))
0051 #define CSI_CPD_BS(n)       (0x00Dc + ((n)*4))
0052 #define CSI_CPD_OFFSET1     0x00ec
0053 #define CSI_CPD_OFFSET2     0x00f0
0054 
0055 /* CSI Register Fields */
0056 #define CSI_SENS_CONF_DATA_FMT_SHIFT        8
0057 #define CSI_SENS_CONF_DATA_FMT_MASK     0x00000700
0058 #define CSI_SENS_CONF_DATA_FMT_RGB_YUV444   0L
0059 #define CSI_SENS_CONF_DATA_FMT_YUV422_YUYV  1L
0060 #define CSI_SENS_CONF_DATA_FMT_YUV422_UYVY  2L
0061 #define CSI_SENS_CONF_DATA_FMT_BAYER        3L
0062 #define CSI_SENS_CONF_DATA_FMT_RGB565       4L
0063 #define CSI_SENS_CONF_DATA_FMT_RGB555       5L
0064 #define CSI_SENS_CONF_DATA_FMT_RGB444       6L
0065 #define CSI_SENS_CONF_DATA_FMT_JPEG     7L
0066 
0067 #define CSI_SENS_CONF_VSYNC_POL_SHIFT       0
0068 #define CSI_SENS_CONF_HSYNC_POL_SHIFT       1
0069 #define CSI_SENS_CONF_DATA_POL_SHIFT        2
0070 #define CSI_SENS_CONF_PIX_CLK_POL_SHIFT     3
0071 #define CSI_SENS_CONF_SENS_PRTCL_MASK       0x00000070
0072 #define CSI_SENS_CONF_SENS_PRTCL_SHIFT      4
0073 #define CSI_SENS_CONF_PACK_TIGHT_SHIFT      7
0074 #define CSI_SENS_CONF_DATA_WIDTH_SHIFT      11
0075 #define CSI_SENS_CONF_EXT_VSYNC_SHIFT       15
0076 #define CSI_SENS_CONF_DIVRATIO_SHIFT        16
0077 
0078 #define CSI_SENS_CONF_DIVRATIO_MASK     0x00ff0000
0079 #define CSI_SENS_CONF_DATA_DEST_SHIFT       24
0080 #define CSI_SENS_CONF_DATA_DEST_MASK        0x07000000
0081 #define CSI_SENS_CONF_JPEG8_EN_SHIFT        27
0082 #define CSI_SENS_CONF_JPEG_EN_SHIFT     28
0083 #define CSI_SENS_CONF_FORCE_EOF_SHIFT       29
0084 #define CSI_SENS_CONF_DATA_EN_POL_SHIFT     31
0085 
0086 #define CSI_DATA_DEST_IC            2
0087 #define CSI_DATA_DEST_IDMAC         4
0088 
0089 #define CSI_CCIR_ERR_DET_EN         0x01000000
0090 #define CSI_HORI_DOWNSIZE_EN            0x80000000
0091 #define CSI_VERT_DOWNSIZE_EN            0x40000000
0092 #define CSI_TEST_GEN_MODE_EN            0x01000000
0093 
0094 #define CSI_HSC_MASK                0x1fff0000
0095 #define CSI_HSC_SHIFT               16
0096 #define CSI_VSC_MASK                0x00000fff
0097 #define CSI_VSC_SHIFT               0
0098 
0099 #define CSI_TEST_GEN_R_MASK         0x000000ff
0100 #define CSI_TEST_GEN_R_SHIFT            0
0101 #define CSI_TEST_GEN_G_MASK         0x0000ff00
0102 #define CSI_TEST_GEN_G_SHIFT            8
0103 #define CSI_TEST_GEN_B_MASK         0x00ff0000
0104 #define CSI_TEST_GEN_B_SHIFT            16
0105 
0106 #define CSI_MAX_RATIO_SKIP_SMFC_MASK        0x00000007
0107 #define CSI_MAX_RATIO_SKIP_SMFC_SHIFT       0
0108 #define CSI_SKIP_SMFC_MASK          0x000000f8
0109 #define CSI_SKIP_SMFC_SHIFT         3
0110 #define CSI_ID_2_SKIP_MASK          0x00000300
0111 #define CSI_ID_2_SKIP_SHIFT         8
0112 
0113 #define CSI_COLOR_FIRST_ROW_MASK        0x00000002
0114 #define CSI_COLOR_FIRST_COMP_MASK       0x00000001
0115 
0116 /* MIPI CSI-2 data types */
0117 #define MIPI_DT_YUV420      0x18 /* YYY.../UYVY.... */
0118 #define MIPI_DT_YUV420_LEGACY   0x1a /* UYY.../VYY...   */
0119 #define MIPI_DT_YUV422      0x1e /* UYVY...         */
0120 #define MIPI_DT_RGB444      0x20
0121 #define MIPI_DT_RGB555      0x21
0122 #define MIPI_DT_RGB565      0x22
0123 #define MIPI_DT_RGB666      0x23
0124 #define MIPI_DT_RGB888      0x24
0125 #define MIPI_DT_RAW6        0x28
0126 #define MIPI_DT_RAW7        0x29
0127 #define MIPI_DT_RAW8        0x2a
0128 #define MIPI_DT_RAW10       0x2b
0129 #define MIPI_DT_RAW12       0x2c
0130 #define MIPI_DT_RAW14       0x2d
0131 
0132 /*
0133  * Bitfield of CSI bus signal polarities and modes.
0134  */
0135 struct ipu_csi_bus_config {
0136     unsigned data_width:4;
0137     unsigned clk_mode:3;
0138     unsigned ext_vsync:1;
0139     unsigned vsync_pol:1;
0140     unsigned hsync_pol:1;
0141     unsigned pixclk_pol:1;
0142     unsigned data_pol:1;
0143     unsigned sens_clksrc:1;
0144     unsigned pack_tight:1;
0145     unsigned force_eof:1;
0146     unsigned data_en_pol:1;
0147 
0148     unsigned data_fmt;
0149     unsigned mipi_dt;
0150 };
0151 
0152 /*
0153  * Enumeration of CSI data bus widths.
0154  */
0155 enum ipu_csi_data_width {
0156     IPU_CSI_DATA_WIDTH_4   = 0,
0157     IPU_CSI_DATA_WIDTH_8   = 1,
0158     IPU_CSI_DATA_WIDTH_10  = 3,
0159     IPU_CSI_DATA_WIDTH_12  = 5,
0160     IPU_CSI_DATA_WIDTH_16  = 9,
0161 };
0162 
0163 /*
0164  * Enumeration of CSI clock modes.
0165  */
0166 enum ipu_csi_clk_mode {
0167     IPU_CSI_CLK_MODE_GATED_CLK,
0168     IPU_CSI_CLK_MODE_NONGATED_CLK,
0169     IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE,
0170     IPU_CSI_CLK_MODE_CCIR656_INTERLACED,
0171     IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR,
0172     IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR,
0173     IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR,
0174     IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR,
0175 };
0176 
0177 static inline u32 ipu_csi_read(struct ipu_csi *csi, unsigned offset)
0178 {
0179     return readl(csi->base + offset);
0180 }
0181 
0182 static inline void ipu_csi_write(struct ipu_csi *csi, u32 value,
0183                  unsigned offset)
0184 {
0185     writel(value, csi->base + offset);
0186 }
0187 
0188 /*
0189  * Set mclk division ratio for generating test mode mclk. Only used
0190  * for test generator.
0191  */
0192 static int ipu_csi_set_testgen_mclk(struct ipu_csi *csi, u32 pixel_clk,
0193                     u32 ipu_clk)
0194 {
0195     u32 temp;
0196     int div_ratio;
0197 
0198     div_ratio = (ipu_clk / pixel_clk) - 1;
0199 
0200     if (div_ratio > 0xFF || div_ratio < 0) {
0201         dev_err(csi->ipu->dev,
0202             "value of pixel_clk extends normal range\n");
0203         return -EINVAL;
0204     }
0205 
0206     temp = ipu_csi_read(csi, CSI_SENS_CONF);
0207     temp &= ~CSI_SENS_CONF_DIVRATIO_MASK;
0208     ipu_csi_write(csi, temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT),
0209               CSI_SENS_CONF);
0210 
0211     return 0;
0212 }
0213 
0214 /*
0215  * Find the CSI data format and data width for the given V4L2 media
0216  * bus pixel format code.
0217  */
0218 static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code,
0219                 enum v4l2_mbus_type mbus_type)
0220 {
0221     switch (mbus_code) {
0222     case MEDIA_BUS_FMT_BGR565_2X8_BE:
0223     case MEDIA_BUS_FMT_BGR565_2X8_LE:
0224     case MEDIA_BUS_FMT_RGB565_2X8_BE:
0225     case MEDIA_BUS_FMT_RGB565_2X8_LE:
0226         if (mbus_type == V4L2_MBUS_CSI2_DPHY)
0227             cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565;
0228         else
0229             cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
0230         cfg->mipi_dt = MIPI_DT_RGB565;
0231         cfg->data_width = IPU_CSI_DATA_WIDTH_8;
0232         break;
0233     case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE:
0234     case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE:
0235         cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB444;
0236         cfg->mipi_dt = MIPI_DT_RGB444;
0237         cfg->data_width = IPU_CSI_DATA_WIDTH_8;
0238         break;
0239     case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
0240     case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
0241         cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555;
0242         cfg->mipi_dt = MIPI_DT_RGB555;
0243         cfg->data_width = IPU_CSI_DATA_WIDTH_8;
0244         break;
0245     case MEDIA_BUS_FMT_RGB888_1X24:
0246     case MEDIA_BUS_FMT_BGR888_1X24:
0247         cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444;
0248         cfg->mipi_dt = MIPI_DT_RGB888;
0249         cfg->data_width = IPU_CSI_DATA_WIDTH_8;
0250         break;
0251     case MEDIA_BUS_FMT_UYVY8_2X8:
0252         cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
0253         cfg->mipi_dt = MIPI_DT_YUV422;
0254         cfg->data_width = IPU_CSI_DATA_WIDTH_8;
0255         break;
0256     case MEDIA_BUS_FMT_YUYV8_2X8:
0257         cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
0258         cfg->mipi_dt = MIPI_DT_YUV422;
0259         cfg->data_width = IPU_CSI_DATA_WIDTH_8;
0260         break;
0261     case MEDIA_BUS_FMT_UYVY8_1X16:
0262         if (mbus_type == V4L2_MBUS_BT656) {
0263             cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
0264             cfg->data_width = IPU_CSI_DATA_WIDTH_8;
0265         } else {
0266             cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
0267             cfg->data_width = IPU_CSI_DATA_WIDTH_16;
0268         }
0269         cfg->mipi_dt = MIPI_DT_YUV422;
0270         break;
0271     case MEDIA_BUS_FMT_YUYV8_1X16:
0272         if (mbus_type == V4L2_MBUS_BT656) {
0273             cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
0274             cfg->data_width = IPU_CSI_DATA_WIDTH_8;
0275         } else {
0276             cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
0277             cfg->data_width = IPU_CSI_DATA_WIDTH_16;
0278         }
0279         cfg->mipi_dt = MIPI_DT_YUV422;
0280         break;
0281     case MEDIA_BUS_FMT_SBGGR8_1X8:
0282     case MEDIA_BUS_FMT_SGBRG8_1X8:
0283     case MEDIA_BUS_FMT_SGRBG8_1X8:
0284     case MEDIA_BUS_FMT_SRGGB8_1X8:
0285     case MEDIA_BUS_FMT_Y8_1X8:
0286         cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
0287         cfg->mipi_dt = MIPI_DT_RAW8;
0288         cfg->data_width = IPU_CSI_DATA_WIDTH_8;
0289         break;
0290     case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8:
0291     case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8:
0292     case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
0293     case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8:
0294     case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE:
0295     case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE:
0296     case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE:
0297     case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE:
0298         cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
0299         cfg->mipi_dt = MIPI_DT_RAW10;
0300         cfg->data_width = IPU_CSI_DATA_WIDTH_8;
0301         break;
0302     case MEDIA_BUS_FMT_SBGGR10_1X10:
0303     case MEDIA_BUS_FMT_SGBRG10_1X10:
0304     case MEDIA_BUS_FMT_SGRBG10_1X10:
0305     case MEDIA_BUS_FMT_SRGGB10_1X10:
0306     case MEDIA_BUS_FMT_Y10_1X10:
0307         cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
0308         cfg->mipi_dt = MIPI_DT_RAW10;
0309         cfg->data_width = IPU_CSI_DATA_WIDTH_10;
0310         break;
0311     case MEDIA_BUS_FMT_SBGGR12_1X12:
0312     case MEDIA_BUS_FMT_SGBRG12_1X12:
0313     case MEDIA_BUS_FMT_SGRBG12_1X12:
0314     case MEDIA_BUS_FMT_SRGGB12_1X12:
0315     case MEDIA_BUS_FMT_Y12_1X12:
0316         cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
0317         cfg->mipi_dt = MIPI_DT_RAW12;
0318         cfg->data_width = IPU_CSI_DATA_WIDTH_12;
0319         break;
0320     case MEDIA_BUS_FMT_JPEG_1X8:
0321         /* TODO */
0322         cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_JPEG;
0323         cfg->mipi_dt = MIPI_DT_RAW8;
0324         cfg->data_width = IPU_CSI_DATA_WIDTH_8;
0325         break;
0326     default:
0327         return -EINVAL;
0328     }
0329 
0330     return 0;
0331 }
0332 
0333 /* translate alternate field mode based on given standard */
0334 static inline enum v4l2_field
0335 ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std)
0336 {
0337     return (field != V4L2_FIELD_ALTERNATE) ? field :
0338         ((std & V4L2_STD_525_60) ?
0339          V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB);
0340 }
0341 
0342 /*
0343  * Fill a CSI bus config struct from mbus_config and mbus_framefmt.
0344  */
0345 static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg,
0346                 const struct v4l2_mbus_config *mbus_cfg,
0347                 const struct v4l2_mbus_framefmt *mbus_fmt)
0348 {
0349     int ret, is_bt1120;
0350 
0351     memset(csicfg, 0, sizeof(*csicfg));
0352 
0353     ret = mbus_code_to_bus_cfg(csicfg, mbus_fmt->code, mbus_cfg->type);
0354     if (ret < 0)
0355         return ret;
0356 
0357     switch (mbus_cfg->type) {
0358     case V4L2_MBUS_PARALLEL:
0359         csicfg->ext_vsync = 1;
0360         csicfg->vsync_pol = (mbus_cfg->bus.parallel.flags &
0361                      V4L2_MBUS_VSYNC_ACTIVE_LOW) ? 1 : 0;
0362         csicfg->hsync_pol = (mbus_cfg->bus.parallel.flags &
0363                      V4L2_MBUS_HSYNC_ACTIVE_LOW) ? 1 : 0;
0364         csicfg->pixclk_pol = (mbus_cfg->bus.parallel.flags &
0365                       V4L2_MBUS_PCLK_SAMPLE_FALLING) ? 1 : 0;
0366         csicfg->clk_mode = IPU_CSI_CLK_MODE_GATED_CLK;
0367         break;
0368     case V4L2_MBUS_BT656:
0369         csicfg->ext_vsync = 0;
0370         /* UYVY10_1X20 etc. should be supported as well */
0371         is_bt1120 = mbus_fmt->code == MEDIA_BUS_FMT_UYVY8_1X16 ||
0372                 mbus_fmt->code == MEDIA_BUS_FMT_YUYV8_1X16;
0373         if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field) ||
0374             mbus_fmt->field == V4L2_FIELD_ALTERNATE)
0375             csicfg->clk_mode = is_bt1120 ?
0376                 IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR :
0377                 IPU_CSI_CLK_MODE_CCIR656_INTERLACED;
0378         else
0379             csicfg->clk_mode = is_bt1120 ?
0380                 IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR :
0381                 IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE;
0382         break;
0383     case V4L2_MBUS_CSI2_DPHY:
0384         /*
0385          * MIPI CSI-2 requires non gated clock mode, all other
0386          * parameters are not applicable for MIPI CSI-2 bus.
0387          */
0388         csicfg->clk_mode = IPU_CSI_CLK_MODE_NONGATED_CLK;
0389         break;
0390     default:
0391         /* will never get here, keep compiler quiet */
0392         break;
0393     }
0394 
0395     return 0;
0396 }
0397 
0398 static int
0399 ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi,
0400                 const struct v4l2_mbus_framefmt *infmt,
0401                 const struct v4l2_mbus_framefmt *outfmt,
0402                 v4l2_std_id std)
0403 {
0404     enum v4l2_field infield, outfield;
0405     bool swap_fields;
0406 
0407     /* get translated field type of input and output */
0408     infield = ipu_csi_translate_field(infmt->field, std);
0409     outfield = ipu_csi_translate_field(outfmt->field, std);
0410 
0411     /*
0412      * Write the H-V-F codes the CSI will match against the
0413      * incoming data for start/end of active and blanking
0414      * field intervals. If input and output field types are
0415      * sequential but not the same (one is SEQ_BT and the other
0416      * is SEQ_TB), swap the F-bit so that the CSI will capture
0417      * field 1 lines before field 0 lines.
0418      */
0419     swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) &&
0420                V4L2_FIELD_IS_SEQUENTIAL(outfield) &&
0421                infield != outfield);
0422 
0423     if (!swap_fields) {
0424         /*
0425          * Field0BlankEnd  = 110, Field0BlankStart  = 010
0426          * Field0ActiveEnd = 100, Field0ActiveStart = 000
0427          * Field1BlankEnd  = 111, Field1BlankStart  = 011
0428          * Field1ActiveEnd = 101, Field1ActiveStart = 001
0429          */
0430         ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN,
0431                   CSI_CCIR_CODE_1);
0432         ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2);
0433     } else {
0434         dev_dbg(csi->ipu->dev, "capture field swap\n");
0435 
0436         /* same as above but with F-bit inverted */
0437         ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
0438                   CSI_CCIR_CODE_1);
0439         ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
0440     }
0441 
0442     ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
0443 
0444     return 0;
0445 }
0446 
0447 
0448 int ipu_csi_init_interface(struct ipu_csi *csi,
0449                const struct v4l2_mbus_config *mbus_cfg,
0450                const struct v4l2_mbus_framefmt *infmt,
0451                const struct v4l2_mbus_framefmt *outfmt)
0452 {
0453     struct ipu_csi_bus_config cfg;
0454     unsigned long flags;
0455     u32 width, height, data = 0;
0456     v4l2_std_id std;
0457     int ret;
0458 
0459     ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt);
0460     if (ret < 0)
0461         return ret;
0462 
0463     /* set default sensor frame width and height */
0464     width = infmt->width;
0465     height = infmt->height;
0466     if (infmt->field == V4L2_FIELD_ALTERNATE)
0467         height *= 2;
0468 
0469     /* Set the CSI_SENS_CONF register remaining fields */
0470     data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
0471         cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
0472         cfg.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT |
0473         cfg.vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT |
0474         cfg.hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT |
0475         cfg.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT |
0476         cfg.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT |
0477         cfg.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT |
0478         cfg.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT |
0479         cfg.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT |
0480         cfg.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT;
0481 
0482     spin_lock_irqsave(&csi->lock, flags);
0483 
0484     ipu_csi_write(csi, data, CSI_SENS_CONF);
0485 
0486     /* Set CCIR registers */
0487 
0488     switch (cfg.clk_mode) {
0489     case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
0490         ipu_csi_write(csi, 0x40030, CSI_CCIR_CODE_1);
0491         ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
0492         break;
0493     case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
0494         if (width == 720 && height == 480) {
0495             std = V4L2_STD_NTSC;
0496             height = 525;
0497         } else if (width == 720 && height == 576) {
0498             std = V4L2_STD_PAL;
0499             height = 625;
0500         } else {
0501             dev_err(csi->ipu->dev,
0502                 "Unsupported interlaced video mode\n");
0503             ret = -EINVAL;
0504             goto out_unlock;
0505         }
0506 
0507         ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std);
0508         if (ret)
0509             goto out_unlock;
0510         break;
0511     case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
0512     case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
0513     case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
0514     case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
0515         ipu_csi_write(csi, 0x40030 | CSI_CCIR_ERR_DET_EN,
0516                    CSI_CCIR_CODE_1);
0517         ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
0518         break;
0519     case IPU_CSI_CLK_MODE_GATED_CLK:
0520     case IPU_CSI_CLK_MODE_NONGATED_CLK:
0521         ipu_csi_write(csi, 0, CSI_CCIR_CODE_1);
0522         break;
0523     }
0524 
0525     /* Setup sensor frame size */
0526     ipu_csi_write(csi, (width - 1) | ((height - 1) << 16),
0527               CSI_SENS_FRM_SIZE);
0528 
0529     dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
0530         ipu_csi_read(csi, CSI_SENS_CONF));
0531     dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
0532         ipu_csi_read(csi, CSI_ACT_FRM_SIZE));
0533 
0534 out_unlock:
0535     spin_unlock_irqrestore(&csi->lock, flags);
0536 
0537     return ret;
0538 }
0539 EXPORT_SYMBOL_GPL(ipu_csi_init_interface);
0540 
0541 bool ipu_csi_is_interlaced(struct ipu_csi *csi)
0542 {
0543     unsigned long flags;
0544     u32 sensor_protocol;
0545 
0546     spin_lock_irqsave(&csi->lock, flags);
0547     sensor_protocol =
0548         (ipu_csi_read(csi, CSI_SENS_CONF) &
0549          CSI_SENS_CONF_SENS_PRTCL_MASK) >>
0550         CSI_SENS_CONF_SENS_PRTCL_SHIFT;
0551     spin_unlock_irqrestore(&csi->lock, flags);
0552 
0553     switch (sensor_protocol) {
0554     case IPU_CSI_CLK_MODE_GATED_CLK:
0555     case IPU_CSI_CLK_MODE_NONGATED_CLK:
0556     case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
0557     case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
0558     case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
0559         return false;
0560     case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
0561     case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
0562     case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
0563         return true;
0564     default:
0565         dev_err(csi->ipu->dev,
0566             "CSI %d sensor protocol unsupported\n", csi->id);
0567         return false;
0568     }
0569 }
0570 EXPORT_SYMBOL_GPL(ipu_csi_is_interlaced);
0571 
0572 void ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w)
0573 {
0574     unsigned long flags;
0575     u32 reg;
0576 
0577     spin_lock_irqsave(&csi->lock, flags);
0578 
0579     reg = ipu_csi_read(csi, CSI_ACT_FRM_SIZE);
0580     w->width = (reg & 0xFFFF) + 1;
0581     w->height = (reg >> 16 & 0xFFFF) + 1;
0582 
0583     reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL);
0584     w->left = (reg & CSI_HSC_MASK) >> CSI_HSC_SHIFT;
0585     w->top = (reg & CSI_VSC_MASK) >> CSI_VSC_SHIFT;
0586 
0587     spin_unlock_irqrestore(&csi->lock, flags);
0588 }
0589 EXPORT_SYMBOL_GPL(ipu_csi_get_window);
0590 
0591 void ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w)
0592 {
0593     unsigned long flags;
0594     u32 reg;
0595 
0596     spin_lock_irqsave(&csi->lock, flags);
0597 
0598     ipu_csi_write(csi, (w->width - 1) | ((w->height - 1) << 16),
0599               CSI_ACT_FRM_SIZE);
0600 
0601     reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL);
0602     reg &= ~(CSI_HSC_MASK | CSI_VSC_MASK);
0603     reg |= ((w->top << CSI_VSC_SHIFT) | (w->left << CSI_HSC_SHIFT));
0604     ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL);
0605 
0606     spin_unlock_irqrestore(&csi->lock, flags);
0607 }
0608 EXPORT_SYMBOL_GPL(ipu_csi_set_window);
0609 
0610 void ipu_csi_set_downsize(struct ipu_csi *csi, bool horiz, bool vert)
0611 {
0612     unsigned long flags;
0613     u32 reg;
0614 
0615     spin_lock_irqsave(&csi->lock, flags);
0616 
0617     reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL);
0618     reg &= ~(CSI_HORI_DOWNSIZE_EN | CSI_VERT_DOWNSIZE_EN);
0619     reg |= (horiz ? CSI_HORI_DOWNSIZE_EN : 0) |
0620            (vert ? CSI_VERT_DOWNSIZE_EN : 0);
0621     ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL);
0622 
0623     spin_unlock_irqrestore(&csi->lock, flags);
0624 }
0625 EXPORT_SYMBOL_GPL(ipu_csi_set_downsize);
0626 
0627 void ipu_csi_set_test_generator(struct ipu_csi *csi, bool active,
0628                 u32 r_value, u32 g_value, u32 b_value,
0629                 u32 pix_clk)
0630 {
0631     unsigned long flags;
0632     u32 ipu_clk = clk_get_rate(csi->clk_ipu);
0633     u32 temp;
0634 
0635     spin_lock_irqsave(&csi->lock, flags);
0636 
0637     temp = ipu_csi_read(csi, CSI_TST_CTRL);
0638 
0639     if (!active) {
0640         temp &= ~CSI_TEST_GEN_MODE_EN;
0641         ipu_csi_write(csi, temp, CSI_TST_CTRL);
0642     } else {
0643         /* Set sensb_mclk div_ratio */
0644         ipu_csi_set_testgen_mclk(csi, pix_clk, ipu_clk);
0645 
0646         temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK |
0647               CSI_TEST_GEN_B_MASK);
0648         temp |= CSI_TEST_GEN_MODE_EN;
0649         temp |= (r_value << CSI_TEST_GEN_R_SHIFT) |
0650             (g_value << CSI_TEST_GEN_G_SHIFT) |
0651             (b_value << CSI_TEST_GEN_B_SHIFT);
0652         ipu_csi_write(csi, temp, CSI_TST_CTRL);
0653     }
0654 
0655     spin_unlock_irqrestore(&csi->lock, flags);
0656 }
0657 EXPORT_SYMBOL_GPL(ipu_csi_set_test_generator);
0658 
0659 int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc,
0660                   struct v4l2_mbus_framefmt *mbus_fmt)
0661 {
0662     struct ipu_csi_bus_config cfg;
0663     unsigned long flags;
0664     u32 temp;
0665     int ret;
0666 
0667     if (vc > 3)
0668         return -EINVAL;
0669 
0670     ret = mbus_code_to_bus_cfg(&cfg, mbus_fmt->code, V4L2_MBUS_CSI2_DPHY);
0671     if (ret < 0)
0672         return ret;
0673 
0674     spin_lock_irqsave(&csi->lock, flags);
0675 
0676     temp = ipu_csi_read(csi, CSI_MIPI_DI);
0677     temp &= ~(0xff << (vc * 8));
0678     temp |= (cfg.mipi_dt << (vc * 8));
0679     ipu_csi_write(csi, temp, CSI_MIPI_DI);
0680 
0681     spin_unlock_irqrestore(&csi->lock, flags);
0682 
0683     return 0;
0684 }
0685 EXPORT_SYMBOL_GPL(ipu_csi_set_mipi_datatype);
0686 
0687 int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip,
0688               u32 max_ratio, u32 id)
0689 {
0690     unsigned long flags;
0691     u32 temp;
0692 
0693     if (max_ratio > 5 || id > 3)
0694         return -EINVAL;
0695 
0696     spin_lock_irqsave(&csi->lock, flags);
0697 
0698     temp = ipu_csi_read(csi, CSI_SKIP);
0699     temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK |
0700           CSI_SKIP_SMFC_MASK);
0701     temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) |
0702         (id << CSI_ID_2_SKIP_SHIFT) |
0703         (skip << CSI_SKIP_SMFC_SHIFT);
0704     ipu_csi_write(csi, temp, CSI_SKIP);
0705 
0706     spin_unlock_irqrestore(&csi->lock, flags);
0707 
0708     return 0;
0709 }
0710 EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc);
0711 
0712 int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest)
0713 {
0714     unsigned long flags;
0715     u32 csi_sens_conf, dest;
0716 
0717     if (csi_dest == IPU_CSI_DEST_IDMAC)
0718         dest = CSI_DATA_DEST_IDMAC;
0719     else
0720         dest = CSI_DATA_DEST_IC; /* IC or VDIC */
0721 
0722     spin_lock_irqsave(&csi->lock, flags);
0723 
0724     csi_sens_conf = ipu_csi_read(csi, CSI_SENS_CONF);
0725     csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK;
0726     csi_sens_conf |= (dest << CSI_SENS_CONF_DATA_DEST_SHIFT);
0727     ipu_csi_write(csi, csi_sens_conf, CSI_SENS_CONF);
0728 
0729     spin_unlock_irqrestore(&csi->lock, flags);
0730 
0731     return 0;
0732 }
0733 EXPORT_SYMBOL_GPL(ipu_csi_set_dest);
0734 
0735 int ipu_csi_enable(struct ipu_csi *csi)
0736 {
0737     ipu_module_enable(csi->ipu, csi->module);
0738 
0739     return 0;
0740 }
0741 EXPORT_SYMBOL_GPL(ipu_csi_enable);
0742 
0743 int ipu_csi_disable(struct ipu_csi *csi)
0744 {
0745     ipu_module_disable(csi->ipu, csi->module);
0746 
0747     return 0;
0748 }
0749 EXPORT_SYMBOL_GPL(ipu_csi_disable);
0750 
0751 struct ipu_csi *ipu_csi_get(struct ipu_soc *ipu, int id)
0752 {
0753     unsigned long flags;
0754     struct ipu_csi *csi, *ret;
0755 
0756     if (id > 1)
0757         return ERR_PTR(-EINVAL);
0758 
0759     csi = ipu->csi_priv[id];
0760     ret = csi;
0761 
0762     spin_lock_irqsave(&csi->lock, flags);
0763 
0764     if (csi->inuse) {
0765         ret = ERR_PTR(-EBUSY);
0766         goto unlock;
0767     }
0768 
0769     csi->inuse = true;
0770 unlock:
0771     spin_unlock_irqrestore(&csi->lock, flags);
0772     return ret;
0773 }
0774 EXPORT_SYMBOL_GPL(ipu_csi_get);
0775 
0776 void ipu_csi_put(struct ipu_csi *csi)
0777 {
0778     unsigned long flags;
0779 
0780     spin_lock_irqsave(&csi->lock, flags);
0781     csi->inuse = false;
0782     spin_unlock_irqrestore(&csi->lock, flags);
0783 }
0784 EXPORT_SYMBOL_GPL(ipu_csi_put);
0785 
0786 int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id,
0787          unsigned long base, u32 module, struct clk *clk_ipu)
0788 {
0789     struct ipu_csi *csi;
0790 
0791     if (id > 1)
0792         return -ENODEV;
0793 
0794     csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL);
0795     if (!csi)
0796         return -ENOMEM;
0797 
0798     ipu->csi_priv[id] = csi;
0799 
0800     spin_lock_init(&csi->lock);
0801     csi->module = module;
0802     csi->id = id;
0803     csi->clk_ipu = clk_ipu;
0804     csi->base = devm_ioremap(dev, base, PAGE_SIZE);
0805     if (!csi->base)
0806         return -ENOMEM;
0807 
0808     dev_dbg(dev, "CSI%d base: 0x%08lx remapped to %p\n",
0809         id, base, csi->base);
0810     csi->ipu = ipu;
0811 
0812     return 0;
0813 }
0814 
0815 void ipu_csi_exit(struct ipu_soc *ipu, int id)
0816 {
0817 }
0818 
0819 void ipu_csi_dump(struct ipu_csi *csi)
0820 {
0821     dev_dbg(csi->ipu->dev, "CSI_SENS_CONF:     %08x\n",
0822         ipu_csi_read(csi, CSI_SENS_CONF));
0823     dev_dbg(csi->ipu->dev, "CSI_SENS_FRM_SIZE: %08x\n",
0824         ipu_csi_read(csi, CSI_SENS_FRM_SIZE));
0825     dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE:  %08x\n",
0826         ipu_csi_read(csi, CSI_ACT_FRM_SIZE));
0827     dev_dbg(csi->ipu->dev, "CSI_OUT_FRM_CTRL:  %08x\n",
0828         ipu_csi_read(csi, CSI_OUT_FRM_CTRL));
0829     dev_dbg(csi->ipu->dev, "CSI_TST_CTRL:      %08x\n",
0830         ipu_csi_read(csi, CSI_TST_CTRL));
0831     dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_1:   %08x\n",
0832         ipu_csi_read(csi, CSI_CCIR_CODE_1));
0833     dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_2:   %08x\n",
0834         ipu_csi_read(csi, CSI_CCIR_CODE_2));
0835     dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_3:   %08x\n",
0836         ipu_csi_read(csi, CSI_CCIR_CODE_3));
0837     dev_dbg(csi->ipu->dev, "CSI_MIPI_DI:       %08x\n",
0838         ipu_csi_read(csi, CSI_MIPI_DI));
0839     dev_dbg(csi->ipu->dev, "CSI_SKIP:          %08x\n",
0840         ipu_csi_read(csi, CSI_SKIP));
0841 }
0842 EXPORT_SYMBOL_GPL(ipu_csi_dump);