]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
2b511edb SN |
2 | /* |
3 | * Register interface file for EXYNOS FIMC-LITE (camera interface) driver | |
4 | * | |
5 | * Copyright (C) 2012 Samsung Electronics Co., Ltd. | |
086eca29 | 6 | * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> |
2b511edb SN |
7 | */ |
8 | ||
086eca29 | 9 | #include <linux/bitops.h> |
2b511edb | 10 | #include <linux/delay.h> |
086eca29 | 11 | #include <linux/io.h> |
d647f0b7 | 12 | #include <media/drv-intf/exynos-fimc.h> |
2b511edb SN |
13 | |
14 | #include "fimc-lite-reg.h" | |
15 | #include "fimc-lite.h" | |
16 | #include "fimc-core.h" | |
17 | ||
18 | #define FLITE_RESET_TIMEOUT 50 /* in ms */ | |
19 | ||
20 | void flite_hw_reset(struct fimc_lite *dev) | |
21 | { | |
22 | unsigned long end = jiffies + msecs_to_jiffies(FLITE_RESET_TIMEOUT); | |
23 | u32 cfg; | |
24 | ||
25 | cfg = readl(dev->regs + FLITE_REG_CIGCTRL); | |
26 | cfg |= FLITE_REG_CIGCTRL_SWRST_REQ; | |
27 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | |
28 | ||
29 | while (time_is_after_jiffies(end)) { | |
30 | cfg = readl(dev->regs + FLITE_REG_CIGCTRL); | |
31 | if (cfg & FLITE_REG_CIGCTRL_SWRST_RDY) | |
32 | break; | |
33 | usleep_range(1000, 5000); | |
34 | } | |
35 | ||
36 | cfg |= FLITE_REG_CIGCTRL_SWRST; | |
37 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | |
38 | } | |
39 | ||
40 | void flite_hw_clear_pending_irq(struct fimc_lite *dev) | |
41 | { | |
42 | u32 cfg = readl(dev->regs + FLITE_REG_CISTATUS); | |
43 | cfg &= ~FLITE_REG_CISTATUS_IRQ_CAM; | |
44 | writel(cfg, dev->regs + FLITE_REG_CISTATUS); | |
45 | } | |
46 | ||
47 | u32 flite_hw_get_interrupt_source(struct fimc_lite *dev) | |
48 | { | |
49 | u32 intsrc = readl(dev->regs + FLITE_REG_CISTATUS); | |
50 | return intsrc & FLITE_REG_CISTATUS_IRQ_MASK; | |
51 | } | |
52 | ||
53 | void flite_hw_clear_last_capture_end(struct fimc_lite *dev) | |
54 | { | |
55 | ||
56 | u32 cfg = readl(dev->regs + FLITE_REG_CISTATUS2); | |
57 | cfg &= ~FLITE_REG_CISTATUS2_LASTCAPEND; | |
58 | writel(cfg, dev->regs + FLITE_REG_CISTATUS2); | |
59 | } | |
60 | ||
61 | void flite_hw_set_interrupt_mask(struct fimc_lite *dev) | |
62 | { | |
63 | u32 cfg, intsrc; | |
64 | ||
65 | /* Select interrupts to be enabled for each output mode */ | |
03878bb4 | 66 | if (atomic_read(&dev->out_path) == FIMC_IO_DMA) { |
2b511edb SN |
67 | intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN | |
68 | FLITE_REG_CIGCTRL_IRQ_LASTEN | | |
086eca29 SN |
69 | FLITE_REG_CIGCTRL_IRQ_STARTEN | |
70 | FLITE_REG_CIGCTRL_IRQ_ENDEN; | |
2b511edb SN |
71 | } else { |
72 | /* An output to the FIMC-IS */ | |
73 | intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN | | |
74 | FLITE_REG_CIGCTRL_IRQ_LASTEN; | |
75 | } | |
76 | ||
77 | cfg = readl(dev->regs + FLITE_REG_CIGCTRL); | |
78 | cfg |= FLITE_REG_CIGCTRL_IRQ_DISABLE_MASK; | |
79 | cfg &= ~intsrc; | |
80 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | |
81 | } | |
82 | ||
83 | void flite_hw_capture_start(struct fimc_lite *dev) | |
84 | { | |
85 | u32 cfg = readl(dev->regs + FLITE_REG_CIIMGCPT); | |
86 | cfg |= FLITE_REG_CIIMGCPT_IMGCPTEN; | |
87 | writel(cfg, dev->regs + FLITE_REG_CIIMGCPT); | |
88 | } | |
89 | ||
90 | void flite_hw_capture_stop(struct fimc_lite *dev) | |
91 | { | |
92 | u32 cfg = readl(dev->regs + FLITE_REG_CIIMGCPT); | |
93 | cfg &= ~FLITE_REG_CIIMGCPT_IMGCPTEN; | |
94 | writel(cfg, dev->regs + FLITE_REG_CIIMGCPT); | |
95 | } | |
96 | ||
97 | /* | |
98 | * Test pattern (color bars) enable/disable. External sensor | |
99 | * pixel clock must be active for the test pattern to work. | |
100 | */ | |
101 | void flite_hw_set_test_pattern(struct fimc_lite *dev, bool on) | |
102 | { | |
103 | u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL); | |
104 | if (on) | |
105 | cfg |= FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR; | |
106 | else | |
107 | cfg &= ~FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR; | |
108 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | |
109 | } | |
110 | ||
111 | static const u32 src_pixfmt_map[8][3] = { | |
27ffaeb0 | 112 | { MEDIA_BUS_FMT_YUYV8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCBYCR, |
2b511edb | 113 | FLITE_REG_CIGCTRL_YUV422_1P }, |
27ffaeb0 | 114 | { MEDIA_BUS_FMT_YVYU8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCRYCB, |
2b511edb | 115 | FLITE_REG_CIGCTRL_YUV422_1P }, |
27ffaeb0 | 116 | { MEDIA_BUS_FMT_UYVY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CBYCRY, |
2b511edb | 117 | FLITE_REG_CIGCTRL_YUV422_1P }, |
27ffaeb0 | 118 | { MEDIA_BUS_FMT_VYUY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CRYCBY, |
2b511edb | 119 | FLITE_REG_CIGCTRL_YUV422_1P }, |
27ffaeb0 BB |
120 | { MEDIA_BUS_FMT_SGRBG8_1X8, 0, FLITE_REG_CIGCTRL_RAW8 }, |
121 | { MEDIA_BUS_FMT_SGRBG10_1X10, 0, FLITE_REG_CIGCTRL_RAW10 }, | |
122 | { MEDIA_BUS_FMT_SGRBG12_1X12, 0, FLITE_REG_CIGCTRL_RAW12 }, | |
123 | { MEDIA_BUS_FMT_JPEG_1X8, 0, FLITE_REG_CIGCTRL_USER(1) }, | |
2b511edb SN |
124 | }; |
125 | ||
126 | /* Set camera input pixel format and resolution */ | |
127 | void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f) | |
128 | { | |
27ffaeb0 | 129 | u32 pixelcode = f->fmt->mbus_code; |
6a536096 | 130 | int i = ARRAY_SIZE(src_pixfmt_map); |
2b511edb SN |
131 | u32 cfg; |
132 | ||
b1fbe051 | 133 | while (--i) { |
2b511edb SN |
134 | if (src_pixfmt_map[i][0] == pixelcode) |
135 | break; | |
136 | } | |
137 | ||
138 | if (i == 0 && src_pixfmt_map[i][0] != pixelcode) { | |
bc7584b0 | 139 | v4l2_err(&dev->ve.vdev, |
2b511edb SN |
140 | "Unsupported pixel code, falling back to %#08x\n", |
141 | src_pixfmt_map[i][0]); | |
142 | } | |
143 | ||
144 | cfg = readl(dev->regs + FLITE_REG_CIGCTRL); | |
145 | cfg &= ~FLITE_REG_CIGCTRL_FMT_MASK; | |
146 | cfg |= src_pixfmt_map[i][2]; | |
147 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | |
148 | ||
149 | cfg = readl(dev->regs + FLITE_REG_CISRCSIZE); | |
150 | cfg &= ~(FLITE_REG_CISRCSIZE_ORDER422_MASK | | |
151 | FLITE_REG_CISRCSIZE_SIZE_CAM_MASK); | |
152 | cfg |= (f->f_width << 16) | f->f_height; | |
153 | cfg |= src_pixfmt_map[i][1]; | |
154 | writel(cfg, dev->regs + FLITE_REG_CISRCSIZE); | |
155 | } | |
156 | ||
157 | /* Set the camera host input window offsets (cropping) */ | |
158 | void flite_hw_set_window_offset(struct fimc_lite *dev, struct flite_frame *f) | |
159 | { | |
160 | u32 hoff2, voff2; | |
161 | u32 cfg; | |
162 | ||
163 | cfg = readl(dev->regs + FLITE_REG_CIWDOFST); | |
164 | cfg &= ~FLITE_REG_CIWDOFST_OFST_MASK; | |
165 | cfg |= (f->rect.left << 16) | f->rect.top; | |
166 | cfg |= FLITE_REG_CIWDOFST_WINOFSEN; | |
167 | writel(cfg, dev->regs + FLITE_REG_CIWDOFST); | |
168 | ||
169 | hoff2 = f->f_width - f->rect.width - f->rect.left; | |
170 | voff2 = f->f_height - f->rect.height - f->rect.top; | |
171 | ||
172 | cfg = (hoff2 << 16) | voff2; | |
173 | writel(cfg, dev->regs + FLITE_REG_CIWDOFST2); | |
174 | } | |
175 | ||
176 | /* Select camera port (A, B) */ | |
177 | static void flite_hw_set_camera_port(struct fimc_lite *dev, int id) | |
178 | { | |
179 | u32 cfg = readl(dev->regs + FLITE_REG_CIGENERAL); | |
180 | if (id == 0) | |
181 | cfg &= ~FLITE_REG_CIGENERAL_CAM_B; | |
182 | else | |
183 | cfg |= FLITE_REG_CIGENERAL_CAM_B; | |
184 | writel(cfg, dev->regs + FLITE_REG_CIGENERAL); | |
185 | } | |
186 | ||
187 | /* Select serial or parallel bus, camera port (A,B) and set signals polarity */ | |
188 | void flite_hw_set_camera_bus(struct fimc_lite *dev, | |
56bc911a | 189 | struct fimc_source_info *si) |
2b511edb SN |
190 | { |
191 | u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL); | |
56bc911a | 192 | unsigned int flags = si->flags; |
2b511edb | 193 | |
56bc911a | 194 | if (si->sensor_bus_type != FIMC_BUS_TYPE_MIPI_CSI2) { |
2b511edb SN |
195 | cfg &= ~(FLITE_REG_CIGCTRL_SELCAM_MIPI | |
196 | FLITE_REG_CIGCTRL_INVPOLPCLK | | |
197 | FLITE_REG_CIGCTRL_INVPOLVSYNC | | |
198 | FLITE_REG_CIGCTRL_INVPOLHREF); | |
199 | ||
200 | if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) | |
201 | cfg |= FLITE_REG_CIGCTRL_INVPOLPCLK; | |
202 | ||
203 | if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) | |
204 | cfg |= FLITE_REG_CIGCTRL_INVPOLVSYNC; | |
205 | ||
206 | if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) | |
207 | cfg |= FLITE_REG_CIGCTRL_INVPOLHREF; | |
208 | } else { | |
209 | cfg |= FLITE_REG_CIGCTRL_SELCAM_MIPI; | |
210 | } | |
211 | ||
212 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | |
213 | ||
56bc911a | 214 | flite_hw_set_camera_port(dev, si->mux_id); |
2b511edb SN |
215 | } |
216 | ||
086eca29 SN |
217 | static void flite_hw_set_pack12(struct fimc_lite *dev, int on) |
218 | { | |
219 | u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT); | |
220 | ||
221 | cfg &= ~FLITE_REG_CIODMAFMT_PACK12; | |
222 | ||
223 | if (on) | |
224 | cfg |= FLITE_REG_CIODMAFMT_PACK12; | |
225 | ||
226 | writel(cfg, dev->regs + FLITE_REG_CIODMAFMT); | |
227 | } | |
228 | ||
7e566be2 | 229 | static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f) |
2b511edb SN |
230 | { |
231 | static const u32 pixcode[4][2] = { | |
27ffaeb0 BB |
232 | { MEDIA_BUS_FMT_YUYV8_2X8, FLITE_REG_CIODMAFMT_YCBYCR }, |
233 | { MEDIA_BUS_FMT_YVYU8_2X8, FLITE_REG_CIODMAFMT_YCRYCB }, | |
234 | { MEDIA_BUS_FMT_UYVY8_2X8, FLITE_REG_CIODMAFMT_CBYCRY }, | |
235 | { MEDIA_BUS_FMT_VYUY8_2X8, FLITE_REG_CIODMAFMT_CRYCBY }, | |
2b511edb SN |
236 | }; |
237 | u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT); | |
6a536096 | 238 | int i = ARRAY_SIZE(pixcode); |
2b511edb | 239 | |
b1fbe051 | 240 | while (--i) |
e90ad659 | 241 | if (pixcode[i][0] == f->fmt->mbus_code) |
2b511edb SN |
242 | break; |
243 | cfg &= ~FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK; | |
244 | writel(cfg | pixcode[i][1], dev->regs + FLITE_REG_CIODMAFMT); | |
245 | } | |
246 | ||
247 | void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f) | |
248 | { | |
249 | u32 cfg; | |
250 | ||
251 | /* Maximum output pixel size */ | |
252 | cfg = readl(dev->regs + FLITE_REG_CIOCAN); | |
253 | cfg &= ~FLITE_REG_CIOCAN_MASK; | |
17198346 | 254 | cfg |= (f->f_height << 16) | f->f_width; |
2b511edb SN |
255 | writel(cfg, dev->regs + FLITE_REG_CIOCAN); |
256 | ||
257 | /* DMA offsets */ | |
258 | cfg = readl(dev->regs + FLITE_REG_CIOOFF); | |
259 | cfg &= ~FLITE_REG_CIOOFF_MASK; | |
260 | cfg |= (f->rect.top << 16) | f->rect.left; | |
261 | writel(cfg, dev->regs + FLITE_REG_CIOOFF); | |
262 | } | |
263 | ||
086eca29 SN |
264 | void flite_hw_set_dma_buffer(struct fimc_lite *dev, struct flite_buffer *buf) |
265 | { | |
266 | unsigned int index; | |
267 | u32 cfg; | |
268 | ||
269 | if (dev->dd->max_dma_bufs == 1) | |
270 | index = 0; | |
271 | else | |
272 | index = buf->index; | |
273 | ||
274 | if (index == 0) | |
275 | writel(buf->paddr, dev->regs + FLITE_REG_CIOSA); | |
276 | else | |
277 | writel(buf->paddr, dev->regs + FLITE_REG_CIOSAN(index - 1)); | |
278 | ||
279 | cfg = readl(dev->regs + FLITE_REG_CIFCNTSEQ); | |
280 | cfg |= BIT(index); | |
281 | writel(cfg, dev->regs + FLITE_REG_CIFCNTSEQ); | |
282 | } | |
283 | ||
284 | void flite_hw_mask_dma_buffer(struct fimc_lite *dev, u32 index) | |
285 | { | |
286 | u32 cfg; | |
287 | ||
288 | if (dev->dd->max_dma_bufs == 1) | |
289 | index = 0; | |
290 | ||
291 | cfg = readl(dev->regs + FLITE_REG_CIFCNTSEQ); | |
292 | cfg &= ~BIT(index); | |
293 | writel(cfg, dev->regs + FLITE_REG_CIFCNTSEQ); | |
294 | } | |
295 | ||
2b511edb SN |
296 | /* Enable/disable output DMA, set output pixel size and offsets (composition) */ |
297 | void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f, | |
298 | bool enable) | |
299 | { | |
300 | u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL); | |
301 | ||
302 | if (!enable) { | |
303 | cfg |= FLITE_REG_CIGCTRL_ODMA_DISABLE; | |
304 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | |
305 | return; | |
306 | } | |
307 | ||
308 | cfg &= ~FLITE_REG_CIGCTRL_ODMA_DISABLE; | |
309 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | |
310 | ||
311 | flite_hw_set_out_order(dev, f); | |
312 | flite_hw_set_dma_window(dev, f); | |
086eca29 | 313 | flite_hw_set_pack12(dev, 0); |
2b511edb SN |
314 | } |
315 | ||
316 | void flite_hw_dump_regs(struct fimc_lite *dev, const char *label) | |
317 | { | |
318 | struct { | |
319 | u32 offset; | |
320 | const char * const name; | |
321 | } registers[] = { | |
322 | { 0x00, "CISRCSIZE" }, | |
323 | { 0x04, "CIGCTRL" }, | |
324 | { 0x08, "CIIMGCPT" }, | |
325 | { 0x0c, "CICPTSEQ" }, | |
326 | { 0x10, "CIWDOFST" }, | |
327 | { 0x14, "CIWDOFST2" }, | |
328 | { 0x18, "CIODMAFMT" }, | |
329 | { 0x20, "CIOCAN" }, | |
330 | { 0x24, "CIOOFF" }, | |
331 | { 0x30, "CIOSA" }, | |
332 | { 0x40, "CISTATUS" }, | |
333 | { 0x44, "CISTATUS2" }, | |
334 | { 0xf0, "CITHOLD" }, | |
335 | { 0xfc, "CIGENERAL" }, | |
336 | }; | |
337 | u32 i; | |
338 | ||
a62082ff SN |
339 | v4l2_info(&dev->subdev, "--- %s ---\n", label); |
340 | ||
2b511edb SN |
341 | for (i = 0; i < ARRAY_SIZE(registers); i++) { |
342 | u32 cfg = readl(dev->regs + registers[i].offset); | |
a62082ff SN |
343 | v4l2_info(&dev->subdev, "%9s: 0x%08x\n", |
344 | registers[i].name, cfg); | |
2b511edb SN |
345 | } |
346 | } |