Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2ffd48f2 SL |
2 | /* |
3 | * Copyright (C) 2012-2014 Mentor Graphics Inc. | |
4 | * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. | |
2ffd48f2 SL |
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 | ||
22 | struct 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 | */ | |
135 | struct 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 | */ | |
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, | |
161 | }; | |
162 | ||
163 | /* | |
164 | * Enumeration of CSI clock modes. | |
165 | */ | |
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, | |
175 | }; | |
176 | ||
177 | static inline u32 ipu_csi_read(struct ipu_csi *csi, unsigned offset) | |
178 | { | |
179 | return readl(csi->base + offset); | |
180 | } | |
181 | ||
182 | static 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 | */ | |
192 | static int ipu_csi_set_testgen_mclk(struct ipu_csi *csi, u32 pixel_clk, | |
193 | u32 ipu_clk) | |
194 | { | |
195 | u32 temp; | |
9b6d0d33 | 196 | int div_ratio; |
2ffd48f2 SL |
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 | */ | |
1e6a1495 JL |
218 | static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, |
219 | enum v4l2_mbus_type mbus_type) | |
2ffd48f2 SL |
220 | { |
221 | switch (mbus_code) { | |
3e47608f BB |
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: | |
2d95e7ed | 226 | if (mbus_type == V4L2_MBUS_CSI2_DPHY) |
1e6a1495 JL |
227 | cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565; |
228 | else | |
229 | cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; | |
2ffd48f2 SL |
230 | cfg->mipi_dt = MIPI_DT_RGB565; |
231 | cfg->data_width = IPU_CSI_DATA_WIDTH_8; | |
232 | break; | |
3e47608f BB |
233 | case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: |
234 | case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: | |
2ffd48f2 SL |
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; | |
3e47608f BB |
239 | case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: |
240 | case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: | |
2ffd48f2 SL |
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; | |
7cc3bf3f MG |
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; | |
3e47608f | 251 | case MEDIA_BUS_FMT_UYVY8_2X8: |
2ffd48f2 SL |
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; | |
3e47608f | 256 | case MEDIA_BUS_FMT_YUYV8_2X8: |
2ffd48f2 SL |
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; | |
3e47608f | 261 | case MEDIA_BUS_FMT_UYVY8_1X16: |
3e47608f | 262 | case MEDIA_BUS_FMT_YUYV8_1X16: |
ea6bead7 | 263 | cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; |
2ffd48f2 SL |
264 | cfg->mipi_dt = MIPI_DT_YUV422; |
265 | cfg->data_width = IPU_CSI_DATA_WIDTH_16; | |
266 | break; | |
3e47608f BB |
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: | |
3e8b2246 | 271 | case MEDIA_BUS_FMT_Y8_1X8: |
2ffd48f2 SL |
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; | |
3e47608f BB |
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: | |
2ffd48f2 SL |
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; | |
3e47608f BB |
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: | |
50b0f0ae | 292 | case MEDIA_BUS_FMT_Y10_1X10: |
2ffd48f2 SL |
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; | |
3e47608f BB |
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: | |
50b0f0ae | 301 | case MEDIA_BUS_FMT_Y12_1X12: |
2ffd48f2 SL |
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; | |
3e47608f | 306 | case MEDIA_BUS_FMT_JPEG_1X8: |
2ffd48f2 SL |
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 | ||
fc8c7238 SL |
319 | /* translate alternate field mode based on given standard */ |
320 | static inline enum v4l2_field | |
321 | ipu_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 | ||
2ffd48f2 SL |
328 | /* |
329 | * Fill a CSI bus config struct from mbus_config and mbus_framefmt. | |
330 | */ | |
d36d0e63 | 331 | static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, |
fc8c7238 SL |
332 | const struct v4l2_mbus_config *mbus_cfg, |
333 | const struct v4l2_mbus_framefmt *mbus_fmt) | |
2ffd48f2 | 334 | { |
d36d0e63 ES |
335 | int ret; |
336 | ||
2ffd48f2 SL |
337 | memset(csicfg, 0, sizeof(*csicfg)); |
338 | ||
1e6a1495 | 339 | ret = mbus_code_to_bus_cfg(csicfg, mbus_fmt->code, mbus_cfg->type); |
d36d0e63 ES |
340 | if (ret < 0) |
341 | return ret; | |
2ffd48f2 SL |
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; | |
bbe4a089 SL |
356 | if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field) || |
357 | mbus_fmt->field == V4L2_FIELD_ALTERNATE) | |
2ffd48f2 SL |
358 | csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; |
359 | else | |
360 | csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE; | |
361 | break; | |
2d95e7ed | 362 | case V4L2_MBUS_CSI2_DPHY: |
2ffd48f2 SL |
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 | } | |
d36d0e63 ES |
373 | |
374 | return 0; | |
2ffd48f2 SL |
375 | } |
376 | ||
fc8c7238 SL |
377 | static int |
378 | ipu_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 | ||
2ffd48f2 | 427 | int ipu_csi_init_interface(struct ipu_csi *csi, |
fc8c7238 SL |
428 | const struct v4l2_mbus_config *mbus_cfg, |
429 | const struct v4l2_mbus_framefmt *infmt, | |
430 | const struct v4l2_mbus_framefmt *outfmt) | |
2ffd48f2 SL |
431 | { |
432 | struct ipu_csi_bus_config cfg; | |
433 | unsigned long flags; | |
aede45b2 | 434 | u32 width, height, data = 0; |
fc8c7238 | 435 | v4l2_std_id std; |
d36d0e63 | 436 | int ret; |
2ffd48f2 | 437 | |
fc8c7238 | 438 | ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt); |
d36d0e63 ES |
439 | if (ret < 0) |
440 | return ret; | |
2ffd48f2 | 441 | |
aede45b2 | 442 | /* set default sensor frame width and height */ |
fc8c7238 SL |
443 | width = infmt->width; |
444 | height = infmt->height; | |
445 | if (infmt->field == V4L2_FIELD_ALTERNATE) | |
446 | height *= 2; | |
aede45b2 | 447 | |
2ffd48f2 SL |
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 | ||
2ffd48f2 SL |
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: | |
fc8c7238 SL |
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; | |
2ffd48f2 SL |
479 | } else { |
480 | dev_err(csi->ipu->dev, | |
fc8c7238 SL |
481 | "Unsupported interlaced video mode\n"); |
482 | ret = -EINVAL; | |
483 | goto out_unlock; | |
2ffd48f2 | 484 | } |
fc8c7238 SL |
485 | |
486 | ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std); | |
487 | if (ret) | |
488 | goto out_unlock; | |
2ffd48f2 SL |
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 | ||
aede45b2 SL |
504 | /* Setup sensor frame size */ |
505 | ipu_csi_write(csi, (width - 1) | ((height - 1) << 16), | |
506 | CSI_SENS_FRM_SIZE); | |
507 | ||
2ffd48f2 SL |
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 | ||
fc8c7238 | 513 | out_unlock: |
2ffd48f2 SL |
514 | spin_unlock_irqrestore(&csi->lock, flags); |
515 | ||
fc8c7238 | 516 | return ret; |
2ffd48f2 SL |
517 | } |
518 | EXPORT_SYMBOL_GPL(ipu_csi_init_interface); | |
519 | ||
520 | bool 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 | } | |
549 | EXPORT_SYMBOL_GPL(ipu_csi_is_interlaced); | |
550 | ||
551 | void 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 | } | |
568 | EXPORT_SYMBOL_GPL(ipu_csi_get_window); | |
569 | ||
570 | void 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 | } | |
587 | EXPORT_SYMBOL_GPL(ipu_csi_set_window); | |
588 | ||
867341b9 PZ |
589 | void 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 | } | |
5f1eb694 | 604 | EXPORT_SYMBOL_GPL(ipu_csi_set_downsize); |
867341b9 | 605 | |
2ffd48f2 SL |
606 | void 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 | ||
6dd0d0e1 | 618 | if (!active) { |
2ffd48f2 SL |
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 | } | |
636 | EXPORT_SYMBOL_GPL(ipu_csi_set_test_generator); | |
637 | ||
638 | int 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; | |
d36d0e63 | 644 | int ret; |
2ffd48f2 SL |
645 | |
646 | if (vc > 3) | |
647 | return -EINVAL; | |
648 | ||
2d95e7ed | 649 | ret = mbus_code_to_bus_cfg(&cfg, mbus_fmt->code, V4L2_MBUS_CSI2_DPHY); |
d36d0e63 ES |
650 | if (ret < 0) |
651 | return ret; | |
2ffd48f2 SL |
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 | } | |
664 | EXPORT_SYMBOL_GPL(ipu_csi_set_mipi_datatype); | |
665 | ||
666 | int 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 | } | |
689 | EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc); | |
690 | ||
691 | int 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 | } | |
712 | EXPORT_SYMBOL_GPL(ipu_csi_set_dest); | |
713 | ||
714 | int ipu_csi_enable(struct ipu_csi *csi) | |
715 | { | |
716 | ipu_module_enable(csi->ipu, csi->module); | |
717 | ||
718 | return 0; | |
719 | } | |
720 | EXPORT_SYMBOL_GPL(ipu_csi_enable); | |
721 | ||
722 | int ipu_csi_disable(struct ipu_csi *csi) | |
723 | { | |
724 | ipu_module_disable(csi->ipu, csi->module); | |
725 | ||
726 | return 0; | |
727 | } | |
728 | EXPORT_SYMBOL_GPL(ipu_csi_disable); | |
729 | ||
730 | struct 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; | |
749 | unlock: | |
750 | spin_unlock_irqrestore(&csi->lock, flags); | |
751 | return ret; | |
752 | } | |
753 | EXPORT_SYMBOL_GPL(ipu_csi_get); | |
754 | ||
755 | void 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 | } | |
763 | EXPORT_SYMBOL_GPL(ipu_csi_put); | |
764 | ||
765 | int 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 | ||
794 | void ipu_csi_exit(struct ipu_soc *ipu, int id) | |
795 | { | |
796 | } | |
797 | ||
798 | void 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 | } | |
821 | EXPORT_SYMBOL_GPL(ipu_csi_dump); |