0001
0002
0003
0004
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;
0027 spinlock_t lock;
0028 bool inuse;
0029 struct ipu_soc *ipu;
0030 };
0031
0032
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
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
0117 #define MIPI_DT_YUV420 0x18
0118 #define MIPI_DT_YUV420_LEGACY 0x1a
0119 #define MIPI_DT_YUV422 0x1e
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
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
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
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
0190
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
0216
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
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
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
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
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
0386
0387
0388 csicfg->clk_mode = IPU_CSI_CLK_MODE_NONGATED_CLK;
0389 break;
0390 default:
0391
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
0408 infield = ipu_csi_translate_field(infmt->field, std);
0409 outfield = ipu_csi_translate_field(outfmt->field, std);
0410
0411
0412
0413
0414
0415
0416
0417
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
0426
0427
0428
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
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
0464 width = infmt->width;
0465 height = infmt->height;
0466 if (infmt->field == V4L2_FIELD_ALTERNATE)
0467 height *= 2;
0468
0469
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
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
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
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;
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);