1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2012-2014 Mentor Graphics Inc.
4 * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
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>
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>
26 struct clk
*clk_ipu
; /* IPU bus clock */
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
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
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
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
86 #define CSI_DATA_DEST_IC 2
87 #define CSI_DATA_DEST_IDMAC 4
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
94 #define CSI_HSC_MASK 0x1fff0000
95 #define CSI_HSC_SHIFT 16
96 #define CSI_VSC_MASK 0x00000fff
97 #define CSI_VSC_SHIFT 0
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
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
113 #define CSI_COLOR_FIRST_ROW_MASK 0x00000002
114 #define CSI_COLOR_FIRST_COMP_MASK 0x00000001
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
133 * Bitfield of CSI bus signal polarities and modes.
135 struct ipu_csi_bus_config
{
136 unsigned data_width
:4;
138 unsigned ext_vsync
:1;
139 unsigned vsync_pol
:1;
140 unsigned hsync_pol
:1;
141 unsigned pixclk_pol
:1;
143 unsigned sens_clksrc
:1;
144 unsigned pack_tight
:1;
145 unsigned force_eof
:1;
146 unsigned data_en_pol
:1;
153 * Enumeration of CSI data bus widths.
155 enum 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,
164 * Enumeration of CSI clock modes.
166 enum 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
,
177 static inline u32
ipu_csi_read(struct ipu_csi
*csi
, unsigned offset
)
179 return readl(csi
->base
+ offset
);
182 static inline void ipu_csi_write(struct ipu_csi
*csi
, u32 value
,
185 writel(value
, csi
->base
+ offset
);
189 * Set mclk division ratio for generating test mode mclk. Only used
190 * for test generator.
192 static int ipu_csi_set_testgen_mclk(struct ipu_csi
*csi
, u32 pixel_clk
,
198 div_ratio
= (ipu_clk
/ pixel_clk
) - 1;
200 if (div_ratio
> 0xFF || div_ratio
< 0) {
201 dev_err(csi
->ipu
->dev
,
202 "value of pixel_clk extends normal range\n");
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
),
215 * Find the CSI data format and data width for the given V4L2 media
216 * bus pixel format code.
218 static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config
*cfg
, u32 mbus_code
,
219 enum v4l2_mbus_type mbus_type
)
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
;
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
;
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
;
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
;
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
;
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
;
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
;
261 case MEDIA_BUS_FMT_UYVY8_1X16
:
262 if (mbus_type
== V4L2_MBUS_BT656
) {
263 cfg
->data_fmt
= CSI_SENS_CONF_DATA_FMT_YUV422_UYVY
;
264 cfg
->data_width
= IPU_CSI_DATA_WIDTH_8
;
266 cfg
->data_fmt
= CSI_SENS_CONF_DATA_FMT_BAYER
;
267 cfg
->data_width
= IPU_CSI_DATA_WIDTH_16
;
269 cfg
->mipi_dt
= MIPI_DT_YUV422
;
271 case MEDIA_BUS_FMT_YUYV8_1X16
:
272 if (mbus_type
== V4L2_MBUS_BT656
) {
273 cfg
->data_fmt
= CSI_SENS_CONF_DATA_FMT_YUV422_YUYV
;
274 cfg
->data_width
= IPU_CSI_DATA_WIDTH_8
;
276 cfg
->data_fmt
= CSI_SENS_CONF_DATA_FMT_BAYER
;
277 cfg
->data_width
= IPU_CSI_DATA_WIDTH_16
;
279 cfg
->mipi_dt
= MIPI_DT_YUV422
;
281 case MEDIA_BUS_FMT_SBGGR8_1X8
:
282 case MEDIA_BUS_FMT_SGBRG8_1X8
:
283 case MEDIA_BUS_FMT_SGRBG8_1X8
:
284 case MEDIA_BUS_FMT_SRGGB8_1X8
:
285 case MEDIA_BUS_FMT_Y8_1X8
:
286 cfg
->data_fmt
= CSI_SENS_CONF_DATA_FMT_BAYER
;
287 cfg
->mipi_dt
= MIPI_DT_RAW8
;
288 cfg
->data_width
= IPU_CSI_DATA_WIDTH_8
;
290 case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8
:
291 case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8
:
292 case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8
:
293 case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8
:
294 case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE
:
295 case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE
:
296 case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE
:
297 case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE
:
298 cfg
->data_fmt
= CSI_SENS_CONF_DATA_FMT_BAYER
;
299 cfg
->mipi_dt
= MIPI_DT_RAW10
;
300 cfg
->data_width
= IPU_CSI_DATA_WIDTH_8
;
302 case MEDIA_BUS_FMT_SBGGR10_1X10
:
303 case MEDIA_BUS_FMT_SGBRG10_1X10
:
304 case MEDIA_BUS_FMT_SGRBG10_1X10
:
305 case MEDIA_BUS_FMT_SRGGB10_1X10
:
306 case MEDIA_BUS_FMT_Y10_1X10
:
307 cfg
->data_fmt
= CSI_SENS_CONF_DATA_FMT_BAYER
;
308 cfg
->mipi_dt
= MIPI_DT_RAW10
;
309 cfg
->data_width
= IPU_CSI_DATA_WIDTH_10
;
311 case MEDIA_BUS_FMT_SBGGR12_1X12
:
312 case MEDIA_BUS_FMT_SGBRG12_1X12
:
313 case MEDIA_BUS_FMT_SGRBG12_1X12
:
314 case MEDIA_BUS_FMT_SRGGB12_1X12
:
315 case MEDIA_BUS_FMT_Y12_1X12
:
316 cfg
->data_fmt
= CSI_SENS_CONF_DATA_FMT_BAYER
;
317 cfg
->mipi_dt
= MIPI_DT_RAW12
;
318 cfg
->data_width
= IPU_CSI_DATA_WIDTH_12
;
320 case MEDIA_BUS_FMT_JPEG_1X8
:
322 cfg
->data_fmt
= CSI_SENS_CONF_DATA_FMT_JPEG
;
323 cfg
->mipi_dt
= MIPI_DT_RAW8
;
324 cfg
->data_width
= IPU_CSI_DATA_WIDTH_8
;
333 /* translate alternate field mode based on given standard */
334 static inline enum v4l2_field
335 ipu_csi_translate_field(enum v4l2_field field
, v4l2_std_id std
)
337 return (field
!= V4L2_FIELD_ALTERNATE
) ? field
:
338 ((std
& V4L2_STD_525_60
) ?
339 V4L2_FIELD_SEQ_BT
: V4L2_FIELD_SEQ_TB
);
343 * Fill a CSI bus config struct from mbus_config and mbus_framefmt.
345 static int fill_csi_bus_cfg(struct ipu_csi_bus_config
*csicfg
,
346 const struct v4l2_mbus_config
*mbus_cfg
,
347 const struct v4l2_mbus_framefmt
*mbus_fmt
)
351 memset(csicfg
, 0, sizeof(*csicfg
));
353 ret
= mbus_code_to_bus_cfg(csicfg
, mbus_fmt
->code
, mbus_cfg
->type
);
357 switch (mbus_cfg
->type
) {
358 case V4L2_MBUS_PARALLEL
:
359 csicfg
->ext_vsync
= 1;
360 csicfg
->vsync_pol
= (mbus_cfg
->bus
.parallel
.flags
&
361 V4L2_MBUS_VSYNC_ACTIVE_LOW
) ? 1 : 0;
362 csicfg
->hsync_pol
= (mbus_cfg
->bus
.parallel
.flags
&
363 V4L2_MBUS_HSYNC_ACTIVE_LOW
) ? 1 : 0;
364 csicfg
->pixclk_pol
= (mbus_cfg
->bus
.parallel
.flags
&
365 V4L2_MBUS_PCLK_SAMPLE_FALLING
) ? 1 : 0;
366 csicfg
->clk_mode
= IPU_CSI_CLK_MODE_GATED_CLK
;
368 case V4L2_MBUS_BT656
:
369 csicfg
->ext_vsync
= 0;
370 /* UYVY10_1X20 etc. should be supported as well */
371 is_bt1120
= mbus_fmt
->code
== MEDIA_BUS_FMT_UYVY8_1X16
||
372 mbus_fmt
->code
== MEDIA_BUS_FMT_YUYV8_1X16
;
373 if (V4L2_FIELD_HAS_BOTH(mbus_fmt
->field
) ||
374 mbus_fmt
->field
== V4L2_FIELD_ALTERNATE
)
375 csicfg
->clk_mode
= is_bt1120
?
376 IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR
:
377 IPU_CSI_CLK_MODE_CCIR656_INTERLACED
;
379 csicfg
->clk_mode
= is_bt1120
?
380 IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR
:
381 IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE
;
383 case V4L2_MBUS_CSI2_DPHY
:
385 * MIPI CSI-2 requires non gated clock mode, all other
386 * parameters are not applicable for MIPI CSI-2 bus.
388 csicfg
->clk_mode
= IPU_CSI_CLK_MODE_NONGATED_CLK
;
391 /* will never get here, keep compiler quiet */
399 ipu_csi_set_bt_interlaced_codes(struct ipu_csi
*csi
,
400 const struct v4l2_mbus_framefmt
*infmt
,
401 const struct v4l2_mbus_framefmt
*outfmt
,
404 enum v4l2_field infield
, outfield
;
407 /* get translated field type of input and output */
408 infield
= ipu_csi_translate_field(infmt
->field
, std
);
409 outfield
= ipu_csi_translate_field(outfmt
->field
, std
);
412 * Write the H-V-F codes the CSI will match against the
413 * incoming data for start/end of active and blanking
414 * field intervals. If input and output field types are
415 * sequential but not the same (one is SEQ_BT and the other
416 * is SEQ_TB), swap the F-bit so that the CSI will capture
417 * field 1 lines before field 0 lines.
419 swap_fields
= (V4L2_FIELD_IS_SEQUENTIAL(infield
) &&
420 V4L2_FIELD_IS_SEQUENTIAL(outfield
) &&
421 infield
!= outfield
);
425 * Field0BlankEnd = 110, Field0BlankStart = 010
426 * Field0ActiveEnd = 100, Field0ActiveStart = 000
427 * Field1BlankEnd = 111, Field1BlankStart = 011
428 * Field1ActiveEnd = 101, Field1ActiveStart = 001
430 ipu_csi_write(csi
, 0x40596 | CSI_CCIR_ERR_DET_EN
,
432 ipu_csi_write(csi
, 0xD07DF, CSI_CCIR_CODE_2
);
434 dev_dbg(csi
->ipu
->dev
, "capture field swap\n");
436 /* same as above but with F-bit inverted */
437 ipu_csi_write(csi
, 0xD07DF | CSI_CCIR_ERR_DET_EN
,
439 ipu_csi_write(csi
, 0x40596, CSI_CCIR_CODE_2
);
442 ipu_csi_write(csi
, 0xFF0000, CSI_CCIR_CODE_3
);
448 int ipu_csi_init_interface(struct ipu_csi
*csi
,
449 const struct v4l2_mbus_config
*mbus_cfg
,
450 const struct v4l2_mbus_framefmt
*infmt
,
451 const struct v4l2_mbus_framefmt
*outfmt
)
453 struct ipu_csi_bus_config cfg
;
455 u32 width
, height
, data
= 0;
459 ret
= fill_csi_bus_cfg(&cfg
, mbus_cfg
, infmt
);
463 /* set default sensor frame width and height */
464 width
= infmt
->width
;
465 height
= infmt
->height
;
466 if (infmt
->field
== V4L2_FIELD_ALTERNATE
)
469 /* Set the CSI_SENS_CONF register remaining fields */
470 data
|= cfg
.data_width
<< CSI_SENS_CONF_DATA_WIDTH_SHIFT
|
471 cfg
.data_fmt
<< CSI_SENS_CONF_DATA_FMT_SHIFT
|
472 cfg
.data_pol
<< CSI_SENS_CONF_DATA_POL_SHIFT
|
473 cfg
.vsync_pol
<< CSI_SENS_CONF_VSYNC_POL_SHIFT
|
474 cfg
.hsync_pol
<< CSI_SENS_CONF_HSYNC_POL_SHIFT
|
475 cfg
.pixclk_pol
<< CSI_SENS_CONF_PIX_CLK_POL_SHIFT
|
476 cfg
.ext_vsync
<< CSI_SENS_CONF_EXT_VSYNC_SHIFT
|
477 cfg
.clk_mode
<< CSI_SENS_CONF_SENS_PRTCL_SHIFT
|
478 cfg
.pack_tight
<< CSI_SENS_CONF_PACK_TIGHT_SHIFT
|
479 cfg
.force_eof
<< CSI_SENS_CONF_FORCE_EOF_SHIFT
|
480 cfg
.data_en_pol
<< CSI_SENS_CONF_DATA_EN_POL_SHIFT
;
482 spin_lock_irqsave(&csi
->lock
, flags
);
484 ipu_csi_write(csi
, data
, CSI_SENS_CONF
);
486 /* Set CCIR registers */
488 switch (cfg
.clk_mode
) {
489 case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE
:
490 ipu_csi_write(csi
, 0x40030, CSI_CCIR_CODE_1
);
491 ipu_csi_write(csi
, 0xFF0000, CSI_CCIR_CODE_3
);
493 case IPU_CSI_CLK_MODE_CCIR656_INTERLACED
:
494 if (width
== 720 && height
== 480) {
497 } else if (width
== 720 && height
== 576) {
501 dev_err(csi
->ipu
->dev
,
502 "Unsupported interlaced video mode\n");
507 ret
= ipu_csi_set_bt_interlaced_codes(csi
, infmt
, outfmt
, std
);
511 case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR
:
512 case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR
:
513 case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR
:
514 case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR
:
515 ipu_csi_write(csi
, 0x40030 | CSI_CCIR_ERR_DET_EN
,
517 ipu_csi_write(csi
, 0xFF0000, CSI_CCIR_CODE_3
);
519 case IPU_CSI_CLK_MODE_GATED_CLK
:
520 case IPU_CSI_CLK_MODE_NONGATED_CLK
:
521 ipu_csi_write(csi
, 0, CSI_CCIR_CODE_1
);
525 /* Setup sensor frame size */
526 ipu_csi_write(csi
, (width
- 1) | ((height
- 1) << 16),
529 dev_dbg(csi
->ipu
->dev
, "CSI_SENS_CONF = 0x%08X\n",
530 ipu_csi_read(csi
, CSI_SENS_CONF
));
531 dev_dbg(csi
->ipu
->dev
, "CSI_ACT_FRM_SIZE = 0x%08X\n",
532 ipu_csi_read(csi
, CSI_ACT_FRM_SIZE
));
535 spin_unlock_irqrestore(&csi
->lock
, flags
);
539 EXPORT_SYMBOL_GPL(ipu_csi_init_interface
);
541 bool ipu_csi_is_interlaced(struct ipu_csi
*csi
)
546 spin_lock_irqsave(&csi
->lock
, flags
);
548 (ipu_csi_read(csi
, CSI_SENS_CONF
) &
549 CSI_SENS_CONF_SENS_PRTCL_MASK
) >>
550 CSI_SENS_CONF_SENS_PRTCL_SHIFT
;
551 spin_unlock_irqrestore(&csi
->lock
, flags
);
553 switch (sensor_protocol
) {
554 case IPU_CSI_CLK_MODE_GATED_CLK
:
555 case IPU_CSI_CLK_MODE_NONGATED_CLK
:
556 case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE
:
557 case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR
:
558 case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR
:
560 case IPU_CSI_CLK_MODE_CCIR656_INTERLACED
:
561 case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR
:
562 case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR
:
565 dev_err(csi
->ipu
->dev
,
566 "CSI %d sensor protocol unsupported\n", csi
->id
);
570 EXPORT_SYMBOL_GPL(ipu_csi_is_interlaced
);
572 void ipu_csi_get_window(struct ipu_csi
*csi
, struct v4l2_rect
*w
)
577 spin_lock_irqsave(&csi
->lock
, flags
);
579 reg
= ipu_csi_read(csi
, CSI_ACT_FRM_SIZE
);
580 w
->width
= (reg
& 0xFFFF) + 1;
581 w
->height
= (reg
>> 16 & 0xFFFF) + 1;
583 reg
= ipu_csi_read(csi
, CSI_OUT_FRM_CTRL
);
584 w
->left
= (reg
& CSI_HSC_MASK
) >> CSI_HSC_SHIFT
;
585 w
->top
= (reg
& CSI_VSC_MASK
) >> CSI_VSC_SHIFT
;
587 spin_unlock_irqrestore(&csi
->lock
, flags
);
589 EXPORT_SYMBOL_GPL(ipu_csi_get_window
);
591 void ipu_csi_set_window(struct ipu_csi
*csi
, struct v4l2_rect
*w
)
596 spin_lock_irqsave(&csi
->lock
, flags
);
598 ipu_csi_write(csi
, (w
->width
- 1) | ((w
->height
- 1) << 16),
601 reg
= ipu_csi_read(csi
, CSI_OUT_FRM_CTRL
);
602 reg
&= ~(CSI_HSC_MASK
| CSI_VSC_MASK
);
603 reg
|= ((w
->top
<< CSI_VSC_SHIFT
) | (w
->left
<< CSI_HSC_SHIFT
));
604 ipu_csi_write(csi
, reg
, CSI_OUT_FRM_CTRL
);
606 spin_unlock_irqrestore(&csi
->lock
, flags
);
608 EXPORT_SYMBOL_GPL(ipu_csi_set_window
);
610 void ipu_csi_set_downsize(struct ipu_csi
*csi
, bool horiz
, bool vert
)
615 spin_lock_irqsave(&csi
->lock
, flags
);
617 reg
= ipu_csi_read(csi
, CSI_OUT_FRM_CTRL
);
618 reg
&= ~(CSI_HORI_DOWNSIZE_EN
| CSI_VERT_DOWNSIZE_EN
);
619 reg
|= (horiz
? CSI_HORI_DOWNSIZE_EN
: 0) |
620 (vert
? CSI_VERT_DOWNSIZE_EN
: 0);
621 ipu_csi_write(csi
, reg
, CSI_OUT_FRM_CTRL
);
623 spin_unlock_irqrestore(&csi
->lock
, flags
);
625 EXPORT_SYMBOL_GPL(ipu_csi_set_downsize
);
627 void ipu_csi_set_test_generator(struct ipu_csi
*csi
, bool active
,
628 u32 r_value
, u32 g_value
, u32 b_value
,
632 u32 ipu_clk
= clk_get_rate(csi
->clk_ipu
);
635 spin_lock_irqsave(&csi
->lock
, flags
);
637 temp
= ipu_csi_read(csi
, CSI_TST_CTRL
);
640 temp
&= ~CSI_TEST_GEN_MODE_EN
;
641 ipu_csi_write(csi
, temp
, CSI_TST_CTRL
);
643 /* Set sensb_mclk div_ratio */
644 ipu_csi_set_testgen_mclk(csi
, pix_clk
, ipu_clk
);
646 temp
&= ~(CSI_TEST_GEN_R_MASK
| CSI_TEST_GEN_G_MASK
|
647 CSI_TEST_GEN_B_MASK
);
648 temp
|= CSI_TEST_GEN_MODE_EN
;
649 temp
|= (r_value
<< CSI_TEST_GEN_R_SHIFT
) |
650 (g_value
<< CSI_TEST_GEN_G_SHIFT
) |
651 (b_value
<< CSI_TEST_GEN_B_SHIFT
);
652 ipu_csi_write(csi
, temp
, CSI_TST_CTRL
);
655 spin_unlock_irqrestore(&csi
->lock
, flags
);
657 EXPORT_SYMBOL_GPL(ipu_csi_set_test_generator
);
659 int ipu_csi_set_mipi_datatype(struct ipu_csi
*csi
, u32 vc
,
660 struct v4l2_mbus_framefmt
*mbus_fmt
)
662 struct ipu_csi_bus_config cfg
;
670 ret
= mbus_code_to_bus_cfg(&cfg
, mbus_fmt
->code
, V4L2_MBUS_CSI2_DPHY
);
674 spin_lock_irqsave(&csi
->lock
, flags
);
676 temp
= ipu_csi_read(csi
, CSI_MIPI_DI
);
677 temp
&= ~(0xff << (vc
* 8));
678 temp
|= (cfg
.mipi_dt
<< (vc
* 8));
679 ipu_csi_write(csi
, temp
, CSI_MIPI_DI
);
681 spin_unlock_irqrestore(&csi
->lock
, flags
);
685 EXPORT_SYMBOL_GPL(ipu_csi_set_mipi_datatype
);
687 int ipu_csi_set_skip_smfc(struct ipu_csi
*csi
, u32 skip
,
688 u32 max_ratio
, u32 id
)
693 if (max_ratio
> 5 || id
> 3)
696 spin_lock_irqsave(&csi
->lock
, flags
);
698 temp
= ipu_csi_read(csi
, CSI_SKIP
);
699 temp
&= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK
| CSI_ID_2_SKIP_MASK
|
701 temp
|= (max_ratio
<< CSI_MAX_RATIO_SKIP_SMFC_SHIFT
) |
702 (id
<< CSI_ID_2_SKIP_SHIFT
) |
703 (skip
<< CSI_SKIP_SMFC_SHIFT
);
704 ipu_csi_write(csi
, temp
, CSI_SKIP
);
706 spin_unlock_irqrestore(&csi
->lock
, flags
);
710 EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc
);
712 int ipu_csi_set_dest(struct ipu_csi
*csi
, enum ipu_csi_dest csi_dest
)
715 u32 csi_sens_conf
, dest
;
717 if (csi_dest
== IPU_CSI_DEST_IDMAC
)
718 dest
= CSI_DATA_DEST_IDMAC
;
720 dest
= CSI_DATA_DEST_IC
; /* IC or VDIC */
722 spin_lock_irqsave(&csi
->lock
, flags
);
724 csi_sens_conf
= ipu_csi_read(csi
, CSI_SENS_CONF
);
725 csi_sens_conf
&= ~CSI_SENS_CONF_DATA_DEST_MASK
;
726 csi_sens_conf
|= (dest
<< CSI_SENS_CONF_DATA_DEST_SHIFT
);
727 ipu_csi_write(csi
, csi_sens_conf
, CSI_SENS_CONF
);
729 spin_unlock_irqrestore(&csi
->lock
, flags
);
733 EXPORT_SYMBOL_GPL(ipu_csi_set_dest
);
735 int ipu_csi_enable(struct ipu_csi
*csi
)
737 ipu_module_enable(csi
->ipu
, csi
->module
);
741 EXPORT_SYMBOL_GPL(ipu_csi_enable
);
743 int ipu_csi_disable(struct ipu_csi
*csi
)
745 ipu_module_disable(csi
->ipu
, csi
->module
);
749 EXPORT_SYMBOL_GPL(ipu_csi_disable
);
751 struct ipu_csi
*ipu_csi_get(struct ipu_soc
*ipu
, int id
)
754 struct ipu_csi
*csi
, *ret
;
757 return ERR_PTR(-EINVAL
);
759 csi
= ipu
->csi_priv
[id
];
762 spin_lock_irqsave(&csi
->lock
, flags
);
765 ret
= ERR_PTR(-EBUSY
);
771 spin_unlock_irqrestore(&csi
->lock
, flags
);
774 EXPORT_SYMBOL_GPL(ipu_csi_get
);
776 void ipu_csi_put(struct ipu_csi
*csi
)
780 spin_lock_irqsave(&csi
->lock
, flags
);
782 spin_unlock_irqrestore(&csi
->lock
, flags
);
784 EXPORT_SYMBOL_GPL(ipu_csi_put
);
786 int ipu_csi_init(struct ipu_soc
*ipu
, struct device
*dev
, int id
,
787 unsigned long base
, u32 module
, struct clk
*clk_ipu
)
794 csi
= devm_kzalloc(dev
, sizeof(*csi
), GFP_KERNEL
);
798 ipu
->csi_priv
[id
] = csi
;
800 spin_lock_init(&csi
->lock
);
801 csi
->module
= module
;
803 csi
->clk_ipu
= clk_ipu
;
804 csi
->base
= devm_ioremap(dev
, base
, PAGE_SIZE
);
808 dev_dbg(dev
, "CSI%d base: 0x%08lx remapped to %p\n",
809 id
, base
, csi
->base
);
815 void ipu_csi_exit(struct ipu_soc
*ipu
, int id
)
819 void ipu_csi_dump(struct ipu_csi
*csi
)
821 dev_dbg(csi
->ipu
->dev
, "CSI_SENS_CONF: %08x\n",
822 ipu_csi_read(csi
, CSI_SENS_CONF
));
823 dev_dbg(csi
->ipu
->dev
, "CSI_SENS_FRM_SIZE: %08x\n",
824 ipu_csi_read(csi
, CSI_SENS_FRM_SIZE
));
825 dev_dbg(csi
->ipu
->dev
, "CSI_ACT_FRM_SIZE: %08x\n",
826 ipu_csi_read(csi
, CSI_ACT_FRM_SIZE
));
827 dev_dbg(csi
->ipu
->dev
, "CSI_OUT_FRM_CTRL: %08x\n",
828 ipu_csi_read(csi
, CSI_OUT_FRM_CTRL
));
829 dev_dbg(csi
->ipu
->dev
, "CSI_TST_CTRL: %08x\n",
830 ipu_csi_read(csi
, CSI_TST_CTRL
));
831 dev_dbg(csi
->ipu
->dev
, "CSI_CCIR_CODE_1: %08x\n",
832 ipu_csi_read(csi
, CSI_CCIR_CODE_1
));
833 dev_dbg(csi
->ipu
->dev
, "CSI_CCIR_CODE_2: %08x\n",
834 ipu_csi_read(csi
, CSI_CCIR_CODE_2
));
835 dev_dbg(csi
->ipu
->dev
, "CSI_CCIR_CODE_3: %08x\n",
836 ipu_csi_read(csi
, CSI_CCIR_CODE_3
));
837 dev_dbg(csi
->ipu
->dev
, "CSI_MIPI_DI: %08x\n",
838 ipu_csi_read(csi
, CSI_MIPI_DI
));
839 dev_dbg(csi
->ipu
->dev
, "CSI_SKIP: %08x\n",
840 ipu_csi_read(csi
, CSI_SKIP
));
842 EXPORT_SYMBOL_GPL(ipu_csi_dump
);