diff options
Diffstat (limited to 'drivers/media/platform/exynos4-is')
31 files changed, 16839 insertions, 0 deletions
diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig new file mode 100644 index 000000000000..6ff99b5849f9 --- /dev/null +++ b/drivers/media/platform/exynos4-is/Kconfig | |||
@@ -0,0 +1,61 @@ | |||
1 | |||
2 | config VIDEO_SAMSUNG_EXYNOS4_IS | ||
3 | bool "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver" | ||
4 | depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && PLAT_S5P && PM_RUNTIME | ||
5 | help | ||
6 | Say Y here to enable camera host interface devices for | ||
7 | Samsung S5P and EXYNOS SoC series. | ||
8 | |||
9 | if VIDEO_SAMSUNG_EXYNOS4_IS | ||
10 | |||
11 | config VIDEO_S5P_FIMC | ||
12 | tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver" | ||
13 | depends on I2C | ||
14 | select VIDEOBUF2_DMA_CONTIG | ||
15 | select V4L2_MEM2MEM_DEV | ||
16 | select MFD_SYSCON if OF | ||
17 | help | ||
18 | This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC camera host | ||
19 | interface and video postprocessor (FIMC) devices. | ||
20 | |||
21 | To compile this driver as a module, choose M here: the | ||
22 | module will be called s5p-fimc. | ||
23 | |||
24 | config VIDEO_S5P_MIPI_CSIS | ||
25 | tristate "S5P/EXYNOS MIPI-CSI2 receiver (MIPI-CSIS) driver" | ||
26 | depends on REGULATOR | ||
27 | select S5P_SETUP_MIPIPHY | ||
28 | help | ||
29 | This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC MIPI-CSI2 | ||
30 | receiver (MIPI-CSIS) devices. | ||
31 | |||
32 | To compile this driver as a module, choose M here: the | ||
33 | module will be called s5p-csis. | ||
34 | |||
35 | if SOC_EXYNOS4212 || SOC_EXYNOS4412 || SOC_EXYNOS5250 | ||
36 | |||
37 | config VIDEO_EXYNOS_FIMC_LITE | ||
38 | tristate "EXYNOS FIMC-LITE camera interface driver" | ||
39 | depends on I2C | ||
40 | select VIDEOBUF2_DMA_CONTIG | ||
41 | help | ||
42 | This is a V4L2 driver for Samsung EXYNOS4/5 SoC FIMC-LITE camera | ||
43 | host interface. | ||
44 | |||
45 | To compile this driver as a module, choose M here: the | ||
46 | module will be called exynos-fimc-lite. | ||
47 | endif | ||
48 | |||
49 | config VIDEO_EXYNOS4_FIMC_IS | ||
50 | tristate "EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver" | ||
51 | select VIDEOBUF2_DMA_CONTIG | ||
52 | depends on OF | ||
53 | select FW_LOADER | ||
54 | help | ||
55 | This is a V4L2 driver for Samsung EXYNOS4x12 SoC series | ||
56 | FIMC-IS (Imaging Subsystem). | ||
57 | |||
58 | To compile this driver as a module, choose M here: the | ||
59 | module will be called exynos4-fimc-is. | ||
60 | |||
61 | endif # VIDEO_SAMSUNG_S5P_FIMC | ||
diff --git a/drivers/media/platform/exynos4-is/Makefile b/drivers/media/platform/exynos4-is/Makefile new file mode 100644 index 000000000000..f25f46377399 --- /dev/null +++ b/drivers/media/platform/exynos4-is/Makefile | |||
@@ -0,0 +1,10 @@ | |||
1 | s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o media-dev.o | ||
2 | exynos-fimc-lite-objs += fimc-lite-reg.o fimc-lite.o | ||
3 | exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o | ||
4 | exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o | ||
5 | s5p-csis-objs := mipi-csis.o | ||
6 | |||
7 | obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o | ||
8 | obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o | ||
9 | obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS) += exynos-fimc-is.o | ||
10 | obj-$(CONFIG_VIDEO_S5P_FIMC) += s5p-fimc.o | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c new file mode 100644 index 000000000000..528f41369364 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-capture.c | |||
@@ -0,0 +1,1893 @@ | |||
1 | /* | ||
2 | * Samsung S5P/EXYNOS4 SoC series camera interface (camera capture) driver | ||
3 | * | ||
4 | * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. | ||
5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/types.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/bug.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/pm_runtime.h> | ||
20 | #include <linux/list.h> | ||
21 | #include <linux/slab.h> | ||
22 | |||
23 | #include <linux/videodev2.h> | ||
24 | #include <media/v4l2-device.h> | ||
25 | #include <media/v4l2-ioctl.h> | ||
26 | #include <media/v4l2-mem2mem.h> | ||
27 | #include <media/videobuf2-core.h> | ||
28 | #include <media/videobuf2-dma-contig.h> | ||
29 | |||
30 | #include "media-dev.h" | ||
31 | #include "fimc-core.h" | ||
32 | #include "fimc-reg.h" | ||
33 | |||
34 | static int fimc_capture_hw_init(struct fimc_dev *fimc) | ||
35 | { | ||
36 | struct fimc_source_info *si = &fimc->vid_cap.source_config; | ||
37 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | ||
38 | int ret; | ||
39 | unsigned long flags; | ||
40 | |||
41 | if (ctx == NULL || ctx->s_frame.fmt == NULL) | ||
42 | return -EINVAL; | ||
43 | |||
44 | if (si->fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) { | ||
45 | ret = fimc_hw_camblk_cfg_writeback(fimc); | ||
46 | if (ret < 0) | ||
47 | return ret; | ||
48 | } | ||
49 | |||
50 | spin_lock_irqsave(&fimc->slock, flags); | ||
51 | fimc_prepare_dma_offset(ctx, &ctx->d_frame); | ||
52 | fimc_set_yuv_order(ctx); | ||
53 | |||
54 | fimc_hw_set_camera_polarity(fimc, si); | ||
55 | fimc_hw_set_camera_type(fimc, si); | ||
56 | fimc_hw_set_camera_source(fimc, si); | ||
57 | fimc_hw_set_camera_offset(fimc, &ctx->s_frame); | ||
58 | |||
59 | ret = fimc_set_scaler_info(ctx); | ||
60 | if (!ret) { | ||
61 | fimc_hw_set_input_path(ctx); | ||
62 | fimc_hw_set_prescaler(ctx); | ||
63 | fimc_hw_set_mainscaler(ctx); | ||
64 | fimc_hw_set_target_format(ctx); | ||
65 | fimc_hw_set_rotation(ctx); | ||
66 | fimc_hw_set_effect(ctx); | ||
67 | fimc_hw_set_output_path(ctx); | ||
68 | fimc_hw_set_out_dma(ctx); | ||
69 | if (fimc->drv_data->alpha_color) | ||
70 | fimc_hw_set_rgb_alpha(ctx); | ||
71 | clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); | ||
72 | } | ||
73 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
74 | return ret; | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * Reinitialize the driver so it is ready to start the streaming again. | ||
79 | * Set fimc->state to indicate stream off and the hardware shut down state. | ||
80 | * If not suspending (@suspend is false), return any buffers to videobuf2. | ||
81 | * Otherwise put any owned buffers onto the pending buffers queue, so they | ||
82 | * can be re-spun when the device is being resumed. Also perform FIMC | ||
83 | * software reset and disable streaming on the whole pipeline if required. | ||
84 | */ | ||
85 | static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) | ||
86 | { | ||
87 | struct fimc_vid_cap *cap = &fimc->vid_cap; | ||
88 | struct fimc_vid_buffer *buf; | ||
89 | unsigned long flags; | ||
90 | bool streaming; | ||
91 | |||
92 | spin_lock_irqsave(&fimc->slock, flags); | ||
93 | streaming = fimc->state & (1 << ST_CAPT_ISP_STREAM); | ||
94 | |||
95 | fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_SHUT | | ||
96 | 1 << ST_CAPT_STREAM | 1 << ST_CAPT_ISP_STREAM); | ||
97 | if (suspend) | ||
98 | fimc->state |= (1 << ST_CAPT_SUSPENDED); | ||
99 | else | ||
100 | fimc->state &= ~(1 << ST_CAPT_PEND | 1 << ST_CAPT_SUSPENDED); | ||
101 | |||
102 | /* Release unused buffers */ | ||
103 | while (!suspend && !list_empty(&cap->pending_buf_q)) { | ||
104 | buf = fimc_pending_queue_pop(cap); | ||
105 | vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); | ||
106 | } | ||
107 | /* If suspending put unused buffers onto pending queue */ | ||
108 | while (!list_empty(&cap->active_buf_q)) { | ||
109 | buf = fimc_active_queue_pop(cap); | ||
110 | if (suspend) | ||
111 | fimc_pending_queue_add(cap, buf); | ||
112 | else | ||
113 | vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); | ||
114 | } | ||
115 | |||
116 | fimc_hw_reset(fimc); | ||
117 | cap->buf_index = 0; | ||
118 | |||
119 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
120 | |||
121 | if (streaming) | ||
122 | return fimc_pipeline_call(fimc, set_stream, | ||
123 | &fimc->pipeline, 0); | ||
124 | else | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int fimc_stop_capture(struct fimc_dev *fimc, bool suspend) | ||
129 | { | ||
130 | unsigned long flags; | ||
131 | |||
132 | if (!fimc_capture_active(fimc)) | ||
133 | return 0; | ||
134 | |||
135 | spin_lock_irqsave(&fimc->slock, flags); | ||
136 | set_bit(ST_CAPT_SHUT, &fimc->state); | ||
137 | fimc_deactivate_capture(fimc); | ||
138 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
139 | |||
140 | wait_event_timeout(fimc->irq_queue, | ||
141 | !test_bit(ST_CAPT_SHUT, &fimc->state), | ||
142 | (2*HZ/10)); /* 200 ms */ | ||
143 | |||
144 | return fimc_capture_state_cleanup(fimc, suspend); | ||
145 | } | ||
146 | |||
147 | /** | ||
148 | * fimc_capture_config_update - apply the camera interface configuration | ||
149 | * | ||
150 | * To be called from within the interrupt handler with fimc.slock | ||
151 | * spinlock held. It updates the camera pixel crop, rotation and | ||
152 | * image flip in H/W. | ||
153 | */ | ||
154 | static int fimc_capture_config_update(struct fimc_ctx *ctx) | ||
155 | { | ||
156 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
157 | int ret; | ||
158 | |||
159 | fimc_hw_set_camera_offset(fimc, &ctx->s_frame); | ||
160 | |||
161 | ret = fimc_set_scaler_info(ctx); | ||
162 | if (ret) | ||
163 | return ret; | ||
164 | |||
165 | fimc_hw_set_prescaler(ctx); | ||
166 | fimc_hw_set_mainscaler(ctx); | ||
167 | fimc_hw_set_target_format(ctx); | ||
168 | fimc_hw_set_rotation(ctx); | ||
169 | fimc_hw_set_effect(ctx); | ||
170 | fimc_prepare_dma_offset(ctx, &ctx->d_frame); | ||
171 | fimc_hw_set_out_dma(ctx); | ||
172 | if (fimc->drv_data->alpha_color) | ||
173 | fimc_hw_set_rgb_alpha(ctx); | ||
174 | |||
175 | clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); | ||
176 | return ret; | ||
177 | } | ||
178 | |||
179 | void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf) | ||
180 | { | ||
181 | struct v4l2_subdev *csis = fimc->pipeline.subdevs[IDX_CSIS]; | ||
182 | struct fimc_vid_cap *cap = &fimc->vid_cap; | ||
183 | struct fimc_frame *f = &cap->ctx->d_frame; | ||
184 | struct fimc_vid_buffer *v_buf; | ||
185 | struct timeval *tv; | ||
186 | struct timespec ts; | ||
187 | |||
188 | if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) { | ||
189 | wake_up(&fimc->irq_queue); | ||
190 | goto done; | ||
191 | } | ||
192 | |||
193 | if (!list_empty(&cap->active_buf_q) && | ||
194 | test_bit(ST_CAPT_RUN, &fimc->state) && deq_buf) { | ||
195 | ktime_get_real_ts(&ts); | ||
196 | |||
197 | v_buf = fimc_active_queue_pop(cap); | ||
198 | |||
199 | tv = &v_buf->vb.v4l2_buf.timestamp; | ||
200 | tv->tv_sec = ts.tv_sec; | ||
201 | tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; | ||
202 | v_buf->vb.v4l2_buf.sequence = cap->frame_count++; | ||
203 | |||
204 | vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE); | ||
205 | } | ||
206 | |||
207 | if (!list_empty(&cap->pending_buf_q)) { | ||
208 | |||
209 | v_buf = fimc_pending_queue_pop(cap); | ||
210 | fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index); | ||
211 | v_buf->index = cap->buf_index; | ||
212 | |||
213 | /* Move the buffer to the capture active queue */ | ||
214 | fimc_active_queue_add(cap, v_buf); | ||
215 | |||
216 | dbg("next frame: %d, done frame: %d", | ||
217 | fimc_hw_get_frame_index(fimc), v_buf->index); | ||
218 | |||
219 | if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) | ||
220 | cap->buf_index = 0; | ||
221 | } | ||
222 | /* | ||
223 | * Set up a buffer at MIPI-CSIS if current image format | ||
224 | * requires the frame embedded data capture. | ||
225 | */ | ||
226 | if (f->fmt->mdataplanes && !list_empty(&cap->active_buf_q)) { | ||
227 | unsigned int plane = ffs(f->fmt->mdataplanes) - 1; | ||
228 | unsigned int size = f->payload[plane]; | ||
229 | s32 index = fimc_hw_get_frame_index(fimc); | ||
230 | void *vaddr; | ||
231 | |||
232 | list_for_each_entry(v_buf, &cap->active_buf_q, list) { | ||
233 | if (v_buf->index != index) | ||
234 | continue; | ||
235 | vaddr = vb2_plane_vaddr(&v_buf->vb, plane); | ||
236 | v4l2_subdev_call(csis, video, s_rx_buffer, | ||
237 | vaddr, &size); | ||
238 | break; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | if (cap->active_buf_cnt == 0) { | ||
243 | if (deq_buf) | ||
244 | clear_bit(ST_CAPT_RUN, &fimc->state); | ||
245 | |||
246 | if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) | ||
247 | cap->buf_index = 0; | ||
248 | } else { | ||
249 | set_bit(ST_CAPT_RUN, &fimc->state); | ||
250 | } | ||
251 | |||
252 | if (test_bit(ST_CAPT_APPLY_CFG, &fimc->state)) | ||
253 | fimc_capture_config_update(cap->ctx); | ||
254 | done: | ||
255 | if (cap->active_buf_cnt == 1) { | ||
256 | fimc_deactivate_capture(fimc); | ||
257 | clear_bit(ST_CAPT_STREAM, &fimc->state); | ||
258 | } | ||
259 | |||
260 | dbg("frame: %d, active_buf_cnt: %d", | ||
261 | fimc_hw_get_frame_index(fimc), cap->active_buf_cnt); | ||
262 | } | ||
263 | |||
264 | |||
265 | static int start_streaming(struct vb2_queue *q, unsigned int count) | ||
266 | { | ||
267 | struct fimc_ctx *ctx = q->drv_priv; | ||
268 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
269 | struct fimc_vid_cap *vid_cap = &fimc->vid_cap; | ||
270 | int min_bufs; | ||
271 | int ret; | ||
272 | |||
273 | vid_cap->frame_count = 0; | ||
274 | |||
275 | ret = fimc_capture_hw_init(fimc); | ||
276 | if (ret) { | ||
277 | fimc_capture_state_cleanup(fimc, false); | ||
278 | return ret; | ||
279 | } | ||
280 | |||
281 | set_bit(ST_CAPT_PEND, &fimc->state); | ||
282 | |||
283 | min_bufs = fimc->vid_cap.reqbufs_count > 1 ? 2 : 1; | ||
284 | |||
285 | if (vid_cap->active_buf_cnt >= min_bufs && | ||
286 | !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) { | ||
287 | fimc_activate_capture(ctx); | ||
288 | |||
289 | if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) | ||
290 | return fimc_pipeline_call(fimc, set_stream, | ||
291 | &fimc->pipeline, 1); | ||
292 | } | ||
293 | |||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | static int stop_streaming(struct vb2_queue *q) | ||
298 | { | ||
299 | struct fimc_ctx *ctx = q->drv_priv; | ||
300 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
301 | |||
302 | if (!fimc_capture_active(fimc)) | ||
303 | return -EINVAL; | ||
304 | |||
305 | return fimc_stop_capture(fimc, false); | ||
306 | } | ||
307 | |||
308 | int fimc_capture_suspend(struct fimc_dev *fimc) | ||
309 | { | ||
310 | bool suspend = fimc_capture_busy(fimc); | ||
311 | |||
312 | int ret = fimc_stop_capture(fimc, suspend); | ||
313 | if (ret) | ||
314 | return ret; | ||
315 | return fimc_pipeline_call(fimc, close, &fimc->pipeline); | ||
316 | } | ||
317 | |||
318 | static void buffer_queue(struct vb2_buffer *vb); | ||
319 | |||
320 | int fimc_capture_resume(struct fimc_dev *fimc) | ||
321 | { | ||
322 | struct fimc_vid_cap *vid_cap = &fimc->vid_cap; | ||
323 | struct fimc_vid_buffer *buf; | ||
324 | int i; | ||
325 | |||
326 | if (!test_and_clear_bit(ST_CAPT_SUSPENDED, &fimc->state)) | ||
327 | return 0; | ||
328 | |||
329 | INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q); | ||
330 | vid_cap->buf_index = 0; | ||
331 | fimc_pipeline_call(fimc, open, &fimc->pipeline, | ||
332 | &vid_cap->vfd.entity, false); | ||
333 | fimc_capture_hw_init(fimc); | ||
334 | |||
335 | clear_bit(ST_CAPT_SUSPENDED, &fimc->state); | ||
336 | |||
337 | for (i = 0; i < vid_cap->reqbufs_count; i++) { | ||
338 | if (list_empty(&vid_cap->pending_buf_q)) | ||
339 | break; | ||
340 | buf = fimc_pending_queue_pop(vid_cap); | ||
341 | buffer_queue(&buf->vb); | ||
342 | } | ||
343 | return 0; | ||
344 | |||
345 | } | ||
346 | |||
347 | static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, | ||
348 | unsigned int *num_buffers, unsigned int *num_planes, | ||
349 | unsigned int sizes[], void *allocators[]) | ||
350 | { | ||
351 | const struct v4l2_pix_format_mplane *pixm = NULL; | ||
352 | struct fimc_ctx *ctx = vq->drv_priv; | ||
353 | struct fimc_frame *frame = &ctx->d_frame; | ||
354 | struct fimc_fmt *fmt = frame->fmt; | ||
355 | unsigned long wh; | ||
356 | int i; | ||
357 | |||
358 | if (pfmt) { | ||
359 | pixm = &pfmt->fmt.pix_mp; | ||
360 | fmt = fimc_find_format(&pixm->pixelformat, NULL, | ||
361 | FMT_FLAGS_CAM | FMT_FLAGS_M2M, -1); | ||
362 | wh = pixm->width * pixm->height; | ||
363 | } else { | ||
364 | wh = frame->f_width * frame->f_height; | ||
365 | } | ||
366 | |||
367 | if (fmt == NULL) | ||
368 | return -EINVAL; | ||
369 | |||
370 | *num_planes = fmt->memplanes; | ||
371 | |||
372 | for (i = 0; i < fmt->memplanes; i++) { | ||
373 | unsigned int size = (wh * fmt->depth[i]) / 8; | ||
374 | if (pixm) | ||
375 | sizes[i] = max(size, pixm->plane_fmt[i].sizeimage); | ||
376 | else if (fimc_fmt_is_user_defined(fmt->color)) | ||
377 | sizes[i] = frame->payload[i]; | ||
378 | else | ||
379 | sizes[i] = max_t(u32, size, frame->payload[i]); | ||
380 | |||
381 | allocators[i] = ctx->fimc_dev->alloc_ctx; | ||
382 | } | ||
383 | |||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | static int buffer_prepare(struct vb2_buffer *vb) | ||
388 | { | ||
389 | struct vb2_queue *vq = vb->vb2_queue; | ||
390 | struct fimc_ctx *ctx = vq->drv_priv; | ||
391 | int i; | ||
392 | |||
393 | if (ctx->d_frame.fmt == NULL) | ||
394 | return -EINVAL; | ||
395 | |||
396 | for (i = 0; i < ctx->d_frame.fmt->memplanes; i++) { | ||
397 | unsigned long size = ctx->d_frame.payload[i]; | ||
398 | |||
399 | if (vb2_plane_size(vb, i) < size) { | ||
400 | v4l2_err(&ctx->fimc_dev->vid_cap.vfd, | ||
401 | "User buffer too small (%ld < %ld)\n", | ||
402 | vb2_plane_size(vb, i), size); | ||
403 | return -EINVAL; | ||
404 | } | ||
405 | vb2_set_plane_payload(vb, i, size); | ||
406 | } | ||
407 | |||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | static void buffer_queue(struct vb2_buffer *vb) | ||
412 | { | ||
413 | struct fimc_vid_buffer *buf | ||
414 | = container_of(vb, struct fimc_vid_buffer, vb); | ||
415 | struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); | ||
416 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
417 | struct fimc_vid_cap *vid_cap = &fimc->vid_cap; | ||
418 | unsigned long flags; | ||
419 | int min_bufs; | ||
420 | |||
421 | spin_lock_irqsave(&fimc->slock, flags); | ||
422 | fimc_prepare_addr(ctx, &buf->vb, &ctx->d_frame, &buf->paddr); | ||
423 | |||
424 | if (!test_bit(ST_CAPT_SUSPENDED, &fimc->state) && | ||
425 | !test_bit(ST_CAPT_STREAM, &fimc->state) && | ||
426 | vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) { | ||
427 | /* Setup the buffer directly for processing. */ | ||
428 | int buf_id = (vid_cap->reqbufs_count == 1) ? -1 : | ||
429 | vid_cap->buf_index; | ||
430 | |||
431 | fimc_hw_set_output_addr(fimc, &buf->paddr, buf_id); | ||
432 | buf->index = vid_cap->buf_index; | ||
433 | fimc_active_queue_add(vid_cap, buf); | ||
434 | |||
435 | if (++vid_cap->buf_index >= FIMC_MAX_OUT_BUFS) | ||
436 | vid_cap->buf_index = 0; | ||
437 | } else { | ||
438 | fimc_pending_queue_add(vid_cap, buf); | ||
439 | } | ||
440 | |||
441 | min_bufs = vid_cap->reqbufs_count > 1 ? 2 : 1; | ||
442 | |||
443 | |||
444 | if (vb2_is_streaming(&vid_cap->vbq) && | ||
445 | vid_cap->active_buf_cnt >= min_bufs && | ||
446 | !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) { | ||
447 | int ret; | ||
448 | |||
449 | fimc_activate_capture(ctx); | ||
450 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
451 | |||
452 | if (test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) | ||
453 | return; | ||
454 | |||
455 | ret = fimc_pipeline_call(fimc, set_stream, &fimc->pipeline, 1); | ||
456 | if (ret < 0) | ||
457 | v4l2_err(&vid_cap->vfd, "stream on failed: %d\n", ret); | ||
458 | return; | ||
459 | } | ||
460 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
461 | } | ||
462 | |||
463 | static struct vb2_ops fimc_capture_qops = { | ||
464 | .queue_setup = queue_setup, | ||
465 | .buf_prepare = buffer_prepare, | ||
466 | .buf_queue = buffer_queue, | ||
467 | .wait_prepare = vb2_ops_wait_prepare, | ||
468 | .wait_finish = vb2_ops_wait_finish, | ||
469 | .start_streaming = start_streaming, | ||
470 | .stop_streaming = stop_streaming, | ||
471 | }; | ||
472 | |||
473 | /** | ||
474 | * fimc_capture_ctrls_create - initialize the control handler | ||
475 | * Initialize the capture video node control handler and fill it | ||
476 | * with the FIMC controls. Inherit any sensor's controls if the | ||
477 | * 'user_subdev_api' flag is false (default behaviour). | ||
478 | * This function need to be called with the graph mutex held. | ||
479 | */ | ||
480 | int fimc_capture_ctrls_create(struct fimc_dev *fimc) | ||
481 | { | ||
482 | struct fimc_vid_cap *vid_cap = &fimc->vid_cap; | ||
483 | struct v4l2_subdev *sensor = fimc->pipeline.subdevs[IDX_SENSOR]; | ||
484 | int ret; | ||
485 | |||
486 | if (WARN_ON(vid_cap->ctx == NULL)) | ||
487 | return -ENXIO; | ||
488 | if (vid_cap->ctx->ctrls.ready) | ||
489 | return 0; | ||
490 | |||
491 | ret = fimc_ctrls_create(vid_cap->ctx); | ||
492 | |||
493 | if (ret || vid_cap->user_subdev_api || !sensor || | ||
494 | !vid_cap->ctx->ctrls.ready) | ||
495 | return ret; | ||
496 | |||
497 | return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrls.handler, | ||
498 | sensor->ctrl_handler, NULL); | ||
499 | } | ||
500 | |||
501 | static int fimc_capture_set_default_format(struct fimc_dev *fimc); | ||
502 | |||
503 | static int fimc_capture_open(struct file *file) | ||
504 | { | ||
505 | struct fimc_dev *fimc = video_drvdata(file); | ||
506 | int ret = -EBUSY; | ||
507 | |||
508 | dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); | ||
509 | |||
510 | fimc_md_graph_lock(fimc); | ||
511 | mutex_lock(&fimc->lock); | ||
512 | |||
513 | if (fimc_m2m_active(fimc)) | ||
514 | goto unlock; | ||
515 | |||
516 | set_bit(ST_CAPT_BUSY, &fimc->state); | ||
517 | ret = pm_runtime_get_sync(&fimc->pdev->dev); | ||
518 | if (ret < 0) | ||
519 | goto unlock; | ||
520 | |||
521 | ret = v4l2_fh_open(file); | ||
522 | if (ret) { | ||
523 | pm_runtime_put(&fimc->pdev->dev); | ||
524 | goto unlock; | ||
525 | } | ||
526 | |||
527 | if (v4l2_fh_is_singular_file(file)) { | ||
528 | ret = fimc_pipeline_call(fimc, open, &fimc->pipeline, | ||
529 | &fimc->vid_cap.vfd.entity, true); | ||
530 | |||
531 | if (!ret && !fimc->vid_cap.user_subdev_api) | ||
532 | ret = fimc_capture_set_default_format(fimc); | ||
533 | |||
534 | if (!ret) | ||
535 | ret = fimc_capture_ctrls_create(fimc); | ||
536 | |||
537 | if (ret < 0) { | ||
538 | clear_bit(ST_CAPT_BUSY, &fimc->state); | ||
539 | pm_runtime_put_sync(&fimc->pdev->dev); | ||
540 | v4l2_fh_release(file); | ||
541 | } else { | ||
542 | fimc->vid_cap.refcnt++; | ||
543 | } | ||
544 | } | ||
545 | unlock: | ||
546 | mutex_unlock(&fimc->lock); | ||
547 | fimc_md_graph_unlock(fimc); | ||
548 | return ret; | ||
549 | } | ||
550 | |||
551 | static int fimc_capture_release(struct file *file) | ||
552 | { | ||
553 | struct fimc_dev *fimc = video_drvdata(file); | ||
554 | struct fimc_vid_cap *vc = &fimc->vid_cap; | ||
555 | int ret; | ||
556 | |||
557 | dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); | ||
558 | |||
559 | mutex_lock(&fimc->lock); | ||
560 | |||
561 | if (v4l2_fh_is_singular_file(file)) { | ||
562 | if (vc->streaming) { | ||
563 | media_entity_pipeline_stop(&vc->vfd.entity); | ||
564 | vc->streaming = false; | ||
565 | } | ||
566 | clear_bit(ST_CAPT_BUSY, &fimc->state); | ||
567 | fimc_stop_capture(fimc, false); | ||
568 | fimc_pipeline_call(fimc, close, &fimc->pipeline); | ||
569 | clear_bit(ST_CAPT_SUSPENDED, &fimc->state); | ||
570 | fimc->vid_cap.refcnt--; | ||
571 | } | ||
572 | |||
573 | pm_runtime_put(&fimc->pdev->dev); | ||
574 | |||
575 | if (v4l2_fh_is_singular_file(file)) | ||
576 | fimc_ctrls_delete(fimc->vid_cap.ctx); | ||
577 | |||
578 | ret = vb2_fop_release(file); | ||
579 | mutex_unlock(&fimc->lock); | ||
580 | |||
581 | return ret; | ||
582 | } | ||
583 | |||
584 | static const struct v4l2_file_operations fimc_capture_fops = { | ||
585 | .owner = THIS_MODULE, | ||
586 | .open = fimc_capture_open, | ||
587 | .release = fimc_capture_release, | ||
588 | .poll = vb2_fop_poll, | ||
589 | .unlocked_ioctl = video_ioctl2, | ||
590 | .mmap = vb2_fop_mmap, | ||
591 | }; | ||
592 | |||
593 | /* | ||
594 | * Format and crop negotiation helpers | ||
595 | */ | ||
596 | |||
597 | static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, | ||
598 | u32 *width, u32 *height, | ||
599 | u32 *code, u32 *fourcc, int pad) | ||
600 | { | ||
601 | bool rotation = ctx->rotation == 90 || ctx->rotation == 270; | ||
602 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
603 | const struct fimc_variant *var = fimc->variant; | ||
604 | const struct fimc_pix_limit *pl = var->pix_limit; | ||
605 | struct fimc_frame *dst = &ctx->d_frame; | ||
606 | u32 depth, min_w, max_w, min_h, align_h = 3; | ||
607 | u32 mask = FMT_FLAGS_CAM; | ||
608 | struct fimc_fmt *ffmt; | ||
609 | |||
610 | /* Conversion from/to JPEG or User Defined format is not supported */ | ||
611 | if (code && ctx->s_frame.fmt && pad == FIMC_SD_PAD_SOURCE && | ||
612 | fimc_fmt_is_user_defined(ctx->s_frame.fmt->color)) | ||
613 | *code = ctx->s_frame.fmt->mbus_code; | ||
614 | |||
615 | if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad == FIMC_SD_PAD_SOURCE) | ||
616 | mask |= FMT_FLAGS_M2M; | ||
617 | |||
618 | if (pad == FIMC_SD_PAD_SINK_FIFO) | ||
619 | mask = FMT_FLAGS_WRITEBACK; | ||
620 | |||
621 | ffmt = fimc_find_format(fourcc, code, mask, 0); | ||
622 | if (WARN_ON(!ffmt)) | ||
623 | return NULL; | ||
624 | |||
625 | if (code) | ||
626 | *code = ffmt->mbus_code; | ||
627 | if (fourcc) | ||
628 | *fourcc = ffmt->fourcc; | ||
629 | |||
630 | if (pad != FIMC_SD_PAD_SOURCE) { | ||
631 | max_w = fimc_fmt_is_user_defined(ffmt->color) ? | ||
632 | pl->scaler_dis_w : pl->scaler_en_w; | ||
633 | /* Apply the camera input interface pixel constraints */ | ||
634 | v4l_bound_align_image(width, max_t(u32, *width, 32), max_w, 4, | ||
635 | height, max_t(u32, *height, 32), | ||
636 | FIMC_CAMIF_MAX_HEIGHT, | ||
637 | fimc_fmt_is_user_defined(ffmt->color) ? | ||
638 | 3 : 1, | ||
639 | 0); | ||
640 | return ffmt; | ||
641 | } | ||
642 | /* Can't scale or crop in transparent (JPEG) transfer mode */ | ||
643 | if (fimc_fmt_is_user_defined(ffmt->color)) { | ||
644 | *width = ctx->s_frame.f_width; | ||
645 | *height = ctx->s_frame.f_height; | ||
646 | return ffmt; | ||
647 | } | ||
648 | /* Apply the scaler and the output DMA constraints */ | ||
649 | max_w = rotation ? pl->out_rot_en_w : pl->out_rot_dis_w; | ||
650 | if (ctx->state & FIMC_COMPOSE) { | ||
651 | min_w = dst->offs_h + dst->width; | ||
652 | min_h = dst->offs_v + dst->height; | ||
653 | } else { | ||
654 | min_w = var->min_out_pixsize; | ||
655 | min_h = var->min_out_pixsize; | ||
656 | } | ||
657 | if (var->min_vsize_align == 1 && !rotation) | ||
658 | align_h = fimc_fmt_is_rgb(ffmt->color) ? 0 : 1; | ||
659 | |||
660 | depth = fimc_get_format_depth(ffmt); | ||
661 | v4l_bound_align_image(width, min_w, max_w, | ||
662 | ffs(var->min_out_pixsize) - 1, | ||
663 | height, min_h, FIMC_CAMIF_MAX_HEIGHT, | ||
664 | align_h, | ||
665 | 64/(ALIGN(depth, 8))); | ||
666 | |||
667 | dbg("pad%d: code: 0x%x, %dx%d. dst fmt: %dx%d", | ||
668 | pad, code ? *code : 0, *width, *height, | ||
669 | dst->f_width, dst->f_height); | ||
670 | |||
671 | return ffmt; | ||
672 | } | ||
673 | |||
674 | static void fimc_capture_try_selection(struct fimc_ctx *ctx, | ||
675 | struct v4l2_rect *r, | ||
676 | int target) | ||
677 | { | ||
678 | bool rotate = ctx->rotation == 90 || ctx->rotation == 270; | ||
679 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
680 | const struct fimc_variant *var = fimc->variant; | ||
681 | const struct fimc_pix_limit *pl = var->pix_limit; | ||
682 | struct fimc_frame *sink = &ctx->s_frame; | ||
683 | u32 max_w, max_h, min_w = 0, min_h = 0, min_sz; | ||
684 | u32 align_sz = 0, align_h = 4; | ||
685 | u32 max_sc_h, max_sc_v; | ||
686 | |||
687 | /* In JPEG transparent transfer mode cropping is not supported */ | ||
688 | if (fimc_fmt_is_user_defined(ctx->d_frame.fmt->color)) { | ||
689 | r->width = sink->f_width; | ||
690 | r->height = sink->f_height; | ||
691 | r->left = r->top = 0; | ||
692 | return; | ||
693 | } | ||
694 | if (target == V4L2_SEL_TGT_COMPOSE) { | ||
695 | if (ctx->rotation != 90 && ctx->rotation != 270) | ||
696 | align_h = 1; | ||
697 | max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3)); | ||
698 | max_sc_v = min(SCALER_MAX_VRATIO, 1 << (ffs(sink->height) - 1)); | ||
699 | min_sz = var->min_out_pixsize; | ||
700 | } else { | ||
701 | u32 depth = fimc_get_format_depth(sink->fmt); | ||
702 | align_sz = 64/ALIGN(depth, 8); | ||
703 | min_sz = var->min_inp_pixsize; | ||
704 | min_w = min_h = min_sz; | ||
705 | max_sc_h = max_sc_v = 1; | ||
706 | } | ||
707 | /* | ||
708 | * For the compose rectangle the following constraints must be met: | ||
709 | * - it must fit in the sink pad format rectangle (f_width/f_height); | ||
710 | * - maximum downscaling ratio is 64; | ||
711 | * - maximum crop size depends if the rotator is used or not; | ||
712 | * - the sink pad format width/height must be 4 multiple of the | ||
713 | * prescaler ratios determined by sink pad size and source pad crop, | ||
714 | * the prescaler ratio is returned by fimc_get_scaler_factor(). | ||
715 | */ | ||
716 | max_w = min_t(u32, | ||
717 | rotate ? pl->out_rot_en_w : pl->out_rot_dis_w, | ||
718 | rotate ? sink->f_height : sink->f_width); | ||
719 | max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height); | ||
720 | |||
721 | if (target == V4L2_SEL_TGT_COMPOSE) { | ||
722 | min_w = min_t(u32, max_w, sink->f_width / max_sc_h); | ||
723 | min_h = min_t(u32, max_h, sink->f_height / max_sc_v); | ||
724 | if (rotate) { | ||
725 | swap(max_sc_h, max_sc_v); | ||
726 | swap(min_w, min_h); | ||
727 | } | ||
728 | } | ||
729 | v4l_bound_align_image(&r->width, min_w, max_w, ffs(min_sz) - 1, | ||
730 | &r->height, min_h, max_h, align_h, | ||
731 | align_sz); | ||
732 | /* Adjust left/top if crop/compose rectangle is out of bounds */ | ||
733 | r->left = clamp_t(u32, r->left, 0, sink->f_width - r->width); | ||
734 | r->top = clamp_t(u32, r->top, 0, sink->f_height - r->height); | ||
735 | r->left = round_down(r->left, var->hor_offs_align); | ||
736 | |||
737 | dbg("target %#x: (%d,%d)/%dx%d, sink fmt: %dx%d", | ||
738 | target, r->left, r->top, r->width, r->height, | ||
739 | sink->f_width, sink->f_height); | ||
740 | } | ||
741 | |||
742 | /* | ||
743 | * The video node ioctl operations | ||
744 | */ | ||
745 | static int fimc_cap_querycap(struct file *file, void *priv, | ||
746 | struct v4l2_capability *cap) | ||
747 | { | ||
748 | struct fimc_dev *fimc = video_drvdata(file); | ||
749 | |||
750 | __fimc_vidioc_querycap(&fimc->pdev->dev, cap, V4L2_CAP_STREAMING | | ||
751 | V4L2_CAP_VIDEO_CAPTURE_MPLANE); | ||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv, | ||
756 | struct v4l2_fmtdesc *f) | ||
757 | { | ||
758 | struct fimc_fmt *fmt; | ||
759 | |||
760 | fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM | FMT_FLAGS_M2M, | ||
761 | f->index); | ||
762 | if (!fmt) | ||
763 | return -EINVAL; | ||
764 | strncpy(f->description, fmt->name, sizeof(f->description) - 1); | ||
765 | f->pixelformat = fmt->fourcc; | ||
766 | if (fmt->fourcc == V4L2_MBUS_FMT_JPEG_1X8) | ||
767 | f->flags |= V4L2_FMT_FLAG_COMPRESSED; | ||
768 | return 0; | ||
769 | } | ||
770 | |||
771 | static struct media_entity *fimc_pipeline_get_head(struct media_entity *me) | ||
772 | { | ||
773 | struct media_pad *pad = &me->pads[0]; | ||
774 | |||
775 | while (!(pad->flags & MEDIA_PAD_FL_SOURCE)) { | ||
776 | pad = media_entity_remote_source(pad); | ||
777 | if (!pad) | ||
778 | break; | ||
779 | me = pad->entity; | ||
780 | pad = &me->pads[0]; | ||
781 | } | ||
782 | |||
783 | return me; | ||
784 | } | ||
785 | |||
786 | /** | ||
787 | * fimc_pipeline_try_format - negotiate and/or set formats at pipeline | ||
788 | * elements | ||
789 | * @ctx: FIMC capture context | ||
790 | * @tfmt: media bus format to try/set on subdevs | ||
791 | * @fmt_id: fimc pixel format id corresponding to returned @tfmt (output) | ||
792 | * @set: true to set format on subdevs, false to try only | ||
793 | */ | ||
794 | static int fimc_pipeline_try_format(struct fimc_ctx *ctx, | ||
795 | struct v4l2_mbus_framefmt *tfmt, | ||
796 | struct fimc_fmt **fmt_id, | ||
797 | bool set) | ||
798 | { | ||
799 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
800 | struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; | ||
801 | struct v4l2_subdev_format sfmt; | ||
802 | struct v4l2_mbus_framefmt *mf = &sfmt.format; | ||
803 | struct media_entity *me; | ||
804 | struct fimc_fmt *ffmt; | ||
805 | struct media_pad *pad; | ||
806 | int ret, i = 1; | ||
807 | u32 fcc; | ||
808 | |||
809 | if (WARN_ON(!sd || !tfmt)) | ||
810 | return -EINVAL; | ||
811 | |||
812 | memset(&sfmt, 0, sizeof(sfmt)); | ||
813 | sfmt.format = *tfmt; | ||
814 | sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY; | ||
815 | |||
816 | me = fimc_pipeline_get_head(&sd->entity); | ||
817 | |||
818 | while (1) { | ||
819 | ffmt = fimc_find_format(NULL, mf->code != 0 ? &mf->code : NULL, | ||
820 | FMT_FLAGS_CAM, i++); | ||
821 | if (ffmt == NULL) { | ||
822 | /* | ||
823 | * Notify user-space if common pixel code for | ||
824 | * host and sensor does not exist. | ||
825 | */ | ||
826 | return -EINVAL; | ||
827 | } | ||
828 | mf->code = tfmt->code = ffmt->mbus_code; | ||
829 | |||
830 | /* set format on all pipeline subdevs */ | ||
831 | while (me != &fimc->vid_cap.subdev.entity) { | ||
832 | sd = media_entity_to_v4l2_subdev(me); | ||
833 | |||
834 | sfmt.pad = 0; | ||
835 | ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt); | ||
836 | if (ret) | ||
837 | return ret; | ||
838 | |||
839 | if (me->pads[0].flags & MEDIA_PAD_FL_SINK) { | ||
840 | sfmt.pad = me->num_pads - 1; | ||
841 | mf->code = tfmt->code; | ||
842 | ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, | ||
843 | &sfmt); | ||
844 | if (ret) | ||
845 | return ret; | ||
846 | } | ||
847 | |||
848 | pad = media_entity_remote_source(&me->pads[sfmt.pad]); | ||
849 | if (!pad) | ||
850 | return -EINVAL; | ||
851 | me = pad->entity; | ||
852 | } | ||
853 | |||
854 | if (mf->code != tfmt->code) | ||
855 | continue; | ||
856 | |||
857 | fcc = ffmt->fourcc; | ||
858 | tfmt->width = mf->width; | ||
859 | tfmt->height = mf->height; | ||
860 | ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height, | ||
861 | NULL, &fcc, FIMC_SD_PAD_SINK_CAM); | ||
862 | ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height, | ||
863 | NULL, &fcc, FIMC_SD_PAD_SOURCE); | ||
864 | if (ffmt && ffmt->mbus_code) | ||
865 | mf->code = ffmt->mbus_code; | ||
866 | if (mf->width != tfmt->width || mf->height != tfmt->height) | ||
867 | continue; | ||
868 | tfmt->code = mf->code; | ||
869 | break; | ||
870 | } | ||
871 | |||
872 | if (fmt_id && ffmt) | ||
873 | *fmt_id = ffmt; | ||
874 | *tfmt = *mf; | ||
875 | |||
876 | return 0; | ||
877 | } | ||
878 | |||
879 | /** | ||
880 | * fimc_get_sensor_frame_desc - query the sensor for media bus frame parameters | ||
881 | * @sensor: pointer to the sensor subdev | ||
882 | * @plane_fmt: provides plane sizes corresponding to the frame layout entries | ||
883 | * @try: true to set the frame parameters, false to query only | ||
884 | * | ||
885 | * This function is used by this driver only for compressed/blob data formats. | ||
886 | */ | ||
887 | static int fimc_get_sensor_frame_desc(struct v4l2_subdev *sensor, | ||
888 | struct v4l2_plane_pix_format *plane_fmt, | ||
889 | unsigned int num_planes, bool try) | ||
890 | { | ||
891 | struct v4l2_mbus_frame_desc fd; | ||
892 | int i, ret; | ||
893 | int pad; | ||
894 | |||
895 | for (i = 0; i < num_planes; i++) | ||
896 | fd.entry[i].length = plane_fmt[i].sizeimage; | ||
897 | |||
898 | pad = sensor->entity.num_pads - 1; | ||
899 | if (try) | ||
900 | ret = v4l2_subdev_call(sensor, pad, set_frame_desc, pad, &fd); | ||
901 | else | ||
902 | ret = v4l2_subdev_call(sensor, pad, get_frame_desc, pad, &fd); | ||
903 | |||
904 | if (ret < 0) | ||
905 | return ret; | ||
906 | |||
907 | if (num_planes != fd.num_entries) | ||
908 | return -EINVAL; | ||
909 | |||
910 | for (i = 0; i < num_planes; i++) | ||
911 | plane_fmt[i].sizeimage = fd.entry[i].length; | ||
912 | |||
913 | if (fd.entry[0].length > FIMC_MAX_JPEG_BUF_SIZE) { | ||
914 | v4l2_err(sensor->v4l2_dev, "Unsupported buffer size: %u\n", | ||
915 | fd.entry[0].length); | ||
916 | |||
917 | return -EINVAL; | ||
918 | } | ||
919 | |||
920 | return 0; | ||
921 | } | ||
922 | |||
923 | static int fimc_cap_g_fmt_mplane(struct file *file, void *fh, | ||
924 | struct v4l2_format *f) | ||
925 | { | ||
926 | struct fimc_dev *fimc = video_drvdata(file); | ||
927 | |||
928 | __fimc_get_format(&fimc->vid_cap.ctx->d_frame, f); | ||
929 | return 0; | ||
930 | } | ||
931 | |||
932 | static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, | ||
933 | struct v4l2_format *f) | ||
934 | { | ||
935 | struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; | ||
936 | struct fimc_dev *fimc = video_drvdata(file); | ||
937 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | ||
938 | struct v4l2_mbus_framefmt mf; | ||
939 | struct fimc_fmt *ffmt = NULL; | ||
940 | int ret = 0; | ||
941 | |||
942 | fimc_md_graph_lock(fimc); | ||
943 | mutex_lock(&fimc->lock); | ||
944 | |||
945 | if (fimc_jpeg_fourcc(pix->pixelformat)) { | ||
946 | fimc_capture_try_format(ctx, &pix->width, &pix->height, | ||
947 | NULL, &pix->pixelformat, | ||
948 | FIMC_SD_PAD_SINK_CAM); | ||
949 | ctx->s_frame.f_width = pix->width; | ||
950 | ctx->s_frame.f_height = pix->height; | ||
951 | } | ||
952 | ffmt = fimc_capture_try_format(ctx, &pix->width, &pix->height, | ||
953 | NULL, &pix->pixelformat, | ||
954 | FIMC_SD_PAD_SOURCE); | ||
955 | if (!ffmt) { | ||
956 | ret = -EINVAL; | ||
957 | goto unlock; | ||
958 | } | ||
959 | |||
960 | if (!fimc->vid_cap.user_subdev_api) { | ||
961 | mf.width = pix->width; | ||
962 | mf.height = pix->height; | ||
963 | mf.code = ffmt->mbus_code; | ||
964 | fimc_pipeline_try_format(ctx, &mf, &ffmt, false); | ||
965 | pix->width = mf.width; | ||
966 | pix->height = mf.height; | ||
967 | if (ffmt) | ||
968 | pix->pixelformat = ffmt->fourcc; | ||
969 | } | ||
970 | |||
971 | fimc_adjust_mplane_format(ffmt, pix->width, pix->height, pix); | ||
972 | |||
973 | if (ffmt->flags & FMT_FLAGS_COMPRESSED) | ||
974 | fimc_get_sensor_frame_desc(fimc->pipeline.subdevs[IDX_SENSOR], | ||
975 | pix->plane_fmt, ffmt->memplanes, true); | ||
976 | unlock: | ||
977 | mutex_unlock(&fimc->lock); | ||
978 | fimc_md_graph_unlock(fimc); | ||
979 | |||
980 | return ret; | ||
981 | } | ||
982 | |||
983 | static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, | ||
984 | enum fimc_color_fmt color) | ||
985 | { | ||
986 | bool jpeg = fimc_fmt_is_user_defined(color); | ||
987 | |||
988 | ctx->scaler.enabled = !jpeg; | ||
989 | fimc_ctrls_activate(ctx, !jpeg); | ||
990 | |||
991 | if (jpeg) | ||
992 | set_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state); | ||
993 | else | ||
994 | clear_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state); | ||
995 | } | ||
996 | |||
997 | static int __fimc_capture_set_format(struct fimc_dev *fimc, | ||
998 | struct v4l2_format *f) | ||
999 | { | ||
1000 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | ||
1001 | struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; | ||
1002 | struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.ci_fmt; | ||
1003 | struct fimc_frame *ff = &ctx->d_frame; | ||
1004 | struct fimc_fmt *s_fmt = NULL; | ||
1005 | int ret, i; | ||
1006 | |||
1007 | if (vb2_is_busy(&fimc->vid_cap.vbq)) | ||
1008 | return -EBUSY; | ||
1009 | |||
1010 | /* Pre-configure format at camera interface input, for JPEG only */ | ||
1011 | if (fimc_jpeg_fourcc(pix->pixelformat)) { | ||
1012 | fimc_capture_try_format(ctx, &pix->width, &pix->height, | ||
1013 | NULL, &pix->pixelformat, | ||
1014 | FIMC_SD_PAD_SINK_CAM); | ||
1015 | ctx->s_frame.f_width = pix->width; | ||
1016 | ctx->s_frame.f_height = pix->height; | ||
1017 | } | ||
1018 | /* Try the format at the scaler and the DMA output */ | ||
1019 | ff->fmt = fimc_capture_try_format(ctx, &pix->width, &pix->height, | ||
1020 | NULL, &pix->pixelformat, | ||
1021 | FIMC_SD_PAD_SOURCE); | ||
1022 | if (!ff->fmt) | ||
1023 | return -EINVAL; | ||
1024 | |||
1025 | /* Update RGB Alpha control state and value range */ | ||
1026 | fimc_alpha_ctrl_update(ctx); | ||
1027 | |||
1028 | /* Try to match format at the host and the sensor */ | ||
1029 | if (!fimc->vid_cap.user_subdev_api) { | ||
1030 | mf->code = ff->fmt->mbus_code; | ||
1031 | mf->width = pix->width; | ||
1032 | mf->height = pix->height; | ||
1033 | ret = fimc_pipeline_try_format(ctx, mf, &s_fmt, true); | ||
1034 | if (ret) | ||
1035 | return ret; | ||
1036 | |||
1037 | pix->width = mf->width; | ||
1038 | pix->height = mf->height; | ||
1039 | } | ||
1040 | |||
1041 | fimc_adjust_mplane_format(ff->fmt, pix->width, pix->height, pix); | ||
1042 | |||
1043 | if (ff->fmt->flags & FMT_FLAGS_COMPRESSED) { | ||
1044 | ret = fimc_get_sensor_frame_desc(fimc->pipeline.subdevs[IDX_SENSOR], | ||
1045 | pix->plane_fmt, ff->fmt->memplanes, | ||
1046 | true); | ||
1047 | if (ret < 0) | ||
1048 | return ret; | ||
1049 | } | ||
1050 | |||
1051 | for (i = 0; i < ff->fmt->memplanes; i++) { | ||
1052 | ff->bytesperline[i] = pix->plane_fmt[i].bytesperline; | ||
1053 | ff->payload[i] = pix->plane_fmt[i].sizeimage; | ||
1054 | } | ||
1055 | |||
1056 | set_frame_bounds(ff, pix->width, pix->height); | ||
1057 | /* Reset the composition rectangle if not yet configured */ | ||
1058 | if (!(ctx->state & FIMC_COMPOSE)) | ||
1059 | set_frame_crop(ff, 0, 0, pix->width, pix->height); | ||
1060 | |||
1061 | fimc_capture_mark_jpeg_xfer(ctx, ff->fmt->color); | ||
1062 | |||
1063 | /* Reset cropping and set format at the camera interface input */ | ||
1064 | if (!fimc->vid_cap.user_subdev_api) { | ||
1065 | ctx->s_frame.fmt = s_fmt; | ||
1066 | set_frame_bounds(&ctx->s_frame, pix->width, pix->height); | ||
1067 | set_frame_crop(&ctx->s_frame, 0, 0, pix->width, pix->height); | ||
1068 | } | ||
1069 | |||
1070 | return ret; | ||
1071 | } | ||
1072 | |||
1073 | static int fimc_cap_s_fmt_mplane(struct file *file, void *priv, | ||
1074 | struct v4l2_format *f) | ||
1075 | { | ||
1076 | struct fimc_dev *fimc = video_drvdata(file); | ||
1077 | int ret; | ||
1078 | |||
1079 | fimc_md_graph_lock(fimc); | ||
1080 | mutex_lock(&fimc->lock); | ||
1081 | /* | ||
1082 | * The graph is walked within __fimc_capture_set_format() to set | ||
1083 | * the format at subdevs thus the graph mutex needs to be held at | ||
1084 | * this point and acquired before the video mutex, to avoid AB-BA | ||
1085 | * deadlock when fimc_md_link_notify() is called by other thread. | ||
1086 | * Ideally the graph walking and setting format at the whole pipeline | ||
1087 | * should be removed from this driver and handled in userspace only. | ||
1088 | */ | ||
1089 | ret = __fimc_capture_set_format(fimc, f); | ||
1090 | |||
1091 | mutex_unlock(&fimc->lock); | ||
1092 | fimc_md_graph_unlock(fimc); | ||
1093 | return ret; | ||
1094 | } | ||
1095 | |||
1096 | static int fimc_cap_enum_input(struct file *file, void *priv, | ||
1097 | struct v4l2_input *i) | ||
1098 | { | ||
1099 | struct fimc_dev *fimc = video_drvdata(file); | ||
1100 | struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; | ||
1101 | |||
1102 | if (i->index != 0) | ||
1103 | return -EINVAL; | ||
1104 | |||
1105 | i->type = V4L2_INPUT_TYPE_CAMERA; | ||
1106 | if (sd) | ||
1107 | strlcpy(i->name, sd->name, sizeof(i->name)); | ||
1108 | return 0; | ||
1109 | } | ||
1110 | |||
1111 | static int fimc_cap_s_input(struct file *file, void *priv, unsigned int i) | ||
1112 | { | ||
1113 | return i == 0 ? i : -EINVAL; | ||
1114 | } | ||
1115 | |||
1116 | static int fimc_cap_g_input(struct file *file, void *priv, unsigned int *i) | ||
1117 | { | ||
1118 | *i = 0; | ||
1119 | return 0; | ||
1120 | } | ||
1121 | |||
1122 | /** | ||
1123 | * fimc_pipeline_validate - check for formats inconsistencies | ||
1124 | * between source and sink pad of each link | ||
1125 | * | ||
1126 | * Return 0 if all formats match or -EPIPE otherwise. | ||
1127 | */ | ||
1128 | static int fimc_pipeline_validate(struct fimc_dev *fimc) | ||
1129 | { | ||
1130 | struct v4l2_subdev_format sink_fmt, src_fmt; | ||
1131 | struct fimc_vid_cap *vc = &fimc->vid_cap; | ||
1132 | struct v4l2_subdev *sd = &vc->subdev; | ||
1133 | struct media_pad *sink_pad, *src_pad; | ||
1134 | int i, ret; | ||
1135 | |||
1136 | while (1) { | ||
1137 | /* | ||
1138 | * Find current entity sink pad and any remote sink pad linked | ||
1139 | * to it. We stop if there is no sink pad in current entity or | ||
1140 | * it is not linked to any other remote entity. | ||
1141 | */ | ||
1142 | src_pad = NULL; | ||
1143 | |||
1144 | for (i = 0; i < sd->entity.num_pads; i++) { | ||
1145 | struct media_pad *p = &sd->entity.pads[i]; | ||
1146 | |||
1147 | if (p->flags & MEDIA_PAD_FL_SINK) { | ||
1148 | sink_pad = p; | ||
1149 | src_pad = media_entity_remote_source(sink_pad); | ||
1150 | if (src_pad) | ||
1151 | break; | ||
1152 | } | ||
1153 | } | ||
1154 | |||
1155 | if (src_pad == NULL || | ||
1156 | media_entity_type(src_pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
1157 | break; | ||
1158 | |||
1159 | /* Don't call FIMC subdev operation to avoid nested locking */ | ||
1160 | if (sd == &vc->subdev) { | ||
1161 | struct fimc_frame *ff = &vc->ctx->s_frame; | ||
1162 | sink_fmt.format.width = ff->f_width; | ||
1163 | sink_fmt.format.height = ff->f_height; | ||
1164 | sink_fmt.format.code = ff->fmt ? ff->fmt->mbus_code : 0; | ||
1165 | } else { | ||
1166 | sink_fmt.pad = sink_pad->index; | ||
1167 | sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
1168 | ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt); | ||
1169 | if (ret < 0 && ret != -ENOIOCTLCMD) | ||
1170 | return -EPIPE; | ||
1171 | } | ||
1172 | |||
1173 | /* Retrieve format at the source pad */ | ||
1174 | sd = media_entity_to_v4l2_subdev(src_pad->entity); | ||
1175 | src_fmt.pad = src_pad->index; | ||
1176 | src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
1177 | ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); | ||
1178 | if (ret < 0 && ret != -ENOIOCTLCMD) | ||
1179 | return -EPIPE; | ||
1180 | |||
1181 | if (src_fmt.format.width != sink_fmt.format.width || | ||
1182 | src_fmt.format.height != sink_fmt.format.height || | ||
1183 | src_fmt.format.code != sink_fmt.format.code) | ||
1184 | return -EPIPE; | ||
1185 | |||
1186 | if (sd == fimc->pipeline.subdevs[IDX_SENSOR] && | ||
1187 | fimc_user_defined_mbus_fmt(src_fmt.format.code)) { | ||
1188 | struct v4l2_plane_pix_format plane_fmt[FIMC_MAX_PLANES]; | ||
1189 | struct fimc_frame *frame = &vc->ctx->d_frame; | ||
1190 | unsigned int i; | ||
1191 | |||
1192 | ret = fimc_get_sensor_frame_desc(sd, plane_fmt, | ||
1193 | frame->fmt->memplanes, | ||
1194 | false); | ||
1195 | if (ret < 0) | ||
1196 | return -EPIPE; | ||
1197 | |||
1198 | for (i = 0; i < frame->fmt->memplanes; i++) | ||
1199 | if (frame->payload[i] < plane_fmt[i].sizeimage) | ||
1200 | return -EPIPE; | ||
1201 | } | ||
1202 | } | ||
1203 | return 0; | ||
1204 | } | ||
1205 | |||
1206 | static int fimc_cap_streamon(struct file *file, void *priv, | ||
1207 | enum v4l2_buf_type type) | ||
1208 | { | ||
1209 | struct fimc_dev *fimc = video_drvdata(file); | ||
1210 | struct fimc_pipeline *p = &fimc->pipeline; | ||
1211 | struct fimc_vid_cap *vc = &fimc->vid_cap; | ||
1212 | struct media_entity *entity = &vc->vfd.entity; | ||
1213 | struct fimc_source_info *si = NULL; | ||
1214 | struct v4l2_subdev *sd; | ||
1215 | int ret; | ||
1216 | |||
1217 | if (fimc_capture_active(fimc)) | ||
1218 | return -EBUSY; | ||
1219 | |||
1220 | ret = media_entity_pipeline_start(entity, p->m_pipeline); | ||
1221 | if (ret < 0) | ||
1222 | return ret; | ||
1223 | |||
1224 | sd = p->subdevs[IDX_SENSOR]; | ||
1225 | if (sd) | ||
1226 | si = v4l2_get_subdev_hostdata(sd); | ||
1227 | |||
1228 | if (si == NULL) { | ||
1229 | ret = -EPIPE; | ||
1230 | goto err_p_stop; | ||
1231 | } | ||
1232 | /* | ||
1233 | * Save configuration data related to currently attached image | ||
1234 | * sensor or other data source, e.g. FIMC-IS. | ||
1235 | */ | ||
1236 | vc->source_config = *si; | ||
1237 | |||
1238 | if (vc->input == GRP_ID_FIMC_IS) | ||
1239 | vc->source_config.fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK; | ||
1240 | |||
1241 | if (vc->user_subdev_api) { | ||
1242 | ret = fimc_pipeline_validate(fimc); | ||
1243 | if (ret < 0) | ||
1244 | goto err_p_stop; | ||
1245 | } | ||
1246 | |||
1247 | ret = vb2_ioctl_streamon(file, priv, type); | ||
1248 | if (!ret) { | ||
1249 | vc->streaming = true; | ||
1250 | return ret; | ||
1251 | } | ||
1252 | |||
1253 | err_p_stop: | ||
1254 | media_entity_pipeline_stop(entity); | ||
1255 | return ret; | ||
1256 | } | ||
1257 | |||
1258 | static int fimc_cap_streamoff(struct file *file, void *priv, | ||
1259 | enum v4l2_buf_type type) | ||
1260 | { | ||
1261 | struct fimc_dev *fimc = video_drvdata(file); | ||
1262 | int ret; | ||
1263 | |||
1264 | ret = vb2_ioctl_streamoff(file, priv, type); | ||
1265 | if (ret < 0) | ||
1266 | return ret; | ||
1267 | |||
1268 | media_entity_pipeline_stop(&fimc->vid_cap.vfd.entity); | ||
1269 | fimc->vid_cap.streaming = false; | ||
1270 | return 0; | ||
1271 | } | ||
1272 | |||
1273 | static int fimc_cap_reqbufs(struct file *file, void *priv, | ||
1274 | struct v4l2_requestbuffers *reqbufs) | ||
1275 | { | ||
1276 | struct fimc_dev *fimc = video_drvdata(file); | ||
1277 | int ret; | ||
1278 | |||
1279 | ret = vb2_ioctl_reqbufs(file, priv, reqbufs); | ||
1280 | |||
1281 | if (!ret) | ||
1282 | fimc->vid_cap.reqbufs_count = reqbufs->count; | ||
1283 | |||
1284 | return ret; | ||
1285 | } | ||
1286 | |||
1287 | static int fimc_cap_g_selection(struct file *file, void *fh, | ||
1288 | struct v4l2_selection *s) | ||
1289 | { | ||
1290 | struct fimc_dev *fimc = video_drvdata(file); | ||
1291 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | ||
1292 | struct fimc_frame *f = &ctx->s_frame; | ||
1293 | |||
1294 | if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | ||
1295 | return -EINVAL; | ||
1296 | |||
1297 | switch (s->target) { | ||
1298 | case V4L2_SEL_TGT_COMPOSE_DEFAULT: | ||
1299 | case V4L2_SEL_TGT_COMPOSE_BOUNDS: | ||
1300 | f = &ctx->d_frame; | ||
1301 | case V4L2_SEL_TGT_CROP_BOUNDS: | ||
1302 | case V4L2_SEL_TGT_CROP_DEFAULT: | ||
1303 | s->r.left = 0; | ||
1304 | s->r.top = 0; | ||
1305 | s->r.width = f->o_width; | ||
1306 | s->r.height = f->o_height; | ||
1307 | return 0; | ||
1308 | |||
1309 | case V4L2_SEL_TGT_COMPOSE: | ||
1310 | f = &ctx->d_frame; | ||
1311 | case V4L2_SEL_TGT_CROP: | ||
1312 | s->r.left = f->offs_h; | ||
1313 | s->r.top = f->offs_v; | ||
1314 | s->r.width = f->width; | ||
1315 | s->r.height = f->height; | ||
1316 | return 0; | ||
1317 | } | ||
1318 | |||
1319 | return -EINVAL; | ||
1320 | } | ||
1321 | |||
1322 | /* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ | ||
1323 | static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) | ||
1324 | { | ||
1325 | if (a->left < b->left || a->top < b->top) | ||
1326 | return 0; | ||
1327 | if (a->left + a->width > b->left + b->width) | ||
1328 | return 0; | ||
1329 | if (a->top + a->height > b->top + b->height) | ||
1330 | return 0; | ||
1331 | |||
1332 | return 1; | ||
1333 | } | ||
1334 | |||
1335 | static int fimc_cap_s_selection(struct file *file, void *fh, | ||
1336 | struct v4l2_selection *s) | ||
1337 | { | ||
1338 | struct fimc_dev *fimc = video_drvdata(file); | ||
1339 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | ||
1340 | struct v4l2_rect rect = s->r; | ||
1341 | struct fimc_frame *f; | ||
1342 | unsigned long flags; | ||
1343 | |||
1344 | if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | ||
1345 | return -EINVAL; | ||
1346 | |||
1347 | if (s->target == V4L2_SEL_TGT_COMPOSE) | ||
1348 | f = &ctx->d_frame; | ||
1349 | else if (s->target == V4L2_SEL_TGT_CROP) | ||
1350 | f = &ctx->s_frame; | ||
1351 | else | ||
1352 | return -EINVAL; | ||
1353 | |||
1354 | fimc_capture_try_selection(ctx, &rect, s->target); | ||
1355 | |||
1356 | if (s->flags & V4L2_SEL_FLAG_LE && | ||
1357 | !enclosed_rectangle(&rect, &s->r)) | ||
1358 | return -ERANGE; | ||
1359 | |||
1360 | if (s->flags & V4L2_SEL_FLAG_GE && | ||
1361 | !enclosed_rectangle(&s->r, &rect)) | ||
1362 | return -ERANGE; | ||
1363 | |||
1364 | s->r = rect; | ||
1365 | spin_lock_irqsave(&fimc->slock, flags); | ||
1366 | set_frame_crop(f, s->r.left, s->r.top, s->r.width, | ||
1367 | s->r.height); | ||
1368 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
1369 | |||
1370 | set_bit(ST_CAPT_APPLY_CFG, &fimc->state); | ||
1371 | return 0; | ||
1372 | } | ||
1373 | |||
1374 | static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { | ||
1375 | .vidioc_querycap = fimc_cap_querycap, | ||
1376 | |||
1377 | .vidioc_enum_fmt_vid_cap_mplane = fimc_cap_enum_fmt_mplane, | ||
1378 | .vidioc_try_fmt_vid_cap_mplane = fimc_cap_try_fmt_mplane, | ||
1379 | .vidioc_s_fmt_vid_cap_mplane = fimc_cap_s_fmt_mplane, | ||
1380 | .vidioc_g_fmt_vid_cap_mplane = fimc_cap_g_fmt_mplane, | ||
1381 | |||
1382 | .vidioc_reqbufs = fimc_cap_reqbufs, | ||
1383 | .vidioc_querybuf = vb2_ioctl_querybuf, | ||
1384 | .vidioc_qbuf = vb2_ioctl_qbuf, | ||
1385 | .vidioc_dqbuf = vb2_ioctl_dqbuf, | ||
1386 | .vidioc_expbuf = vb2_ioctl_expbuf, | ||
1387 | .vidioc_prepare_buf = vb2_ioctl_prepare_buf, | ||
1388 | .vidioc_create_bufs = vb2_ioctl_create_bufs, | ||
1389 | |||
1390 | .vidioc_streamon = fimc_cap_streamon, | ||
1391 | .vidioc_streamoff = fimc_cap_streamoff, | ||
1392 | |||
1393 | .vidioc_g_selection = fimc_cap_g_selection, | ||
1394 | .vidioc_s_selection = fimc_cap_s_selection, | ||
1395 | |||
1396 | .vidioc_enum_input = fimc_cap_enum_input, | ||
1397 | .vidioc_s_input = fimc_cap_s_input, | ||
1398 | .vidioc_g_input = fimc_cap_g_input, | ||
1399 | }; | ||
1400 | |||
1401 | /* Capture subdev media entity operations */ | ||
1402 | static int fimc_link_setup(struct media_entity *entity, | ||
1403 | const struct media_pad *local, | ||
1404 | const struct media_pad *remote, u32 flags) | ||
1405 | { | ||
1406 | struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
1407 | struct fimc_dev *fimc = v4l2_get_subdevdata(sd); | ||
1408 | |||
1409 | if (media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
1410 | return -EINVAL; | ||
1411 | |||
1412 | if (WARN_ON(fimc == NULL)) | ||
1413 | return 0; | ||
1414 | |||
1415 | dbg("%s --> %s, flags: 0x%x. input: 0x%x", | ||
1416 | local->entity->name, remote->entity->name, flags, | ||
1417 | fimc->vid_cap.input); | ||
1418 | |||
1419 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
1420 | if (fimc->vid_cap.input != 0) | ||
1421 | return -EBUSY; | ||
1422 | fimc->vid_cap.input = sd->grp_id; | ||
1423 | return 0; | ||
1424 | } | ||
1425 | |||
1426 | fimc->vid_cap.input = 0; | ||
1427 | return 0; | ||
1428 | } | ||
1429 | |||
1430 | static const struct media_entity_operations fimc_sd_media_ops = { | ||
1431 | .link_setup = fimc_link_setup, | ||
1432 | }; | ||
1433 | |||
1434 | /** | ||
1435 | * fimc_sensor_notify - v4l2_device notification from a sensor subdev | ||
1436 | * @sd: pointer to a subdev generating the notification | ||
1437 | * @notification: the notification type, must be S5P_FIMC_TX_END_NOTIFY | ||
1438 | * @arg: pointer to an u32 type integer that stores the frame payload value | ||
1439 | * | ||
1440 | * The End Of Frame notification sent by sensor subdev in its still capture | ||
1441 | * mode. If there is only a single VSYNC generated by the sensor at the | ||
1442 | * beginning of a frame transmission, FIMC does not issue the LastIrq | ||
1443 | * (end of frame) interrupt. And this notification is used to complete the | ||
1444 | * frame capture and returning a buffer to user-space. Subdev drivers should | ||
1445 | * call this notification from their last 'End of frame capture' interrupt. | ||
1446 | */ | ||
1447 | void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, | ||
1448 | void *arg) | ||
1449 | { | ||
1450 | struct fimc_source_info *si; | ||
1451 | struct fimc_vid_buffer *buf; | ||
1452 | struct fimc_md *fmd; | ||
1453 | struct fimc_dev *fimc; | ||
1454 | unsigned long flags; | ||
1455 | |||
1456 | if (sd == NULL) | ||
1457 | return; | ||
1458 | |||
1459 | si = v4l2_get_subdev_hostdata(sd); | ||
1460 | fmd = entity_to_fimc_mdev(&sd->entity); | ||
1461 | |||
1462 | spin_lock_irqsave(&fmd->slock, flags); | ||
1463 | |||
1464 | fimc = si ? source_to_sensor_info(si)->host : NULL; | ||
1465 | |||
1466 | if (fimc && arg && notification == S5P_FIMC_TX_END_NOTIFY && | ||
1467 | test_bit(ST_CAPT_PEND, &fimc->state)) { | ||
1468 | unsigned long irq_flags; | ||
1469 | spin_lock_irqsave(&fimc->slock, irq_flags); | ||
1470 | if (!list_empty(&fimc->vid_cap.active_buf_q)) { | ||
1471 | buf = list_entry(fimc->vid_cap.active_buf_q.next, | ||
1472 | struct fimc_vid_buffer, list); | ||
1473 | vb2_set_plane_payload(&buf->vb, 0, *((u32 *)arg)); | ||
1474 | } | ||
1475 | fimc_capture_irq_handler(fimc, 1); | ||
1476 | fimc_deactivate_capture(fimc); | ||
1477 | spin_unlock_irqrestore(&fimc->slock, irq_flags); | ||
1478 | } | ||
1479 | spin_unlock_irqrestore(&fmd->slock, flags); | ||
1480 | } | ||
1481 | |||
1482 | static int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd, | ||
1483 | struct v4l2_subdev_fh *fh, | ||
1484 | struct v4l2_subdev_mbus_code_enum *code) | ||
1485 | { | ||
1486 | struct fimc_fmt *fmt; | ||
1487 | |||
1488 | fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, code->index); | ||
1489 | if (!fmt) | ||
1490 | return -EINVAL; | ||
1491 | code->code = fmt->mbus_code; | ||
1492 | return 0; | ||
1493 | } | ||
1494 | |||
1495 | static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, | ||
1496 | struct v4l2_subdev_fh *fh, | ||
1497 | struct v4l2_subdev_format *fmt) | ||
1498 | { | ||
1499 | struct fimc_dev *fimc = v4l2_get_subdevdata(sd); | ||
1500 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | ||
1501 | struct fimc_frame *ff = &ctx->s_frame; | ||
1502 | struct v4l2_mbus_framefmt *mf; | ||
1503 | |||
1504 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1505 | mf = v4l2_subdev_get_try_format(fh, fmt->pad); | ||
1506 | fmt->format = *mf; | ||
1507 | return 0; | ||
1508 | } | ||
1509 | |||
1510 | mf = &fmt->format; | ||
1511 | mutex_lock(&fimc->lock); | ||
1512 | |||
1513 | switch (fmt->pad) { | ||
1514 | case FIMC_SD_PAD_SOURCE: | ||
1515 | if (!WARN_ON(ff->fmt == NULL)) | ||
1516 | mf->code = ff->fmt->mbus_code; | ||
1517 | /* Sink pads crop rectangle size */ | ||
1518 | mf->width = ff->width; | ||
1519 | mf->height = ff->height; | ||
1520 | break; | ||
1521 | case FIMC_SD_PAD_SINK_FIFO: | ||
1522 | *mf = fimc->vid_cap.wb_fmt; | ||
1523 | break; | ||
1524 | case FIMC_SD_PAD_SINK_CAM: | ||
1525 | default: | ||
1526 | *mf = fimc->vid_cap.ci_fmt; | ||
1527 | break; | ||
1528 | } | ||
1529 | |||
1530 | mutex_unlock(&fimc->lock); | ||
1531 | mf->colorspace = V4L2_COLORSPACE_JPEG; | ||
1532 | |||
1533 | return 0; | ||
1534 | } | ||
1535 | |||
1536 | static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, | ||
1537 | struct v4l2_subdev_fh *fh, | ||
1538 | struct v4l2_subdev_format *fmt) | ||
1539 | { | ||
1540 | struct fimc_dev *fimc = v4l2_get_subdevdata(sd); | ||
1541 | struct v4l2_mbus_framefmt *mf = &fmt->format; | ||
1542 | struct fimc_vid_cap *vc = &fimc->vid_cap; | ||
1543 | struct fimc_ctx *ctx = vc->ctx; | ||
1544 | struct fimc_frame *ff; | ||
1545 | struct fimc_fmt *ffmt; | ||
1546 | |||
1547 | dbg("pad%d: code: 0x%x, %dx%d", | ||
1548 | fmt->pad, mf->code, mf->width, mf->height); | ||
1549 | |||
1550 | if (fmt->pad == FIMC_SD_PAD_SOURCE && vb2_is_busy(&vc->vbq)) | ||
1551 | return -EBUSY; | ||
1552 | |||
1553 | mutex_lock(&fimc->lock); | ||
1554 | ffmt = fimc_capture_try_format(ctx, &mf->width, &mf->height, | ||
1555 | &mf->code, NULL, fmt->pad); | ||
1556 | mutex_unlock(&fimc->lock); | ||
1557 | mf->colorspace = V4L2_COLORSPACE_JPEG; | ||
1558 | |||
1559 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1560 | mf = v4l2_subdev_get_try_format(fh, fmt->pad); | ||
1561 | *mf = fmt->format; | ||
1562 | return 0; | ||
1563 | } | ||
1564 | /* There must be a bug in the driver if this happens */ | ||
1565 | if (WARN_ON(ffmt == NULL)) | ||
1566 | return -EINVAL; | ||
1567 | |||
1568 | /* Update RGB Alpha control state and value range */ | ||
1569 | fimc_alpha_ctrl_update(ctx); | ||
1570 | |||
1571 | fimc_capture_mark_jpeg_xfer(ctx, ffmt->color); | ||
1572 | if (fmt->pad == FIMC_SD_PAD_SOURCE) { | ||
1573 | ff = &ctx->d_frame; | ||
1574 | /* Sink pads crop rectangle size */ | ||
1575 | mf->width = ctx->s_frame.width; | ||
1576 | mf->height = ctx->s_frame.height; | ||
1577 | } else { | ||
1578 | ff = &ctx->s_frame; | ||
1579 | } | ||
1580 | |||
1581 | mutex_lock(&fimc->lock); | ||
1582 | set_frame_bounds(ff, mf->width, mf->height); | ||
1583 | |||
1584 | if (fmt->pad == FIMC_SD_PAD_SINK_FIFO) | ||
1585 | vc->wb_fmt = *mf; | ||
1586 | else if (fmt->pad == FIMC_SD_PAD_SINK_CAM) | ||
1587 | vc->ci_fmt = *mf; | ||
1588 | |||
1589 | ff->fmt = ffmt; | ||
1590 | |||
1591 | /* Reset the crop rectangle if required. */ | ||
1592 | if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_COMPOSE))) | ||
1593 | set_frame_crop(ff, 0, 0, mf->width, mf->height); | ||
1594 | |||
1595 | if (fmt->pad != FIMC_SD_PAD_SOURCE) | ||
1596 | ctx->state &= ~FIMC_COMPOSE; | ||
1597 | |||
1598 | mutex_unlock(&fimc->lock); | ||
1599 | return 0; | ||
1600 | } | ||
1601 | |||
1602 | static int fimc_subdev_get_selection(struct v4l2_subdev *sd, | ||
1603 | struct v4l2_subdev_fh *fh, | ||
1604 | struct v4l2_subdev_selection *sel) | ||
1605 | { | ||
1606 | struct fimc_dev *fimc = v4l2_get_subdevdata(sd); | ||
1607 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | ||
1608 | struct fimc_frame *f = &ctx->s_frame; | ||
1609 | struct v4l2_rect *r = &sel->r; | ||
1610 | struct v4l2_rect *try_sel; | ||
1611 | |||
1612 | if (sel->pad == FIMC_SD_PAD_SOURCE) | ||
1613 | return -EINVAL; | ||
1614 | |||
1615 | mutex_lock(&fimc->lock); | ||
1616 | |||
1617 | switch (sel->target) { | ||
1618 | case V4L2_SEL_TGT_COMPOSE_BOUNDS: | ||
1619 | f = &ctx->d_frame; | ||
1620 | case V4L2_SEL_TGT_CROP_BOUNDS: | ||
1621 | r->width = f->o_width; | ||
1622 | r->height = f->o_height; | ||
1623 | r->left = 0; | ||
1624 | r->top = 0; | ||
1625 | mutex_unlock(&fimc->lock); | ||
1626 | return 0; | ||
1627 | |||
1628 | case V4L2_SEL_TGT_CROP: | ||
1629 | try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); | ||
1630 | break; | ||
1631 | case V4L2_SEL_TGT_COMPOSE: | ||
1632 | try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); | ||
1633 | f = &ctx->d_frame; | ||
1634 | break; | ||
1635 | default: | ||
1636 | mutex_unlock(&fimc->lock); | ||
1637 | return -EINVAL; | ||
1638 | } | ||
1639 | |||
1640 | if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1641 | sel->r = *try_sel; | ||
1642 | } else { | ||
1643 | r->left = f->offs_h; | ||
1644 | r->top = f->offs_v; | ||
1645 | r->width = f->width; | ||
1646 | r->height = f->height; | ||
1647 | } | ||
1648 | |||
1649 | dbg("target %#x: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d", | ||
1650 | sel->pad, r->left, r->top, r->width, r->height, | ||
1651 | f->f_width, f->f_height); | ||
1652 | |||
1653 | mutex_unlock(&fimc->lock); | ||
1654 | return 0; | ||
1655 | } | ||
1656 | |||
1657 | static int fimc_subdev_set_selection(struct v4l2_subdev *sd, | ||
1658 | struct v4l2_subdev_fh *fh, | ||
1659 | struct v4l2_subdev_selection *sel) | ||
1660 | { | ||
1661 | struct fimc_dev *fimc = v4l2_get_subdevdata(sd); | ||
1662 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | ||
1663 | struct fimc_frame *f = &ctx->s_frame; | ||
1664 | struct v4l2_rect *r = &sel->r; | ||
1665 | struct v4l2_rect *try_sel; | ||
1666 | unsigned long flags; | ||
1667 | |||
1668 | if (sel->pad == FIMC_SD_PAD_SOURCE) | ||
1669 | return -EINVAL; | ||
1670 | |||
1671 | mutex_lock(&fimc->lock); | ||
1672 | fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP); | ||
1673 | |||
1674 | switch (sel->target) { | ||
1675 | case V4L2_SEL_TGT_CROP: | ||
1676 | try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); | ||
1677 | break; | ||
1678 | case V4L2_SEL_TGT_COMPOSE: | ||
1679 | try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); | ||
1680 | f = &ctx->d_frame; | ||
1681 | break; | ||
1682 | default: | ||
1683 | mutex_unlock(&fimc->lock); | ||
1684 | return -EINVAL; | ||
1685 | } | ||
1686 | |||
1687 | if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1688 | *try_sel = sel->r; | ||
1689 | } else { | ||
1690 | spin_lock_irqsave(&fimc->slock, flags); | ||
1691 | set_frame_crop(f, r->left, r->top, r->width, r->height); | ||
1692 | set_bit(ST_CAPT_APPLY_CFG, &fimc->state); | ||
1693 | if (sel->target == V4L2_SEL_TGT_COMPOSE) | ||
1694 | ctx->state |= FIMC_COMPOSE; | ||
1695 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
1696 | } | ||
1697 | |||
1698 | dbg("target %#x: (%d,%d)/%dx%d", sel->target, r->left, r->top, | ||
1699 | r->width, r->height); | ||
1700 | |||
1701 | mutex_unlock(&fimc->lock); | ||
1702 | return 0; | ||
1703 | } | ||
1704 | |||
1705 | static struct v4l2_subdev_pad_ops fimc_subdev_pad_ops = { | ||
1706 | .enum_mbus_code = fimc_subdev_enum_mbus_code, | ||
1707 | .get_selection = fimc_subdev_get_selection, | ||
1708 | .set_selection = fimc_subdev_set_selection, | ||
1709 | .get_fmt = fimc_subdev_get_fmt, | ||
1710 | .set_fmt = fimc_subdev_set_fmt, | ||
1711 | }; | ||
1712 | |||
1713 | static struct v4l2_subdev_ops fimc_subdev_ops = { | ||
1714 | .pad = &fimc_subdev_pad_ops, | ||
1715 | }; | ||
1716 | |||
1717 | /* Set default format at the sensor and host interface */ | ||
1718 | static int fimc_capture_set_default_format(struct fimc_dev *fimc) | ||
1719 | { | ||
1720 | struct v4l2_format fmt = { | ||
1721 | .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, | ||
1722 | .fmt.pix_mp = { | ||
1723 | .width = 640, | ||
1724 | .height = 480, | ||
1725 | .pixelformat = V4L2_PIX_FMT_YUYV, | ||
1726 | .field = V4L2_FIELD_NONE, | ||
1727 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
1728 | }, | ||
1729 | }; | ||
1730 | |||
1731 | return __fimc_capture_set_format(fimc, &fmt); | ||
1732 | } | ||
1733 | |||
1734 | /* fimc->lock must be already initialized */ | ||
1735 | static int fimc_register_capture_device(struct fimc_dev *fimc, | ||
1736 | struct v4l2_device *v4l2_dev) | ||
1737 | { | ||
1738 | struct video_device *vfd = &fimc->vid_cap.vfd; | ||
1739 | struct vb2_queue *q = &fimc->vid_cap.vbq; | ||
1740 | struct fimc_ctx *ctx; | ||
1741 | struct fimc_vid_cap *vid_cap; | ||
1742 | int ret = -ENOMEM; | ||
1743 | |||
1744 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | ||
1745 | if (!ctx) | ||
1746 | return -ENOMEM; | ||
1747 | |||
1748 | ctx->fimc_dev = fimc; | ||
1749 | ctx->in_path = FIMC_IO_CAMERA; | ||
1750 | ctx->out_path = FIMC_IO_DMA; | ||
1751 | ctx->state = FIMC_CTX_CAP; | ||
1752 | ctx->s_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0); | ||
1753 | ctx->d_frame.fmt = ctx->s_frame.fmt; | ||
1754 | |||
1755 | memset(vfd, 0, sizeof(*vfd)); | ||
1756 | snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.capture", fimc->id); | ||
1757 | |||
1758 | vfd->fops = &fimc_capture_fops; | ||
1759 | vfd->ioctl_ops = &fimc_capture_ioctl_ops; | ||
1760 | vfd->v4l2_dev = v4l2_dev; | ||
1761 | vfd->minor = -1; | ||
1762 | vfd->release = video_device_release_empty; | ||
1763 | vfd->queue = q; | ||
1764 | vfd->lock = &fimc->lock; | ||
1765 | |||
1766 | video_set_drvdata(vfd, fimc); | ||
1767 | vid_cap = &fimc->vid_cap; | ||
1768 | vid_cap->active_buf_cnt = 0; | ||
1769 | vid_cap->reqbufs_count = 0; | ||
1770 | vid_cap->ctx = ctx; | ||
1771 | |||
1772 | INIT_LIST_HEAD(&vid_cap->pending_buf_q); | ||
1773 | INIT_LIST_HEAD(&vid_cap->active_buf_q); | ||
1774 | |||
1775 | memset(q, 0, sizeof(*q)); | ||
1776 | q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | ||
1777 | q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; | ||
1778 | q->drv_priv = ctx; | ||
1779 | q->ops = &fimc_capture_qops; | ||
1780 | q->mem_ops = &vb2_dma_contig_memops; | ||
1781 | q->buf_struct_size = sizeof(struct fimc_vid_buffer); | ||
1782 | q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; | ||
1783 | q->lock = &fimc->lock; | ||
1784 | |||
1785 | ret = vb2_queue_init(q); | ||
1786 | if (ret) | ||
1787 | goto err_ent; | ||
1788 | |||
1789 | vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK; | ||
1790 | ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0); | ||
1791 | if (ret) | ||
1792 | goto err_ent; | ||
1793 | /* | ||
1794 | * For proper order of acquiring/releasing the video | ||
1795 | * and the graph mutex. | ||
1796 | */ | ||
1797 | v4l2_disable_ioctl_locking(vfd, VIDIOC_TRY_FMT); | ||
1798 | v4l2_disable_ioctl_locking(vfd, VIDIOC_S_FMT); | ||
1799 | |||
1800 | ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); | ||
1801 | if (ret) | ||
1802 | goto err_vd; | ||
1803 | |||
1804 | v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n", | ||
1805 | vfd->name, video_device_node_name(vfd)); | ||
1806 | |||
1807 | vfd->ctrl_handler = &ctx->ctrls.handler; | ||
1808 | return 0; | ||
1809 | |||
1810 | err_vd: | ||
1811 | media_entity_cleanup(&vfd->entity); | ||
1812 | err_ent: | ||
1813 | kfree(ctx); | ||
1814 | return ret; | ||
1815 | } | ||
1816 | |||
1817 | static int fimc_capture_subdev_registered(struct v4l2_subdev *sd) | ||
1818 | { | ||
1819 | struct fimc_dev *fimc = v4l2_get_subdevdata(sd); | ||
1820 | int ret; | ||
1821 | |||
1822 | if (fimc == NULL) | ||
1823 | return -ENXIO; | ||
1824 | |||
1825 | ret = fimc_register_m2m_device(fimc, sd->v4l2_dev); | ||
1826 | if (ret) | ||
1827 | return ret; | ||
1828 | |||
1829 | fimc->pipeline_ops = v4l2_get_subdev_hostdata(sd); | ||
1830 | |||
1831 | ret = fimc_register_capture_device(fimc, sd->v4l2_dev); | ||
1832 | if (ret) { | ||
1833 | fimc_unregister_m2m_device(fimc); | ||
1834 | fimc->pipeline_ops = NULL; | ||
1835 | } | ||
1836 | |||
1837 | return ret; | ||
1838 | } | ||
1839 | |||
1840 | static void fimc_capture_subdev_unregistered(struct v4l2_subdev *sd) | ||
1841 | { | ||
1842 | struct fimc_dev *fimc = v4l2_get_subdevdata(sd); | ||
1843 | |||
1844 | if (fimc == NULL) | ||
1845 | return; | ||
1846 | |||
1847 | fimc_unregister_m2m_device(fimc); | ||
1848 | |||
1849 | if (video_is_registered(&fimc->vid_cap.vfd)) { | ||
1850 | video_unregister_device(&fimc->vid_cap.vfd); | ||
1851 | media_entity_cleanup(&fimc->vid_cap.vfd.entity); | ||
1852 | fimc->pipeline_ops = NULL; | ||
1853 | } | ||
1854 | kfree(fimc->vid_cap.ctx); | ||
1855 | fimc->vid_cap.ctx = NULL; | ||
1856 | } | ||
1857 | |||
1858 | static const struct v4l2_subdev_internal_ops fimc_capture_sd_internal_ops = { | ||
1859 | .registered = fimc_capture_subdev_registered, | ||
1860 | .unregistered = fimc_capture_subdev_unregistered, | ||
1861 | }; | ||
1862 | |||
1863 | int fimc_initialize_capture_subdev(struct fimc_dev *fimc) | ||
1864 | { | ||
1865 | struct v4l2_subdev *sd = &fimc->vid_cap.subdev; | ||
1866 | int ret; | ||
1867 | |||
1868 | v4l2_subdev_init(sd, &fimc_subdev_ops); | ||
1869 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1870 | snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->id); | ||
1871 | |||
1872 | fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_CAM].flags = MEDIA_PAD_FL_SINK; | ||
1873 | fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_FIFO].flags = MEDIA_PAD_FL_SINK; | ||
1874 | fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; | ||
1875 | ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, | ||
1876 | fimc->vid_cap.sd_pads, 0); | ||
1877 | if (ret) | ||
1878 | return ret; | ||
1879 | |||
1880 | sd->entity.ops = &fimc_sd_media_ops; | ||
1881 | sd->internal_ops = &fimc_capture_sd_internal_ops; | ||
1882 | v4l2_set_subdevdata(sd, fimc); | ||
1883 | return 0; | ||
1884 | } | ||
1885 | |||
1886 | void fimc_unregister_capture_subdev(struct fimc_dev *fimc) | ||
1887 | { | ||
1888 | struct v4l2_subdev *sd = &fimc->vid_cap.subdev; | ||
1889 | |||
1890 | v4l2_device_unregister_subdev(sd); | ||
1891 | media_entity_cleanup(&sd->entity); | ||
1892 | v4l2_set_subdevdata(sd, NULL); | ||
1893 | } | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c new file mode 100644 index 000000000000..379a5e9d52a7 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-core.c | |||
@@ -0,0 +1,1311 @@ | |||
1 | /* | ||
2 | * Samsung S5P/EXYNOS4 SoC series FIMC (CAMIF) driver | ||
3 | * | ||
4 | * Copyright (C) 2010-2012 Samsung Electronics Co., Ltd. | ||
5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published | ||
9 | * by the Free Software Foundation, either version 2 of the License, | ||
10 | * or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/bug.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/device.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/pm_runtime.h> | ||
22 | #include <linux/list.h> | ||
23 | #include <linux/mfd/syscon.h> | ||
24 | #include <linux/io.h> | ||
25 | #include <linux/of.h> | ||
26 | #include <linux/of_device.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/clk.h> | ||
29 | #include <media/v4l2-ioctl.h> | ||
30 | #include <media/videobuf2-core.h> | ||
31 | #include <media/videobuf2-dma-contig.h> | ||
32 | |||
33 | #include "fimc-core.h" | ||
34 | #include "fimc-reg.h" | ||
35 | #include "media-dev.h" | ||
36 | |||
37 | static char *fimc_clocks[MAX_FIMC_CLOCKS] = { | ||
38 | "sclk_fimc", "fimc" | ||
39 | }; | ||
40 | |||
41 | static struct fimc_fmt fimc_formats[] = { | ||
42 | { | ||
43 | .name = "RGB565", | ||
44 | .fourcc = V4L2_PIX_FMT_RGB565, | ||
45 | .depth = { 16 }, | ||
46 | .color = FIMC_FMT_RGB565, | ||
47 | .memplanes = 1, | ||
48 | .colplanes = 1, | ||
49 | .flags = FMT_FLAGS_M2M, | ||
50 | }, { | ||
51 | .name = "BGR666", | ||
52 | .fourcc = V4L2_PIX_FMT_BGR666, | ||
53 | .depth = { 32 }, | ||
54 | .color = FIMC_FMT_RGB666, | ||
55 | .memplanes = 1, | ||
56 | .colplanes = 1, | ||
57 | .flags = FMT_FLAGS_M2M, | ||
58 | }, { | ||
59 | .name = "ARGB8888, 32 bpp", | ||
60 | .fourcc = V4L2_PIX_FMT_RGB32, | ||
61 | .depth = { 32 }, | ||
62 | .color = FIMC_FMT_RGB888, | ||
63 | .memplanes = 1, | ||
64 | .colplanes = 1, | ||
65 | .flags = FMT_FLAGS_M2M | FMT_HAS_ALPHA, | ||
66 | }, { | ||
67 | .name = "ARGB1555", | ||
68 | .fourcc = V4L2_PIX_FMT_RGB555, | ||
69 | .depth = { 16 }, | ||
70 | .color = FIMC_FMT_RGB555, | ||
71 | .memplanes = 1, | ||
72 | .colplanes = 1, | ||
73 | .flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA, | ||
74 | }, { | ||
75 | .name = "ARGB4444", | ||
76 | .fourcc = V4L2_PIX_FMT_RGB444, | ||
77 | .depth = { 16 }, | ||
78 | .color = FIMC_FMT_RGB444, | ||
79 | .memplanes = 1, | ||
80 | .colplanes = 1, | ||
81 | .flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA, | ||
82 | }, { | ||
83 | .name = "YUV 4:4:4", | ||
84 | .mbus_code = V4L2_MBUS_FMT_YUV10_1X30, | ||
85 | .flags = FMT_FLAGS_WRITEBACK, | ||
86 | }, { | ||
87 | .name = "YUV 4:2:2 packed, YCbYCr", | ||
88 | .fourcc = V4L2_PIX_FMT_YUYV, | ||
89 | .depth = { 16 }, | ||
90 | .color = FIMC_FMT_YCBYCR422, | ||
91 | .memplanes = 1, | ||
92 | .colplanes = 1, | ||
93 | .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, | ||
94 | .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, | ||
95 | }, { | ||
96 | .name = "YUV 4:2:2 packed, CbYCrY", | ||
97 | .fourcc = V4L2_PIX_FMT_UYVY, | ||
98 | .depth = { 16 }, | ||
99 | .color = FIMC_FMT_CBYCRY422, | ||
100 | .memplanes = 1, | ||
101 | .colplanes = 1, | ||
102 | .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, | ||
103 | .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, | ||
104 | }, { | ||
105 | .name = "YUV 4:2:2 packed, CrYCbY", | ||
106 | .fourcc = V4L2_PIX_FMT_VYUY, | ||
107 | .depth = { 16 }, | ||
108 | .color = FIMC_FMT_CRYCBY422, | ||
109 | .memplanes = 1, | ||
110 | .colplanes = 1, | ||
111 | .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8, | ||
112 | .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, | ||
113 | }, { | ||
114 | .name = "YUV 4:2:2 packed, YCrYCb", | ||
115 | .fourcc = V4L2_PIX_FMT_YVYU, | ||
116 | .depth = { 16 }, | ||
117 | .color = FIMC_FMT_YCRYCB422, | ||
118 | .memplanes = 1, | ||
119 | .colplanes = 1, | ||
120 | .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8, | ||
121 | .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, | ||
122 | }, { | ||
123 | .name = "YUV 4:2:2 planar, Y/Cb/Cr", | ||
124 | .fourcc = V4L2_PIX_FMT_YUV422P, | ||
125 | .depth = { 12 }, | ||
126 | .color = FIMC_FMT_YCBYCR422, | ||
127 | .memplanes = 1, | ||
128 | .colplanes = 3, | ||
129 | .flags = FMT_FLAGS_M2M, | ||
130 | }, { | ||
131 | .name = "YUV 4:2:2 planar, Y/CbCr", | ||
132 | .fourcc = V4L2_PIX_FMT_NV16, | ||
133 | .depth = { 16 }, | ||
134 | .color = FIMC_FMT_YCBYCR422, | ||
135 | .memplanes = 1, | ||
136 | .colplanes = 2, | ||
137 | .flags = FMT_FLAGS_M2M, | ||
138 | }, { | ||
139 | .name = "YUV 4:2:2 planar, Y/CrCb", | ||
140 | .fourcc = V4L2_PIX_FMT_NV61, | ||
141 | .depth = { 16 }, | ||
142 | .color = FIMC_FMT_YCRYCB422, | ||
143 | .memplanes = 1, | ||
144 | .colplanes = 2, | ||
145 | .flags = FMT_FLAGS_M2M, | ||
146 | }, { | ||
147 | .name = "YUV 4:2:0 planar, YCbCr", | ||
148 | .fourcc = V4L2_PIX_FMT_YUV420, | ||
149 | .depth = { 12 }, | ||
150 | .color = FIMC_FMT_YCBCR420, | ||
151 | .memplanes = 1, | ||
152 | .colplanes = 3, | ||
153 | .flags = FMT_FLAGS_M2M, | ||
154 | }, { | ||
155 | .name = "YUV 4:2:0 planar, Y/CbCr", | ||
156 | .fourcc = V4L2_PIX_FMT_NV12, | ||
157 | .depth = { 12 }, | ||
158 | .color = FIMC_FMT_YCBCR420, | ||
159 | .memplanes = 1, | ||
160 | .colplanes = 2, | ||
161 | .flags = FMT_FLAGS_M2M, | ||
162 | }, { | ||
163 | .name = "YUV 4:2:0 non-contig. 2p, Y/CbCr", | ||
164 | .fourcc = V4L2_PIX_FMT_NV12M, | ||
165 | .color = FIMC_FMT_YCBCR420, | ||
166 | .depth = { 8, 4 }, | ||
167 | .memplanes = 2, | ||
168 | .colplanes = 2, | ||
169 | .flags = FMT_FLAGS_M2M, | ||
170 | }, { | ||
171 | .name = "YUV 4:2:0 non-contig. 3p, Y/Cb/Cr", | ||
172 | .fourcc = V4L2_PIX_FMT_YUV420M, | ||
173 | .color = FIMC_FMT_YCBCR420, | ||
174 | .depth = { 8, 2, 2 }, | ||
175 | .memplanes = 3, | ||
176 | .colplanes = 3, | ||
177 | .flags = FMT_FLAGS_M2M, | ||
178 | }, { | ||
179 | .name = "YUV 4:2:0 non-contig. 2p, tiled", | ||
180 | .fourcc = V4L2_PIX_FMT_NV12MT, | ||
181 | .color = FIMC_FMT_YCBCR420, | ||
182 | .depth = { 8, 4 }, | ||
183 | .memplanes = 2, | ||
184 | .colplanes = 2, | ||
185 | .flags = FMT_FLAGS_M2M, | ||
186 | }, { | ||
187 | .name = "JPEG encoded data", | ||
188 | .fourcc = V4L2_PIX_FMT_JPEG, | ||
189 | .color = FIMC_FMT_JPEG, | ||
190 | .depth = { 8 }, | ||
191 | .memplanes = 1, | ||
192 | .colplanes = 1, | ||
193 | .mbus_code = V4L2_MBUS_FMT_JPEG_1X8, | ||
194 | .flags = FMT_FLAGS_CAM | FMT_FLAGS_COMPRESSED, | ||
195 | }, { | ||
196 | .name = "S5C73MX interleaved UYVY/JPEG", | ||
197 | .fourcc = V4L2_PIX_FMT_S5C_UYVY_JPG, | ||
198 | .color = FIMC_FMT_YUYV_JPEG, | ||
199 | .depth = { 8 }, | ||
200 | .memplanes = 2, | ||
201 | .colplanes = 1, | ||
202 | .mdataplanes = 0x2, /* plane 1 holds frame meta data */ | ||
203 | .mbus_code = V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8, | ||
204 | .flags = FMT_FLAGS_CAM | FMT_FLAGS_COMPRESSED, | ||
205 | }, | ||
206 | }; | ||
207 | |||
208 | struct fimc_fmt *fimc_get_format(unsigned int index) | ||
209 | { | ||
210 | if (index >= ARRAY_SIZE(fimc_formats)) | ||
211 | return NULL; | ||
212 | |||
213 | return &fimc_formats[index]; | ||
214 | } | ||
215 | |||
216 | void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, | ||
217 | unsigned int caps) | ||
218 | { | ||
219 | strlcpy(cap->driver, dev->driver->name, sizeof(cap->driver)); | ||
220 | strlcpy(cap->card, dev->driver->name, sizeof(cap->card)); | ||
221 | snprintf(cap->bus_info, sizeof(cap->bus_info), | ||
222 | "platform:%s", dev_name(dev)); | ||
223 | cap->device_caps = caps; | ||
224 | cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; | ||
225 | } | ||
226 | |||
227 | int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, | ||
228 | int dw, int dh, int rotation) | ||
229 | { | ||
230 | if (rotation == 90 || rotation == 270) | ||
231 | swap(dw, dh); | ||
232 | |||
233 | if (!ctx->scaler.enabled) | ||
234 | return (sw == dw && sh == dh) ? 0 : -EINVAL; | ||
235 | |||
236 | if ((sw >= SCALER_MAX_HRATIO * dw) || (sh >= SCALER_MAX_VRATIO * dh)) | ||
237 | return -EINVAL; | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift) | ||
243 | { | ||
244 | u32 sh = 6; | ||
245 | |||
246 | if (src >= 64 * tar) | ||
247 | return -EINVAL; | ||
248 | |||
249 | while (sh--) { | ||
250 | u32 tmp = 1 << sh; | ||
251 | if (src >= tar * tmp) { | ||
252 | *shift = sh, *ratio = tmp; | ||
253 | return 0; | ||
254 | } | ||
255 | } | ||
256 | *shift = 0, *ratio = 1; | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | int fimc_set_scaler_info(struct fimc_ctx *ctx) | ||
261 | { | ||
262 | const struct fimc_variant *variant = ctx->fimc_dev->variant; | ||
263 | struct device *dev = &ctx->fimc_dev->pdev->dev; | ||
264 | struct fimc_scaler *sc = &ctx->scaler; | ||
265 | struct fimc_frame *s_frame = &ctx->s_frame; | ||
266 | struct fimc_frame *d_frame = &ctx->d_frame; | ||
267 | int tx, ty, sx, sy; | ||
268 | int ret; | ||
269 | |||
270 | if (ctx->rotation == 90 || ctx->rotation == 270) { | ||
271 | ty = d_frame->width; | ||
272 | tx = d_frame->height; | ||
273 | } else { | ||
274 | tx = d_frame->width; | ||
275 | ty = d_frame->height; | ||
276 | } | ||
277 | if (tx <= 0 || ty <= 0) { | ||
278 | dev_err(dev, "Invalid target size: %dx%d\n", tx, ty); | ||
279 | return -EINVAL; | ||
280 | } | ||
281 | |||
282 | sx = s_frame->width; | ||
283 | sy = s_frame->height; | ||
284 | if (sx <= 0 || sy <= 0) { | ||
285 | dev_err(dev, "Invalid source size: %dx%d\n", sx, sy); | ||
286 | return -EINVAL; | ||
287 | } | ||
288 | sc->real_width = sx; | ||
289 | sc->real_height = sy; | ||
290 | |||
291 | ret = fimc_get_scaler_factor(sx, tx, &sc->pre_hratio, &sc->hfactor); | ||
292 | if (ret) | ||
293 | return ret; | ||
294 | |||
295 | ret = fimc_get_scaler_factor(sy, ty, &sc->pre_vratio, &sc->vfactor); | ||
296 | if (ret) | ||
297 | return ret; | ||
298 | |||
299 | sc->pre_dst_width = sx / sc->pre_hratio; | ||
300 | sc->pre_dst_height = sy / sc->pre_vratio; | ||
301 | |||
302 | if (variant->has_mainscaler_ext) { | ||
303 | sc->main_hratio = (sx << 14) / (tx << sc->hfactor); | ||
304 | sc->main_vratio = (sy << 14) / (ty << sc->vfactor); | ||
305 | } else { | ||
306 | sc->main_hratio = (sx << 8) / (tx << sc->hfactor); | ||
307 | sc->main_vratio = (sy << 8) / (ty << sc->vfactor); | ||
308 | |||
309 | } | ||
310 | |||
311 | sc->scaleup_h = (tx >= sx) ? 1 : 0; | ||
312 | sc->scaleup_v = (ty >= sy) ? 1 : 0; | ||
313 | |||
314 | /* check to see if input and output size/format differ */ | ||
315 | if (s_frame->fmt->color == d_frame->fmt->color | ||
316 | && s_frame->width == d_frame->width | ||
317 | && s_frame->height == d_frame->height) | ||
318 | sc->copy_mode = 1; | ||
319 | else | ||
320 | sc->copy_mode = 0; | ||
321 | |||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | static irqreturn_t fimc_irq_handler(int irq, void *priv) | ||
326 | { | ||
327 | struct fimc_dev *fimc = priv; | ||
328 | struct fimc_ctx *ctx; | ||
329 | |||
330 | fimc_hw_clear_irq(fimc); | ||
331 | |||
332 | spin_lock(&fimc->slock); | ||
333 | |||
334 | if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) { | ||
335 | if (test_and_clear_bit(ST_M2M_SUSPENDING, &fimc->state)) { | ||
336 | set_bit(ST_M2M_SUSPENDED, &fimc->state); | ||
337 | wake_up(&fimc->irq_queue); | ||
338 | goto out; | ||
339 | } | ||
340 | ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev); | ||
341 | if (ctx != NULL) { | ||
342 | spin_unlock(&fimc->slock); | ||
343 | fimc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE); | ||
344 | |||
345 | if (ctx->state & FIMC_CTX_SHUT) { | ||
346 | ctx->state &= ~FIMC_CTX_SHUT; | ||
347 | wake_up(&fimc->irq_queue); | ||
348 | } | ||
349 | return IRQ_HANDLED; | ||
350 | } | ||
351 | } else if (test_bit(ST_CAPT_PEND, &fimc->state)) { | ||
352 | int last_buf = test_bit(ST_CAPT_JPEG, &fimc->state) && | ||
353 | fimc->vid_cap.reqbufs_count == 1; | ||
354 | fimc_capture_irq_handler(fimc, !last_buf); | ||
355 | } | ||
356 | out: | ||
357 | spin_unlock(&fimc->slock); | ||
358 | return IRQ_HANDLED; | ||
359 | } | ||
360 | |||
361 | /* The color format (colplanes, memplanes) must be already configured. */ | ||
362 | int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, | ||
363 | struct fimc_frame *frame, struct fimc_addr *paddr) | ||
364 | { | ||
365 | int ret = 0; | ||
366 | u32 pix_size; | ||
367 | |||
368 | if (vb == NULL || frame == NULL) | ||
369 | return -EINVAL; | ||
370 | |||
371 | pix_size = frame->width * frame->height; | ||
372 | |||
373 | dbg("memplanes= %d, colplanes= %d, pix_size= %d", | ||
374 | frame->fmt->memplanes, frame->fmt->colplanes, pix_size); | ||
375 | |||
376 | paddr->y = vb2_dma_contig_plane_dma_addr(vb, 0); | ||
377 | |||
378 | if (frame->fmt->memplanes == 1) { | ||
379 | switch (frame->fmt->colplanes) { | ||
380 | case 1: | ||
381 | paddr->cb = 0; | ||
382 | paddr->cr = 0; | ||
383 | break; | ||
384 | case 2: | ||
385 | /* decompose Y into Y/Cb */ | ||
386 | paddr->cb = (u32)(paddr->y + pix_size); | ||
387 | paddr->cr = 0; | ||
388 | break; | ||
389 | case 3: | ||
390 | paddr->cb = (u32)(paddr->y + pix_size); | ||
391 | /* decompose Y into Y/Cb/Cr */ | ||
392 | if (FIMC_FMT_YCBCR420 == frame->fmt->color) | ||
393 | paddr->cr = (u32)(paddr->cb | ||
394 | + (pix_size >> 2)); | ||
395 | else /* 422 */ | ||
396 | paddr->cr = (u32)(paddr->cb | ||
397 | + (pix_size >> 1)); | ||
398 | break; | ||
399 | default: | ||
400 | return -EINVAL; | ||
401 | } | ||
402 | } else if (!frame->fmt->mdataplanes) { | ||
403 | if (frame->fmt->memplanes >= 2) | ||
404 | paddr->cb = vb2_dma_contig_plane_dma_addr(vb, 1); | ||
405 | |||
406 | if (frame->fmt->memplanes == 3) | ||
407 | paddr->cr = vb2_dma_contig_plane_dma_addr(vb, 2); | ||
408 | } | ||
409 | |||
410 | dbg("PHYS_ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d", | ||
411 | paddr->y, paddr->cb, paddr->cr, ret); | ||
412 | |||
413 | return ret; | ||
414 | } | ||
415 | |||
416 | /* Set order for 1 and 2 plane YCBCR 4:2:2 formats. */ | ||
417 | void fimc_set_yuv_order(struct fimc_ctx *ctx) | ||
418 | { | ||
419 | /* The one only mode supported in SoC. */ | ||
420 | ctx->in_order_2p = FIMC_REG_CIOCTRL_ORDER422_2P_LSB_CRCB; | ||
421 | ctx->out_order_2p = FIMC_REG_CIOCTRL_ORDER422_2P_LSB_CRCB; | ||
422 | |||
423 | /* Set order for 1 plane input formats. */ | ||
424 | switch (ctx->s_frame.fmt->color) { | ||
425 | case FIMC_FMT_YCRYCB422: | ||
426 | ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCRYCB; | ||
427 | break; | ||
428 | case FIMC_FMT_CBYCRY422: | ||
429 | ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CBYCRY; | ||
430 | break; | ||
431 | case FIMC_FMT_CRYCBY422: | ||
432 | ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CRYCBY; | ||
433 | break; | ||
434 | case FIMC_FMT_YCBYCR422: | ||
435 | default: | ||
436 | ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCBYCR; | ||
437 | break; | ||
438 | } | ||
439 | dbg("ctx->in_order_1p= %d", ctx->in_order_1p); | ||
440 | |||
441 | switch (ctx->d_frame.fmt->color) { | ||
442 | case FIMC_FMT_YCRYCB422: | ||
443 | ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCRYCB; | ||
444 | break; | ||
445 | case FIMC_FMT_CBYCRY422: | ||
446 | ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CBYCRY; | ||
447 | break; | ||
448 | case FIMC_FMT_CRYCBY422: | ||
449 | ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CRYCBY; | ||
450 | break; | ||
451 | case FIMC_FMT_YCBYCR422: | ||
452 | default: | ||
453 | ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCBYCR; | ||
454 | break; | ||
455 | } | ||
456 | dbg("ctx->out_order_1p= %d", ctx->out_order_1p); | ||
457 | } | ||
458 | |||
459 | void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) | ||
460 | { | ||
461 | bool pix_hoff = ctx->fimc_dev->drv_data->dma_pix_hoff; | ||
462 | u32 i, depth = 0; | ||
463 | |||
464 | for (i = 0; i < f->fmt->colplanes; i++) | ||
465 | depth += f->fmt->depth[i]; | ||
466 | |||
467 | f->dma_offset.y_h = f->offs_h; | ||
468 | if (!pix_hoff) | ||
469 | f->dma_offset.y_h *= (depth >> 3); | ||
470 | |||
471 | f->dma_offset.y_v = f->offs_v; | ||
472 | |||
473 | f->dma_offset.cb_h = f->offs_h; | ||
474 | f->dma_offset.cb_v = f->offs_v; | ||
475 | |||
476 | f->dma_offset.cr_h = f->offs_h; | ||
477 | f->dma_offset.cr_v = f->offs_v; | ||
478 | |||
479 | if (!pix_hoff) { | ||
480 | if (f->fmt->colplanes == 3) { | ||
481 | f->dma_offset.cb_h >>= 1; | ||
482 | f->dma_offset.cr_h >>= 1; | ||
483 | } | ||
484 | if (f->fmt->color == FIMC_FMT_YCBCR420) { | ||
485 | f->dma_offset.cb_v >>= 1; | ||
486 | f->dma_offset.cr_v >>= 1; | ||
487 | } | ||
488 | } | ||
489 | |||
490 | dbg("in_offset: color= %d, y_h= %d, y_v= %d", | ||
491 | f->fmt->color, f->dma_offset.y_h, f->dma_offset.y_v); | ||
492 | } | ||
493 | |||
494 | static int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx) | ||
495 | { | ||
496 | struct fimc_effect *effect = &ctx->effect; | ||
497 | |||
498 | switch (colorfx) { | ||
499 | case V4L2_COLORFX_NONE: | ||
500 | effect->type = FIMC_REG_CIIMGEFF_FIN_BYPASS; | ||
501 | break; | ||
502 | case V4L2_COLORFX_BW: | ||
503 | effect->type = FIMC_REG_CIIMGEFF_FIN_ARBITRARY; | ||
504 | effect->pat_cb = 128; | ||
505 | effect->pat_cr = 128; | ||
506 | break; | ||
507 | case V4L2_COLORFX_SEPIA: | ||
508 | effect->type = FIMC_REG_CIIMGEFF_FIN_ARBITRARY; | ||
509 | effect->pat_cb = 115; | ||
510 | effect->pat_cr = 145; | ||
511 | break; | ||
512 | case V4L2_COLORFX_NEGATIVE: | ||
513 | effect->type = FIMC_REG_CIIMGEFF_FIN_NEGATIVE; | ||
514 | break; | ||
515 | case V4L2_COLORFX_EMBOSS: | ||
516 | effect->type = FIMC_REG_CIIMGEFF_FIN_EMBOSSING; | ||
517 | break; | ||
518 | case V4L2_COLORFX_ART_FREEZE: | ||
519 | effect->type = FIMC_REG_CIIMGEFF_FIN_ARTFREEZE; | ||
520 | break; | ||
521 | case V4L2_COLORFX_SILHOUETTE: | ||
522 | effect->type = FIMC_REG_CIIMGEFF_FIN_SILHOUETTE; | ||
523 | break; | ||
524 | case V4L2_COLORFX_SET_CBCR: | ||
525 | effect->type = FIMC_REG_CIIMGEFF_FIN_ARBITRARY; | ||
526 | effect->pat_cb = ctx->ctrls.colorfx_cbcr->val >> 8; | ||
527 | effect->pat_cr = ctx->ctrls.colorfx_cbcr->val & 0xff; | ||
528 | break; | ||
529 | default: | ||
530 | return -EINVAL; | ||
531 | } | ||
532 | |||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | /* | ||
537 | * V4L2 controls handling | ||
538 | */ | ||
539 | #define ctrl_to_ctx(__ctrl) \ | ||
540 | container_of((__ctrl)->handler, struct fimc_ctx, ctrls.handler) | ||
541 | |||
542 | static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl) | ||
543 | { | ||
544 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
545 | const struct fimc_variant *variant = fimc->variant; | ||
546 | int ret = 0; | ||
547 | |||
548 | if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) | ||
549 | return 0; | ||
550 | |||
551 | switch (ctrl->id) { | ||
552 | case V4L2_CID_HFLIP: | ||
553 | ctx->hflip = ctrl->val; | ||
554 | break; | ||
555 | |||
556 | case V4L2_CID_VFLIP: | ||
557 | ctx->vflip = ctrl->val; | ||
558 | break; | ||
559 | |||
560 | case V4L2_CID_ROTATE: | ||
561 | if (fimc_capture_pending(fimc)) { | ||
562 | ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, | ||
563 | ctx->s_frame.height, ctx->d_frame.width, | ||
564 | ctx->d_frame.height, ctrl->val); | ||
565 | if (ret) | ||
566 | return -EINVAL; | ||
567 | } | ||
568 | if ((ctrl->val == 90 || ctrl->val == 270) && | ||
569 | !variant->has_out_rot) | ||
570 | return -EINVAL; | ||
571 | |||
572 | ctx->rotation = ctrl->val; | ||
573 | break; | ||
574 | |||
575 | case V4L2_CID_ALPHA_COMPONENT: | ||
576 | ctx->d_frame.alpha = ctrl->val; | ||
577 | break; | ||
578 | |||
579 | case V4L2_CID_COLORFX: | ||
580 | ret = fimc_set_color_effect(ctx, ctrl->val); | ||
581 | if (ret) | ||
582 | return ret; | ||
583 | break; | ||
584 | } | ||
585 | |||
586 | ctx->state |= FIMC_PARAMS; | ||
587 | set_bit(ST_CAPT_APPLY_CFG, &fimc->state); | ||
588 | return 0; | ||
589 | } | ||
590 | |||
591 | static int fimc_s_ctrl(struct v4l2_ctrl *ctrl) | ||
592 | { | ||
593 | struct fimc_ctx *ctx = ctrl_to_ctx(ctrl); | ||
594 | unsigned long flags; | ||
595 | int ret; | ||
596 | |||
597 | spin_lock_irqsave(&ctx->fimc_dev->slock, flags); | ||
598 | ret = __fimc_s_ctrl(ctx, ctrl); | ||
599 | spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags); | ||
600 | |||
601 | return ret; | ||
602 | } | ||
603 | |||
604 | static const struct v4l2_ctrl_ops fimc_ctrl_ops = { | ||
605 | .s_ctrl = fimc_s_ctrl, | ||
606 | }; | ||
607 | |||
608 | int fimc_ctrls_create(struct fimc_ctx *ctx) | ||
609 | { | ||
610 | unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt); | ||
611 | struct fimc_ctrls *ctrls = &ctx->ctrls; | ||
612 | struct v4l2_ctrl_handler *handler = &ctrls->handler; | ||
613 | |||
614 | if (ctx->ctrls.ready) | ||
615 | return 0; | ||
616 | |||
617 | v4l2_ctrl_handler_init(handler, 6); | ||
618 | |||
619 | ctrls->rotate = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, | ||
620 | V4L2_CID_ROTATE, 0, 270, 90, 0); | ||
621 | ctrls->hflip = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, | ||
622 | V4L2_CID_HFLIP, 0, 1, 1, 0); | ||
623 | ctrls->vflip = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, | ||
624 | V4L2_CID_VFLIP, 0, 1, 1, 0); | ||
625 | |||
626 | if (ctx->fimc_dev->drv_data->alpha_color) | ||
627 | ctrls->alpha = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, | ||
628 | V4L2_CID_ALPHA_COMPONENT, | ||
629 | 0, max_alpha, 1, 0); | ||
630 | else | ||
631 | ctrls->alpha = NULL; | ||
632 | |||
633 | ctrls->colorfx = v4l2_ctrl_new_std_menu(handler, &fimc_ctrl_ops, | ||
634 | V4L2_CID_COLORFX, V4L2_COLORFX_SET_CBCR, | ||
635 | ~0x983f, V4L2_COLORFX_NONE); | ||
636 | |||
637 | ctrls->colorfx_cbcr = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, | ||
638 | V4L2_CID_COLORFX_CBCR, 0, 0xffff, 1, 0); | ||
639 | |||
640 | ctx->effect.type = FIMC_REG_CIIMGEFF_FIN_BYPASS; | ||
641 | |||
642 | if (!handler->error) { | ||
643 | v4l2_ctrl_cluster(2, &ctrls->colorfx); | ||
644 | ctrls->ready = true; | ||
645 | } | ||
646 | |||
647 | return handler->error; | ||
648 | } | ||
649 | |||
650 | void fimc_ctrls_delete(struct fimc_ctx *ctx) | ||
651 | { | ||
652 | struct fimc_ctrls *ctrls = &ctx->ctrls; | ||
653 | |||
654 | if (ctrls->ready) { | ||
655 | v4l2_ctrl_handler_free(&ctrls->handler); | ||
656 | ctrls->ready = false; | ||
657 | ctrls->alpha = NULL; | ||
658 | } | ||
659 | } | ||
660 | |||
661 | void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active) | ||
662 | { | ||
663 | unsigned int has_alpha = ctx->d_frame.fmt->flags & FMT_HAS_ALPHA; | ||
664 | struct fimc_ctrls *ctrls = &ctx->ctrls; | ||
665 | |||
666 | if (!ctrls->ready) | ||
667 | return; | ||
668 | |||
669 | mutex_lock(ctrls->handler.lock); | ||
670 | v4l2_ctrl_activate(ctrls->rotate, active); | ||
671 | v4l2_ctrl_activate(ctrls->hflip, active); | ||
672 | v4l2_ctrl_activate(ctrls->vflip, active); | ||
673 | v4l2_ctrl_activate(ctrls->colorfx, active); | ||
674 | if (ctrls->alpha) | ||
675 | v4l2_ctrl_activate(ctrls->alpha, active && has_alpha); | ||
676 | |||
677 | if (active) { | ||
678 | fimc_set_color_effect(ctx, ctrls->colorfx->cur.val); | ||
679 | ctx->rotation = ctrls->rotate->val; | ||
680 | ctx->hflip = ctrls->hflip->val; | ||
681 | ctx->vflip = ctrls->vflip->val; | ||
682 | } else { | ||
683 | ctx->effect.type = FIMC_REG_CIIMGEFF_FIN_BYPASS; | ||
684 | ctx->rotation = 0; | ||
685 | ctx->hflip = 0; | ||
686 | ctx->vflip = 0; | ||
687 | } | ||
688 | mutex_unlock(ctrls->handler.lock); | ||
689 | } | ||
690 | |||
691 | /* Update maximum value of the alpha color control */ | ||
692 | void fimc_alpha_ctrl_update(struct fimc_ctx *ctx) | ||
693 | { | ||
694 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
695 | struct v4l2_ctrl *ctrl = ctx->ctrls.alpha; | ||
696 | |||
697 | if (ctrl == NULL || !fimc->drv_data->alpha_color) | ||
698 | return; | ||
699 | |||
700 | v4l2_ctrl_lock(ctrl); | ||
701 | ctrl->maximum = fimc_get_alpha_mask(ctx->d_frame.fmt); | ||
702 | |||
703 | if (ctrl->cur.val > ctrl->maximum) | ||
704 | ctrl->cur.val = ctrl->maximum; | ||
705 | |||
706 | v4l2_ctrl_unlock(ctrl); | ||
707 | } | ||
708 | |||
709 | void __fimc_get_format(struct fimc_frame *frame, struct v4l2_format *f) | ||
710 | { | ||
711 | struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; | ||
712 | int i; | ||
713 | |||
714 | pixm->width = frame->o_width; | ||
715 | pixm->height = frame->o_height; | ||
716 | pixm->field = V4L2_FIELD_NONE; | ||
717 | pixm->pixelformat = frame->fmt->fourcc; | ||
718 | pixm->colorspace = V4L2_COLORSPACE_JPEG; | ||
719 | pixm->num_planes = frame->fmt->memplanes; | ||
720 | |||
721 | for (i = 0; i < pixm->num_planes; ++i) { | ||
722 | pixm->plane_fmt[i].bytesperline = frame->bytesperline[i]; | ||
723 | pixm->plane_fmt[i].sizeimage = frame->payload[i]; | ||
724 | } | ||
725 | } | ||
726 | |||
727 | /** | ||
728 | * fimc_adjust_mplane_format - adjust bytesperline/sizeimage for each plane | ||
729 | * @fmt: fimc pixel format description (input) | ||
730 | * @width: requested pixel width | ||
731 | * @height: requested pixel height | ||
732 | * @pix: multi-plane format to adjust | ||
733 | */ | ||
734 | void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, | ||
735 | struct v4l2_pix_format_mplane *pix) | ||
736 | { | ||
737 | u32 bytesperline = 0; | ||
738 | int i; | ||
739 | |||
740 | pix->colorspace = V4L2_COLORSPACE_JPEG; | ||
741 | pix->field = V4L2_FIELD_NONE; | ||
742 | pix->num_planes = fmt->memplanes; | ||
743 | pix->pixelformat = fmt->fourcc; | ||
744 | pix->height = height; | ||
745 | pix->width = width; | ||
746 | |||
747 | for (i = 0; i < pix->num_planes; ++i) { | ||
748 | struct v4l2_plane_pix_format *plane_fmt = &pix->plane_fmt[i]; | ||
749 | u32 bpl = plane_fmt->bytesperline; | ||
750 | |||
751 | if (fmt->colplanes > 1 && (bpl == 0 || bpl < pix->width)) | ||
752 | bpl = pix->width; /* Planar */ | ||
753 | |||
754 | if (fmt->colplanes == 1 && /* Packed */ | ||
755 | (bpl == 0 || ((bpl * 8) / fmt->depth[i]) < pix->width)) | ||
756 | bpl = (pix->width * fmt->depth[0]) / 8; | ||
757 | /* | ||
758 | * Currently bytesperline for each plane is same, except | ||
759 | * V4L2_PIX_FMT_YUV420M format. This calculation may need | ||
760 | * to be changed when other multi-planar formats are added | ||
761 | * to the fimc_formats[] array. | ||
762 | */ | ||
763 | if (i == 0) | ||
764 | bytesperline = bpl; | ||
765 | else if (i == 1 && fmt->memplanes == 3) | ||
766 | bytesperline /= 2; | ||
767 | |||
768 | plane_fmt->bytesperline = bytesperline; | ||
769 | plane_fmt->sizeimage = max((pix->width * pix->height * | ||
770 | fmt->depth[i]) / 8, plane_fmt->sizeimage); | ||
771 | } | ||
772 | } | ||
773 | |||
774 | /** | ||
775 | * fimc_find_format - lookup fimc color format by fourcc or media bus format | ||
776 | * @pixelformat: fourcc to match, ignored if null | ||
777 | * @mbus_code: media bus code to match, ignored if null | ||
778 | * @mask: the color flags to match | ||
779 | * @index: offset in the fimc_formats array, ignored if negative | ||
780 | */ | ||
781 | struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code, | ||
782 | unsigned int mask, int index) | ||
783 | { | ||
784 | struct fimc_fmt *fmt, *def_fmt = NULL; | ||
785 | unsigned int i; | ||
786 | int id = 0; | ||
787 | |||
788 | if (index >= (int)ARRAY_SIZE(fimc_formats)) | ||
789 | return NULL; | ||
790 | |||
791 | for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) { | ||
792 | fmt = &fimc_formats[i]; | ||
793 | if (!(fmt->flags & mask)) | ||
794 | continue; | ||
795 | if (pixelformat && fmt->fourcc == *pixelformat) | ||
796 | return fmt; | ||
797 | if (mbus_code && fmt->mbus_code == *mbus_code) | ||
798 | return fmt; | ||
799 | if (index == id) | ||
800 | def_fmt = fmt; | ||
801 | id++; | ||
802 | } | ||
803 | return def_fmt; | ||
804 | } | ||
805 | |||
806 | static void fimc_clk_put(struct fimc_dev *fimc) | ||
807 | { | ||
808 | int i; | ||
809 | for (i = 0; i < MAX_FIMC_CLOCKS; i++) { | ||
810 | if (IS_ERR(fimc->clock[i])) | ||
811 | continue; | ||
812 | clk_unprepare(fimc->clock[i]); | ||
813 | clk_put(fimc->clock[i]); | ||
814 | fimc->clock[i] = ERR_PTR(-EINVAL); | ||
815 | } | ||
816 | } | ||
817 | |||
818 | static int fimc_clk_get(struct fimc_dev *fimc) | ||
819 | { | ||
820 | int i, ret; | ||
821 | |||
822 | for (i = 0; i < MAX_FIMC_CLOCKS; i++) | ||
823 | fimc->clock[i] = ERR_PTR(-EINVAL); | ||
824 | |||
825 | for (i = 0; i < MAX_FIMC_CLOCKS; i++) { | ||
826 | fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]); | ||
827 | if (IS_ERR(fimc->clock[i])) { | ||
828 | ret = PTR_ERR(fimc->clock[i]); | ||
829 | goto err; | ||
830 | } | ||
831 | ret = clk_prepare(fimc->clock[i]); | ||
832 | if (ret < 0) { | ||
833 | clk_put(fimc->clock[i]); | ||
834 | fimc->clock[i] = ERR_PTR(-EINVAL); | ||
835 | goto err; | ||
836 | } | ||
837 | } | ||
838 | return 0; | ||
839 | err: | ||
840 | fimc_clk_put(fimc); | ||
841 | dev_err(&fimc->pdev->dev, "failed to get clock: %s\n", | ||
842 | fimc_clocks[i]); | ||
843 | return -ENXIO; | ||
844 | } | ||
845 | |||
846 | static int fimc_m2m_suspend(struct fimc_dev *fimc) | ||
847 | { | ||
848 | unsigned long flags; | ||
849 | int timeout; | ||
850 | |||
851 | spin_lock_irqsave(&fimc->slock, flags); | ||
852 | if (!fimc_m2m_pending(fimc)) { | ||
853 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
854 | return 0; | ||
855 | } | ||
856 | clear_bit(ST_M2M_SUSPENDED, &fimc->state); | ||
857 | set_bit(ST_M2M_SUSPENDING, &fimc->state); | ||
858 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
859 | |||
860 | timeout = wait_event_timeout(fimc->irq_queue, | ||
861 | test_bit(ST_M2M_SUSPENDED, &fimc->state), | ||
862 | FIMC_SHUTDOWN_TIMEOUT); | ||
863 | |||
864 | clear_bit(ST_M2M_SUSPENDING, &fimc->state); | ||
865 | return timeout == 0 ? -EAGAIN : 0; | ||
866 | } | ||
867 | |||
868 | static int fimc_m2m_resume(struct fimc_dev *fimc) | ||
869 | { | ||
870 | struct fimc_ctx *ctx; | ||
871 | unsigned long flags; | ||
872 | |||
873 | spin_lock_irqsave(&fimc->slock, flags); | ||
874 | /* Clear for full H/W setup in first run after resume */ | ||
875 | ctx = fimc->m2m.ctx; | ||
876 | fimc->m2m.ctx = NULL; | ||
877 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
878 | |||
879 | if (test_and_clear_bit(ST_M2M_SUSPENDED, &fimc->state)) | ||
880 | fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); | ||
881 | |||
882 | return 0; | ||
883 | } | ||
884 | |||
885 | static const struct of_device_id fimc_of_match[]; | ||
886 | |||
887 | static int fimc_parse_dt(struct fimc_dev *fimc, u32 *clk_freq) | ||
888 | { | ||
889 | struct device *dev = &fimc->pdev->dev; | ||
890 | struct device_node *node = dev->of_node; | ||
891 | const struct of_device_id *of_id; | ||
892 | struct fimc_variant *v; | ||
893 | struct fimc_pix_limit *lim; | ||
894 | u32 args[FIMC_PIX_LIMITS_MAX]; | ||
895 | int ret; | ||
896 | |||
897 | if (of_property_read_bool(node, "samsung,lcd-wb")) | ||
898 | return -ENODEV; | ||
899 | |||
900 | v = devm_kzalloc(dev, sizeof(*v) + sizeof(*lim), GFP_KERNEL); | ||
901 | if (!v) | ||
902 | return -ENOMEM; | ||
903 | |||
904 | of_id = of_match_node(fimc_of_match, node); | ||
905 | if (!of_id) | ||
906 | return -EINVAL; | ||
907 | fimc->drv_data = of_id->data; | ||
908 | ret = of_property_read_u32_array(node, "samsung,pix-limits", | ||
909 | args, FIMC_PIX_LIMITS_MAX); | ||
910 | if (ret < 0) | ||
911 | return ret; | ||
912 | |||
913 | lim = (struct fimc_pix_limit *)&v[1]; | ||
914 | |||
915 | lim->scaler_en_w = args[0]; | ||
916 | lim->scaler_dis_w = args[1]; | ||
917 | lim->out_rot_en_w = args[2]; | ||
918 | lim->out_rot_dis_w = args[3]; | ||
919 | v->pix_limit = lim; | ||
920 | |||
921 | ret = of_property_read_u32_array(node, "samsung,min-pix-sizes", | ||
922 | args, 2); | ||
923 | v->min_inp_pixsize = ret ? FIMC_DEF_MIN_SIZE : args[0]; | ||
924 | v->min_out_pixsize = ret ? FIMC_DEF_MIN_SIZE : args[1]; | ||
925 | ret = of_property_read_u32_array(node, "samsung,min-pix-alignment", | ||
926 | args, 2); | ||
927 | v->min_vsize_align = ret ? FIMC_DEF_HEIGHT_ALIGN : args[0]; | ||
928 | v->hor_offs_align = ret ? FIMC_DEF_HOR_OFFS_ALIGN : args[1]; | ||
929 | |||
930 | ret = of_property_read_u32(node, "samsung,rotators", &args[1]); | ||
931 | v->has_inp_rot = ret ? 1 : args[1] & 0x01; | ||
932 | v->has_out_rot = ret ? 1 : args[1] & 0x10; | ||
933 | v->has_mainscaler_ext = of_property_read_bool(node, | ||
934 | "samsung,mainscaler-ext"); | ||
935 | |||
936 | v->has_isp_wb = of_property_read_bool(node, "samsung,isp-wb"); | ||
937 | v->has_cam_if = of_property_read_bool(node, "samsung,cam-if"); | ||
938 | of_property_read_u32(node, "clock-frequency", clk_freq); | ||
939 | fimc->id = of_alias_get_id(node, "fimc"); | ||
940 | |||
941 | fimc->variant = v; | ||
942 | return 0; | ||
943 | } | ||
944 | |||
945 | static int fimc_probe(struct platform_device *pdev) | ||
946 | { | ||
947 | struct device *dev = &pdev->dev; | ||
948 | u32 lclk_freq = 0; | ||
949 | struct fimc_dev *fimc; | ||
950 | struct resource *res; | ||
951 | int ret = 0; | ||
952 | |||
953 | fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL); | ||
954 | if (!fimc) | ||
955 | return -ENOMEM; | ||
956 | |||
957 | fimc->pdev = pdev; | ||
958 | |||
959 | if (dev->of_node) { | ||
960 | ret = fimc_parse_dt(fimc, &lclk_freq); | ||
961 | if (ret < 0) | ||
962 | return ret; | ||
963 | } else { | ||
964 | fimc->drv_data = fimc_get_drvdata(pdev); | ||
965 | fimc->id = pdev->id; | ||
966 | } | ||
967 | if (!fimc->drv_data || fimc->id >= fimc->drv_data->num_entities || | ||
968 | fimc->id < 0) { | ||
969 | dev_err(dev, "Invalid driver data or device id (%d)\n", | ||
970 | fimc->id); | ||
971 | return -EINVAL; | ||
972 | } | ||
973 | if (!dev->of_node) | ||
974 | fimc->variant = fimc->drv_data->variant[fimc->id]; | ||
975 | |||
976 | init_waitqueue_head(&fimc->irq_queue); | ||
977 | spin_lock_init(&fimc->slock); | ||
978 | mutex_init(&fimc->lock); | ||
979 | |||
980 | fimc->sysreg = fimc_get_sysreg_regmap(dev->of_node); | ||
981 | if (IS_ERR(fimc->sysreg)) | ||
982 | return PTR_ERR(fimc->sysreg); | ||
983 | |||
984 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
985 | fimc->regs = devm_ioremap_resource(dev, res); | ||
986 | if (IS_ERR(fimc->regs)) | ||
987 | return PTR_ERR(fimc->regs); | ||
988 | |||
989 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
990 | if (res == NULL) { | ||
991 | dev_err(dev, "Failed to get IRQ resource\n"); | ||
992 | return -ENXIO; | ||
993 | } | ||
994 | |||
995 | ret = fimc_clk_get(fimc); | ||
996 | if (ret) | ||
997 | return ret; | ||
998 | |||
999 | if (lclk_freq == 0) | ||
1000 | lclk_freq = fimc->drv_data->lclk_frequency; | ||
1001 | |||
1002 | ret = clk_set_rate(fimc->clock[CLK_BUS], lclk_freq); | ||
1003 | if (ret < 0) | ||
1004 | return ret; | ||
1005 | |||
1006 | ret = clk_enable(fimc->clock[CLK_BUS]); | ||
1007 | if (ret < 0) | ||
1008 | return ret; | ||
1009 | |||
1010 | ret = devm_request_irq(dev, res->start, fimc_irq_handler, | ||
1011 | 0, dev_name(dev), fimc); | ||
1012 | if (ret) { | ||
1013 | dev_err(dev, "failed to install irq (%d)\n", ret); | ||
1014 | goto err_clk; | ||
1015 | } | ||
1016 | |||
1017 | ret = fimc_initialize_capture_subdev(fimc); | ||
1018 | if (ret) | ||
1019 | goto err_clk; | ||
1020 | |||
1021 | platform_set_drvdata(pdev, fimc); | ||
1022 | pm_runtime_enable(dev); | ||
1023 | ret = pm_runtime_get_sync(dev); | ||
1024 | if (ret < 0) | ||
1025 | goto err_sd; | ||
1026 | /* Initialize contiguous memory allocator */ | ||
1027 | fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); | ||
1028 | if (IS_ERR(fimc->alloc_ctx)) { | ||
1029 | ret = PTR_ERR(fimc->alloc_ctx); | ||
1030 | goto err_pm; | ||
1031 | } | ||
1032 | |||
1033 | dev_dbg(dev, "FIMC.%d registered successfully\n", fimc->id); | ||
1034 | |||
1035 | pm_runtime_put(dev); | ||
1036 | return 0; | ||
1037 | err_pm: | ||
1038 | pm_runtime_put(dev); | ||
1039 | err_sd: | ||
1040 | fimc_unregister_capture_subdev(fimc); | ||
1041 | err_clk: | ||
1042 | clk_disable(fimc->clock[CLK_BUS]); | ||
1043 | fimc_clk_put(fimc); | ||
1044 | return ret; | ||
1045 | } | ||
1046 | |||
1047 | static int fimc_runtime_resume(struct device *dev) | ||
1048 | { | ||
1049 | struct fimc_dev *fimc = dev_get_drvdata(dev); | ||
1050 | |||
1051 | dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state); | ||
1052 | |||
1053 | /* Enable clocks and perform basic initalization */ | ||
1054 | clk_enable(fimc->clock[CLK_GATE]); | ||
1055 | fimc_hw_reset(fimc); | ||
1056 | |||
1057 | /* Resume the capture or mem-to-mem device */ | ||
1058 | if (fimc_capture_busy(fimc)) | ||
1059 | return fimc_capture_resume(fimc); | ||
1060 | |||
1061 | return fimc_m2m_resume(fimc); | ||
1062 | } | ||
1063 | |||
1064 | static int fimc_runtime_suspend(struct device *dev) | ||
1065 | { | ||
1066 | struct fimc_dev *fimc = dev_get_drvdata(dev); | ||
1067 | int ret = 0; | ||
1068 | |||
1069 | if (fimc_capture_busy(fimc)) | ||
1070 | ret = fimc_capture_suspend(fimc); | ||
1071 | else | ||
1072 | ret = fimc_m2m_suspend(fimc); | ||
1073 | if (!ret) | ||
1074 | clk_disable(fimc->clock[CLK_GATE]); | ||
1075 | |||
1076 | dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state); | ||
1077 | return ret; | ||
1078 | } | ||
1079 | |||
1080 | #ifdef CONFIG_PM_SLEEP | ||
1081 | static int fimc_resume(struct device *dev) | ||
1082 | { | ||
1083 | struct fimc_dev *fimc = dev_get_drvdata(dev); | ||
1084 | unsigned long flags; | ||
1085 | |||
1086 | dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state); | ||
1087 | |||
1088 | /* Do not resume if the device was idle before system suspend */ | ||
1089 | spin_lock_irqsave(&fimc->slock, flags); | ||
1090 | if (!test_and_clear_bit(ST_LPM, &fimc->state) || | ||
1091 | (!fimc_m2m_active(fimc) && !fimc_capture_busy(fimc))) { | ||
1092 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
1093 | return 0; | ||
1094 | } | ||
1095 | fimc_hw_reset(fimc); | ||
1096 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
1097 | |||
1098 | if (fimc_capture_busy(fimc)) | ||
1099 | return fimc_capture_resume(fimc); | ||
1100 | |||
1101 | return fimc_m2m_resume(fimc); | ||
1102 | } | ||
1103 | |||
1104 | static int fimc_suspend(struct device *dev) | ||
1105 | { | ||
1106 | struct fimc_dev *fimc = dev_get_drvdata(dev); | ||
1107 | |||
1108 | dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state); | ||
1109 | |||
1110 | if (test_and_set_bit(ST_LPM, &fimc->state)) | ||
1111 | return 0; | ||
1112 | if (fimc_capture_busy(fimc)) | ||
1113 | return fimc_capture_suspend(fimc); | ||
1114 | |||
1115 | return fimc_m2m_suspend(fimc); | ||
1116 | } | ||
1117 | #endif /* CONFIG_PM_SLEEP */ | ||
1118 | |||
1119 | static int fimc_remove(struct platform_device *pdev) | ||
1120 | { | ||
1121 | struct fimc_dev *fimc = platform_get_drvdata(pdev); | ||
1122 | |||
1123 | pm_runtime_disable(&pdev->dev); | ||
1124 | pm_runtime_set_suspended(&pdev->dev); | ||
1125 | |||
1126 | fimc_unregister_capture_subdev(fimc); | ||
1127 | vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); | ||
1128 | |||
1129 | clk_disable(fimc->clock[CLK_BUS]); | ||
1130 | fimc_clk_put(fimc); | ||
1131 | |||
1132 | dev_info(&pdev->dev, "driver unloaded\n"); | ||
1133 | return 0; | ||
1134 | } | ||
1135 | |||
1136 | /* Image pixel limits, similar across several FIMC HW revisions. */ | ||
1137 | static const struct fimc_pix_limit s5p_pix_limit[4] = { | ||
1138 | [0] = { | ||
1139 | .scaler_en_w = 3264, | ||
1140 | .scaler_dis_w = 8192, | ||
1141 | .out_rot_en_w = 1920, | ||
1142 | .out_rot_dis_w = 4224, | ||
1143 | }, | ||
1144 | [1] = { | ||
1145 | .scaler_en_w = 4224, | ||
1146 | .scaler_dis_w = 8192, | ||
1147 | .out_rot_en_w = 1920, | ||
1148 | .out_rot_dis_w = 4224, | ||
1149 | }, | ||
1150 | [2] = { | ||
1151 | .scaler_en_w = 1920, | ||
1152 | .scaler_dis_w = 8192, | ||
1153 | .out_rot_en_w = 1280, | ||
1154 | .out_rot_dis_w = 1920, | ||
1155 | }, | ||
1156 | }; | ||
1157 | |||
1158 | static const struct fimc_variant fimc0_variant_s5p = { | ||
1159 | .has_inp_rot = 1, | ||
1160 | .has_out_rot = 1, | ||
1161 | .has_cam_if = 1, | ||
1162 | .min_inp_pixsize = 16, | ||
1163 | .min_out_pixsize = 16, | ||
1164 | .hor_offs_align = 8, | ||
1165 | .min_vsize_align = 16, | ||
1166 | .pix_limit = &s5p_pix_limit[0], | ||
1167 | }; | ||
1168 | |||
1169 | static const struct fimc_variant fimc2_variant_s5p = { | ||
1170 | .has_cam_if = 1, | ||
1171 | .min_inp_pixsize = 16, | ||
1172 | .min_out_pixsize = 16, | ||
1173 | .hor_offs_align = 8, | ||
1174 | .min_vsize_align = 16, | ||
1175 | .pix_limit = &s5p_pix_limit[1], | ||
1176 | }; | ||
1177 | |||
1178 | static const struct fimc_variant fimc0_variant_s5pv210 = { | ||
1179 | .has_inp_rot = 1, | ||
1180 | .has_out_rot = 1, | ||
1181 | .has_cam_if = 1, | ||
1182 | .min_inp_pixsize = 16, | ||
1183 | .min_out_pixsize = 16, | ||
1184 | .hor_offs_align = 8, | ||
1185 | .min_vsize_align = 16, | ||
1186 | .pix_limit = &s5p_pix_limit[1], | ||
1187 | }; | ||
1188 | |||
1189 | static const struct fimc_variant fimc1_variant_s5pv210 = { | ||
1190 | .has_inp_rot = 1, | ||
1191 | .has_out_rot = 1, | ||
1192 | .has_cam_if = 1, | ||
1193 | .has_mainscaler_ext = 1, | ||
1194 | .min_inp_pixsize = 16, | ||
1195 | .min_out_pixsize = 16, | ||
1196 | .hor_offs_align = 1, | ||
1197 | .min_vsize_align = 1, | ||
1198 | .pix_limit = &s5p_pix_limit[2], | ||
1199 | }; | ||
1200 | |||
1201 | static const struct fimc_variant fimc2_variant_s5pv210 = { | ||
1202 | .has_cam_if = 1, | ||
1203 | .min_inp_pixsize = 16, | ||
1204 | .min_out_pixsize = 16, | ||
1205 | .hor_offs_align = 8, | ||
1206 | .min_vsize_align = 16, | ||
1207 | .pix_limit = &s5p_pix_limit[2], | ||
1208 | }; | ||
1209 | |||
1210 | /* S5PC100 */ | ||
1211 | static const struct fimc_drvdata fimc_drvdata_s5p = { | ||
1212 | .variant = { | ||
1213 | [0] = &fimc0_variant_s5p, | ||
1214 | [1] = &fimc0_variant_s5p, | ||
1215 | [2] = &fimc2_variant_s5p, | ||
1216 | }, | ||
1217 | .num_entities = 3, | ||
1218 | .lclk_frequency = 133000000UL, | ||
1219 | .out_buf_count = 4, | ||
1220 | }; | ||
1221 | |||
1222 | /* S5PV210, S5PC110 */ | ||
1223 | static const struct fimc_drvdata fimc_drvdata_s5pv210 = { | ||
1224 | .variant = { | ||
1225 | [0] = &fimc0_variant_s5pv210, | ||
1226 | [1] = &fimc1_variant_s5pv210, | ||
1227 | [2] = &fimc2_variant_s5pv210, | ||
1228 | }, | ||
1229 | .num_entities = 3, | ||
1230 | .lclk_frequency = 166000000UL, | ||
1231 | .out_buf_count = 4, | ||
1232 | .dma_pix_hoff = 1, | ||
1233 | }; | ||
1234 | |||
1235 | /* EXYNOS4210, S5PV310, S5PC210 */ | ||
1236 | static const struct fimc_drvdata fimc_drvdata_exynos4210 = { | ||
1237 | .num_entities = 4, | ||
1238 | .lclk_frequency = 166000000UL, | ||
1239 | .dma_pix_hoff = 1, | ||
1240 | .cistatus2 = 1, | ||
1241 | .alpha_color = 1, | ||
1242 | .out_buf_count = 32, | ||
1243 | }; | ||
1244 | |||
1245 | /* EXYNOS4212, EXYNOS4412 */ | ||
1246 | static const struct fimc_drvdata fimc_drvdata_exynos4x12 = { | ||
1247 | .num_entities = 4, | ||
1248 | .lclk_frequency = 166000000UL, | ||
1249 | .dma_pix_hoff = 1, | ||
1250 | .cistatus2 = 1, | ||
1251 | .alpha_color = 1, | ||
1252 | .out_buf_count = 32, | ||
1253 | }; | ||
1254 | |||
1255 | static const struct platform_device_id fimc_driver_ids[] = { | ||
1256 | { | ||
1257 | .name = "s5p-fimc", | ||
1258 | .driver_data = (unsigned long)&fimc_drvdata_s5p, | ||
1259 | }, { | ||
1260 | .name = "s5pv210-fimc", | ||
1261 | .driver_data = (unsigned long)&fimc_drvdata_s5pv210, | ||
1262 | }, { | ||
1263 | .name = "exynos4-fimc", | ||
1264 | .driver_data = (unsigned long)&fimc_drvdata_exynos4210, | ||
1265 | }, { | ||
1266 | .name = "exynos4x12-fimc", | ||
1267 | .driver_data = (unsigned long)&fimc_drvdata_exynos4x12, | ||
1268 | }, | ||
1269 | { }, | ||
1270 | }; | ||
1271 | |||
1272 | static const struct of_device_id fimc_of_match[] = { | ||
1273 | { | ||
1274 | .compatible = "samsung,s5pv210-fimc", | ||
1275 | .data = &fimc_drvdata_s5pv210, | ||
1276 | }, { | ||
1277 | .compatible = "samsung,exynos4210-fimc", | ||
1278 | .data = &fimc_drvdata_exynos4210, | ||
1279 | }, { | ||
1280 | .compatible = "samsung,exynos4212-fimc", | ||
1281 | .data = &fimc_drvdata_exynos4x12, | ||
1282 | }, | ||
1283 | { /* sentinel */ }, | ||
1284 | }; | ||
1285 | |||
1286 | static const struct dev_pm_ops fimc_pm_ops = { | ||
1287 | SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume) | ||
1288 | SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL) | ||
1289 | }; | ||
1290 | |||
1291 | static struct platform_driver fimc_driver = { | ||
1292 | .probe = fimc_probe, | ||
1293 | .remove = fimc_remove, | ||
1294 | .id_table = fimc_driver_ids, | ||
1295 | .driver = { | ||
1296 | .of_match_table = fimc_of_match, | ||
1297 | .name = FIMC_DRIVER_NAME, | ||
1298 | .owner = THIS_MODULE, | ||
1299 | .pm = &fimc_pm_ops, | ||
1300 | } | ||
1301 | }; | ||
1302 | |||
1303 | int __init fimc_register_driver(void) | ||
1304 | { | ||
1305 | return platform_driver_register(&fimc_driver); | ||
1306 | } | ||
1307 | |||
1308 | void __exit fimc_unregister_driver(void) | ||
1309 | { | ||
1310 | platform_driver_unregister(&fimc_driver); | ||
1311 | } | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h new file mode 100644 index 000000000000..539a3f71c16a --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-core.h | |||
@@ -0,0 +1,731 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef FIMC_CORE_H_ | ||
10 | #define FIMC_CORE_H_ | ||
11 | |||
12 | /*#define DEBUG*/ | ||
13 | |||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/regmap.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | #include <linux/mfd/syscon.h> | ||
19 | #include <linux/types.h> | ||
20 | #include <linux/videodev2.h> | ||
21 | #include <linux/io.h> | ||
22 | #include <linux/sizes.h> | ||
23 | |||
24 | #include <media/media-entity.h> | ||
25 | #include <media/videobuf2-core.h> | ||
26 | #include <media/v4l2-ctrls.h> | ||
27 | #include <media/v4l2-device.h> | ||
28 | #include <media/v4l2-mem2mem.h> | ||
29 | #include <media/v4l2-mediabus.h> | ||
30 | #include <media/s5p_fimc.h> | ||
31 | |||
32 | #define dbg(fmt, args...) \ | ||
33 | pr_debug("%s:%d: " fmt "\n", __func__, __LINE__, ##args) | ||
34 | |||
35 | /* Time to wait for next frame VSYNC interrupt while stopping operation. */ | ||
36 | #define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) | ||
37 | #define MAX_FIMC_CLOCKS 2 | ||
38 | #define FIMC_DRIVER_NAME "exynos4-fimc" | ||
39 | #define FIMC_MAX_DEVS 4 | ||
40 | #define FIMC_MAX_OUT_BUFS 4 | ||
41 | #define SCALER_MAX_HRATIO 64 | ||
42 | #define SCALER_MAX_VRATIO 64 | ||
43 | #define DMA_MIN_SIZE 8 | ||
44 | #define FIMC_CAMIF_MAX_HEIGHT 0x2000 | ||
45 | #define FIMC_MAX_JPEG_BUF_SIZE (10 * SZ_1M) | ||
46 | #define FIMC_MAX_PLANES 3 | ||
47 | #define FIMC_PIX_LIMITS_MAX 4 | ||
48 | #define FIMC_DEF_MIN_SIZE 16 | ||
49 | #define FIMC_DEF_HEIGHT_ALIGN 2 | ||
50 | #define FIMC_DEF_HOR_OFFS_ALIGN 1 | ||
51 | |||
52 | /* indices to the clocks array */ | ||
53 | enum { | ||
54 | CLK_BUS, | ||
55 | CLK_GATE, | ||
56 | }; | ||
57 | |||
58 | enum fimc_dev_flags { | ||
59 | ST_LPM, | ||
60 | /* m2m node */ | ||
61 | ST_M2M_RUN, | ||
62 | ST_M2M_PEND, | ||
63 | ST_M2M_SUSPENDING, | ||
64 | ST_M2M_SUSPENDED, | ||
65 | /* capture node */ | ||
66 | ST_CAPT_PEND, | ||
67 | ST_CAPT_RUN, | ||
68 | ST_CAPT_STREAM, | ||
69 | ST_CAPT_ISP_STREAM, | ||
70 | ST_CAPT_SUSPENDED, | ||
71 | ST_CAPT_SHUT, | ||
72 | ST_CAPT_BUSY, | ||
73 | ST_CAPT_APPLY_CFG, | ||
74 | ST_CAPT_JPEG, | ||
75 | }; | ||
76 | |||
77 | #define fimc_m2m_active(dev) test_bit(ST_M2M_RUN, &(dev)->state) | ||
78 | #define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state) | ||
79 | |||
80 | #define fimc_capture_running(dev) test_bit(ST_CAPT_RUN, &(dev)->state) | ||
81 | #define fimc_capture_pending(dev) test_bit(ST_CAPT_PEND, &(dev)->state) | ||
82 | #define fimc_capture_busy(dev) test_bit(ST_CAPT_BUSY, &(dev)->state) | ||
83 | |||
84 | enum fimc_datapath { | ||
85 | FIMC_IO_NONE, | ||
86 | FIMC_IO_CAMERA, | ||
87 | FIMC_IO_DMA, | ||
88 | FIMC_IO_LCDFIFO, | ||
89 | FIMC_IO_WRITEBACK, | ||
90 | FIMC_IO_ISP, | ||
91 | }; | ||
92 | |||
93 | enum fimc_color_fmt { | ||
94 | FIMC_FMT_RGB444 = 0x10, | ||
95 | FIMC_FMT_RGB555, | ||
96 | FIMC_FMT_RGB565, | ||
97 | FIMC_FMT_RGB666, | ||
98 | FIMC_FMT_RGB888, | ||
99 | FIMC_FMT_RGB30_LOCAL, | ||
100 | FIMC_FMT_YCBCR420 = 0x20, | ||
101 | FIMC_FMT_YCBYCR422, | ||
102 | FIMC_FMT_YCRYCB422, | ||
103 | FIMC_FMT_CBYCRY422, | ||
104 | FIMC_FMT_CRYCBY422, | ||
105 | FIMC_FMT_YCBCR444_LOCAL, | ||
106 | FIMC_FMT_RAW8 = 0x40, | ||
107 | FIMC_FMT_RAW10, | ||
108 | FIMC_FMT_RAW12, | ||
109 | FIMC_FMT_JPEG = 0x80, | ||
110 | FIMC_FMT_YUYV_JPEG = 0x100, | ||
111 | }; | ||
112 | |||
113 | #define fimc_fmt_is_user_defined(x) (!!((x) & 0x180)) | ||
114 | #define fimc_fmt_is_rgb(x) (!!((x) & 0x10)) | ||
115 | |||
116 | #define IS_M2M(__strt) ((__strt) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || \ | ||
117 | __strt == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | ||
118 | |||
119 | /* The hardware context state. */ | ||
120 | #define FIMC_PARAMS (1 << 0) | ||
121 | #define FIMC_COMPOSE (1 << 1) | ||
122 | #define FIMC_CTX_M2M (1 << 16) | ||
123 | #define FIMC_CTX_CAP (1 << 17) | ||
124 | #define FIMC_CTX_SHUT (1 << 18) | ||
125 | |||
126 | /* Image conversion flags */ | ||
127 | #define FIMC_IN_DMA_ACCESS_TILED (1 << 0) | ||
128 | #define FIMC_IN_DMA_ACCESS_LINEAR (0 << 0) | ||
129 | #define FIMC_OUT_DMA_ACCESS_TILED (1 << 1) | ||
130 | #define FIMC_OUT_DMA_ACCESS_LINEAR (0 << 1) | ||
131 | #define FIMC_SCAN_MODE_PROGRESSIVE (0 << 2) | ||
132 | #define FIMC_SCAN_MODE_INTERLACED (1 << 2) | ||
133 | /* | ||
134 | * YCbCr data dynamic range for RGB-YUV color conversion. | ||
135 | * Y/Cb/Cr: (0 ~ 255) */ | ||
136 | #define FIMC_COLOR_RANGE_WIDE (0 << 3) | ||
137 | /* Y (16 ~ 235), Cb/Cr (16 ~ 240) */ | ||
138 | #define FIMC_COLOR_RANGE_NARROW (1 << 3) | ||
139 | |||
140 | /** | ||
141 | * struct fimc_dma_offset - pixel offset information for DMA | ||
142 | * @y_h: y value horizontal offset | ||
143 | * @y_v: y value vertical offset | ||
144 | * @cb_h: cb value horizontal offset | ||
145 | * @cb_v: cb value vertical offset | ||
146 | * @cr_h: cr value horizontal offset | ||
147 | * @cr_v: cr value vertical offset | ||
148 | */ | ||
149 | struct fimc_dma_offset { | ||
150 | int y_h; | ||
151 | int y_v; | ||
152 | int cb_h; | ||
153 | int cb_v; | ||
154 | int cr_h; | ||
155 | int cr_v; | ||
156 | }; | ||
157 | |||
158 | /** | ||
159 | * struct fimc_effect - color effect information | ||
160 | * @type: effect type | ||
161 | * @pat_cb: cr value when type is "arbitrary" | ||
162 | * @pat_cr: cr value when type is "arbitrary" | ||
163 | */ | ||
164 | struct fimc_effect { | ||
165 | u32 type; | ||
166 | u8 pat_cb; | ||
167 | u8 pat_cr; | ||
168 | }; | ||
169 | |||
170 | /** | ||
171 | * struct fimc_scaler - the configuration data for FIMC inetrnal scaler | ||
172 | * @scaleup_h: flag indicating scaling up horizontally | ||
173 | * @scaleup_v: flag indicating scaling up vertically | ||
174 | * @copy_mode: flag indicating transparent DMA transfer (no scaling | ||
175 | * and color format conversion) | ||
176 | * @enabled: flag indicating if the scaler is used | ||
177 | * @hfactor: horizontal shift factor | ||
178 | * @vfactor: vertical shift factor | ||
179 | * @pre_hratio: horizontal ratio of the prescaler | ||
180 | * @pre_vratio: vertical ratio of the prescaler | ||
181 | * @pre_dst_width: the prescaler's destination width | ||
182 | * @pre_dst_height: the prescaler's destination height | ||
183 | * @main_hratio: the main scaler's horizontal ratio | ||
184 | * @main_vratio: the main scaler's vertical ratio | ||
185 | * @real_width: source pixel (width - offset) | ||
186 | * @real_height: source pixel (height - offset) | ||
187 | */ | ||
188 | struct fimc_scaler { | ||
189 | unsigned int scaleup_h:1; | ||
190 | unsigned int scaleup_v:1; | ||
191 | unsigned int copy_mode:1; | ||
192 | unsigned int enabled:1; | ||
193 | u32 hfactor; | ||
194 | u32 vfactor; | ||
195 | u32 pre_hratio; | ||
196 | u32 pre_vratio; | ||
197 | u32 pre_dst_width; | ||
198 | u32 pre_dst_height; | ||
199 | u32 main_hratio; | ||
200 | u32 main_vratio; | ||
201 | u32 real_width; | ||
202 | u32 real_height; | ||
203 | }; | ||
204 | |||
205 | /** | ||
206 | * struct fimc_addr - the FIMC physical address set for DMA | ||
207 | * @y: luminance plane physical address | ||
208 | * @cb: Cb plane physical address | ||
209 | * @cr: Cr plane physical address | ||
210 | */ | ||
211 | struct fimc_addr { | ||
212 | u32 y; | ||
213 | u32 cb; | ||
214 | u32 cr; | ||
215 | }; | ||
216 | |||
217 | /** | ||
218 | * struct fimc_vid_buffer - the driver's video buffer | ||
219 | * @vb: v4l videobuf buffer | ||
220 | * @list: linked list structure for buffer queue | ||
221 | * @paddr: precalculated physical address set | ||
222 | * @index: buffer index for the output DMA engine | ||
223 | */ | ||
224 | struct fimc_vid_buffer { | ||
225 | struct vb2_buffer vb; | ||
226 | struct list_head list; | ||
227 | struct fimc_addr paddr; | ||
228 | int index; | ||
229 | }; | ||
230 | |||
231 | /** | ||
232 | * struct fimc_frame - source/target frame properties | ||
233 | * @f_width: image full width (virtual screen size) | ||
234 | * @f_height: image full height (virtual screen size) | ||
235 | * @o_width: original image width as set by S_FMT | ||
236 | * @o_height: original image height as set by S_FMT | ||
237 | * @offs_h: image horizontal pixel offset | ||
238 | * @offs_v: image vertical pixel offset | ||
239 | * @width: image pixel width | ||
240 | * @height: image pixel weight | ||
241 | * @payload: image size in bytes (w x h x bpp) | ||
242 | * @bytesperline: bytesperline value for each plane | ||
243 | * @paddr: image frame buffer physical addresses | ||
244 | * @dma_offset: DMA offset in bytes | ||
245 | * @fmt: fimc color format pointer | ||
246 | */ | ||
247 | struct fimc_frame { | ||
248 | u32 f_width; | ||
249 | u32 f_height; | ||
250 | u32 o_width; | ||
251 | u32 o_height; | ||
252 | u32 offs_h; | ||
253 | u32 offs_v; | ||
254 | u32 width; | ||
255 | u32 height; | ||
256 | unsigned int payload[VIDEO_MAX_PLANES]; | ||
257 | unsigned int bytesperline[VIDEO_MAX_PLANES]; | ||
258 | struct fimc_addr paddr; | ||
259 | struct fimc_dma_offset dma_offset; | ||
260 | struct fimc_fmt *fmt; | ||
261 | u8 alpha; | ||
262 | }; | ||
263 | |||
264 | /** | ||
265 | * struct fimc_m2m_device - v4l2 memory-to-memory device data | ||
266 | * @vfd: the video device node for v4l2 m2m mode | ||
267 | * @m2m_dev: v4l2 memory-to-memory device data | ||
268 | * @ctx: hardware context data | ||
269 | * @refcnt: the reference counter | ||
270 | */ | ||
271 | struct fimc_m2m_device { | ||
272 | struct video_device vfd; | ||
273 | struct v4l2_m2m_dev *m2m_dev; | ||
274 | struct fimc_ctx *ctx; | ||
275 | int refcnt; | ||
276 | }; | ||
277 | |||
278 | #define FIMC_SD_PAD_SINK_CAM 0 | ||
279 | #define FIMC_SD_PAD_SINK_FIFO 1 | ||
280 | #define FIMC_SD_PAD_SOURCE 2 | ||
281 | #define FIMC_SD_PADS_NUM 3 | ||
282 | |||
283 | /** | ||
284 | * struct fimc_vid_cap - camera capture device information | ||
285 | * @ctx: hardware context data | ||
286 | * @vfd: video device node for camera capture mode | ||
287 | * @subdev: subdev exposing the FIMC processing block | ||
288 | * @vd_pad: fimc video capture node pad | ||
289 | * @sd_pads: fimc video processing block pads | ||
290 | * @ci_fmt: image format at the FIMC camera input (and the scaler output) | ||
291 | * @wb_fmt: image format at the FIMC ISP Writeback input | ||
292 | * @source_config: external image source related configuration structure | ||
293 | * @pending_buf_q: the pending buffer queue head | ||
294 | * @active_buf_q: the queue head of buffers scheduled in hardware | ||
295 | * @vbq: the capture am video buffer queue | ||
296 | * @active_buf_cnt: number of video buffers scheduled in hardware | ||
297 | * @buf_index: index for managing the output DMA buffers | ||
298 | * @frame_count: the frame counter for statistics | ||
299 | * @reqbufs_count: the number of buffers requested in REQBUFS ioctl | ||
300 | * @input_index: input (camera sensor) index | ||
301 | * @refcnt: driver's private reference counter | ||
302 | * @input: capture input type, grp_id of the attached subdev | ||
303 | * @user_subdev_api: true if subdevs are not configured by the host driver | ||
304 | */ | ||
305 | struct fimc_vid_cap { | ||
306 | struct fimc_ctx *ctx; | ||
307 | struct vb2_alloc_ctx *alloc_ctx; | ||
308 | struct video_device vfd; | ||
309 | struct v4l2_subdev subdev; | ||
310 | struct media_pad vd_pad; | ||
311 | struct media_pad sd_pads[FIMC_SD_PADS_NUM]; | ||
312 | struct v4l2_mbus_framefmt ci_fmt; | ||
313 | struct v4l2_mbus_framefmt wb_fmt; | ||
314 | struct fimc_source_info source_config; | ||
315 | struct list_head pending_buf_q; | ||
316 | struct list_head active_buf_q; | ||
317 | struct vb2_queue vbq; | ||
318 | int active_buf_cnt; | ||
319 | int buf_index; | ||
320 | unsigned int frame_count; | ||
321 | unsigned int reqbufs_count; | ||
322 | bool streaming; | ||
323 | int input_index; | ||
324 | int refcnt; | ||
325 | u32 input; | ||
326 | bool user_subdev_api; | ||
327 | }; | ||
328 | |||
329 | /** | ||
330 | * struct fimc_pix_limit - image pixel size limits in various IP configurations | ||
331 | * | ||
332 | * @scaler_en_w: max input pixel width when the scaler is enabled | ||
333 | * @scaler_dis_w: max input pixel width when the scaler is disabled | ||
334 | * @in_rot_en_h: max input width with the input rotator is on | ||
335 | * @in_rot_dis_w: max input width with the input rotator is off | ||
336 | * @out_rot_en_w: max output width with the output rotator on | ||
337 | * @out_rot_dis_w: max output width with the output rotator off | ||
338 | */ | ||
339 | struct fimc_pix_limit { | ||
340 | u16 scaler_en_w; | ||
341 | u16 scaler_dis_w; | ||
342 | u16 in_rot_en_h; | ||
343 | u16 in_rot_dis_w; | ||
344 | u16 out_rot_en_w; | ||
345 | u16 out_rot_dis_w; | ||
346 | }; | ||
347 | |||
348 | /** | ||
349 | * struct fimc_variant - FIMC device variant information | ||
350 | * @has_inp_rot: set if has input rotator | ||
351 | * @has_out_rot: set if has output rotator | ||
352 | * @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register | ||
353 | * are present in this IP revision | ||
354 | * @has_cam_if: set if this instance has a camera input interface | ||
355 | * @has_isp_wb: set if this instance has ISP writeback input | ||
356 | * @pix_limit: pixel size constraints for the scaler | ||
357 | * @min_inp_pixsize: minimum input pixel size | ||
358 | * @min_out_pixsize: minimum output pixel size | ||
359 | * @hor_offs_align: horizontal pixel offset aligment | ||
360 | * @min_vsize_align: minimum vertical pixel size alignment | ||
361 | */ | ||
362 | struct fimc_variant { | ||
363 | unsigned int has_inp_rot:1; | ||
364 | unsigned int has_out_rot:1; | ||
365 | unsigned int has_mainscaler_ext:1; | ||
366 | unsigned int has_cam_if:1; | ||
367 | unsigned int has_isp_wb:1; | ||
368 | const struct fimc_pix_limit *pix_limit; | ||
369 | u16 min_inp_pixsize; | ||
370 | u16 min_out_pixsize; | ||
371 | u16 hor_offs_align; | ||
372 | u16 min_vsize_align; | ||
373 | }; | ||
374 | |||
375 | /** | ||
376 | * struct fimc_drvdata - per device type driver data | ||
377 | * @variant: variant information for this device | ||
378 | * @num_entities: number of fimc instances available in a SoC | ||
379 | * @lclk_frequency: local bus clock frequency | ||
380 | * @cistatus2: 1 if the FIMC IPs have CISTATUS2 register | ||
381 | * @dma_pix_hoff: the horizontal DMA offset unit: 1 - pixels, 0 - bytes | ||
382 | * @alpha_color: 1 if alpha color component is supported | ||
383 | * @out_buf_count: maximum number of output DMA buffers supported | ||
384 | */ | ||
385 | struct fimc_drvdata { | ||
386 | const struct fimc_variant *variant[FIMC_MAX_DEVS]; | ||
387 | int num_entities; | ||
388 | unsigned long lclk_frequency; | ||
389 | /* Fields common to all FIMC IP instances */ | ||
390 | u8 cistatus2; | ||
391 | u8 dma_pix_hoff; | ||
392 | u8 alpha_color; | ||
393 | u8 out_buf_count; | ||
394 | }; | ||
395 | |||
396 | #define fimc_get_drvdata(_pdev) \ | ||
397 | ((struct fimc_drvdata *) platform_get_device_id(_pdev)->driver_data) | ||
398 | |||
399 | struct fimc_ctx; | ||
400 | |||
401 | /** | ||
402 | * struct fimc_dev - abstraction for FIMC entity | ||
403 | * @slock: the spinlock protecting this data structure | ||
404 | * @lock: the mutex protecting this data structure | ||
405 | * @pdev: pointer to the FIMC platform device | ||
406 | * @pdata: pointer to the device platform data | ||
407 | * @sysreg: pointer to the SYSREG regmap | ||
408 | * @variant: the IP variant information | ||
409 | * @id: FIMC device index (0..FIMC_MAX_DEVS) | ||
410 | * @clock: clocks required for FIMC operation | ||
411 | * @regs: the mapped hardware registers | ||
412 | * @irq_queue: interrupt handler waitqueue | ||
413 | * @v4l2_dev: root v4l2_device | ||
414 | * @m2m: memory-to-memory V4L2 device information | ||
415 | * @vid_cap: camera capture device information | ||
416 | * @state: flags used to synchronize m2m and capture mode operation | ||
417 | * @alloc_ctx: videobuf2 memory allocator context | ||
418 | * @pipeline: fimc video capture pipeline data structure | ||
419 | */ | ||
420 | struct fimc_dev { | ||
421 | spinlock_t slock; | ||
422 | struct mutex lock; | ||
423 | struct platform_device *pdev; | ||
424 | struct s5p_platform_fimc *pdata; | ||
425 | struct regmap *sysreg; | ||
426 | const struct fimc_variant *variant; | ||
427 | const struct fimc_drvdata *drv_data; | ||
428 | int id; | ||
429 | struct clk *clock[MAX_FIMC_CLOCKS]; | ||
430 | void __iomem *regs; | ||
431 | wait_queue_head_t irq_queue; | ||
432 | struct v4l2_device *v4l2_dev; | ||
433 | struct fimc_m2m_device m2m; | ||
434 | struct fimc_vid_cap vid_cap; | ||
435 | unsigned long state; | ||
436 | struct vb2_alloc_ctx *alloc_ctx; | ||
437 | struct fimc_pipeline pipeline; | ||
438 | const struct fimc_pipeline_ops *pipeline_ops; | ||
439 | }; | ||
440 | |||
441 | /** | ||
442 | * struct fimc_ctrls - v4l2 controls structure | ||
443 | * @handler: the control handler | ||
444 | * @colorfx: image effect control | ||
445 | * @colorfx_cbcr: Cb/Cr coefficients control | ||
446 | * @rotate: image rotation control | ||
447 | * @hflip: horizontal flip control | ||
448 | * @vflip: vertical flip control | ||
449 | * @alpha: RGB alpha control | ||
450 | * @ready: true if @handler is initialized | ||
451 | */ | ||
452 | struct fimc_ctrls { | ||
453 | struct v4l2_ctrl_handler handler; | ||
454 | struct { | ||
455 | struct v4l2_ctrl *colorfx; | ||
456 | struct v4l2_ctrl *colorfx_cbcr; | ||
457 | }; | ||
458 | struct v4l2_ctrl *rotate; | ||
459 | struct v4l2_ctrl *hflip; | ||
460 | struct v4l2_ctrl *vflip; | ||
461 | struct v4l2_ctrl *alpha; | ||
462 | bool ready; | ||
463 | }; | ||
464 | |||
465 | /** | ||
466 | * fimc_ctx - the device context data | ||
467 | * @s_frame: source frame properties | ||
468 | * @d_frame: destination frame properties | ||
469 | * @out_order_1p: output 1-plane YCBCR order | ||
470 | * @out_order_2p: output 2-plane YCBCR order | ||
471 | * @in_order_1p input 1-plane YCBCR order | ||
472 | * @in_order_2p: input 2-plane YCBCR order | ||
473 | * @in_path: input mode (DMA or camera) | ||
474 | * @out_path: output mode (DMA or FIFO) | ||
475 | * @scaler: image scaler properties | ||
476 | * @effect: image effect | ||
477 | * @rotation: image clockwise rotation in degrees | ||
478 | * @hflip: indicates image horizontal flip if set | ||
479 | * @vflip: indicates image vertical flip if set | ||
480 | * @flags: additional flags for image conversion | ||
481 | * @state: flags to keep track of user configuration | ||
482 | * @fimc_dev: the FIMC device this context applies to | ||
483 | * @m2m_ctx: memory-to-memory device context | ||
484 | * @fh: v4l2 file handle | ||
485 | * @ctrls: v4l2 controls structure | ||
486 | */ | ||
487 | struct fimc_ctx { | ||
488 | struct fimc_frame s_frame; | ||
489 | struct fimc_frame d_frame; | ||
490 | u32 out_order_1p; | ||
491 | u32 out_order_2p; | ||
492 | u32 in_order_1p; | ||
493 | u32 in_order_2p; | ||
494 | enum fimc_datapath in_path; | ||
495 | enum fimc_datapath out_path; | ||
496 | struct fimc_scaler scaler; | ||
497 | struct fimc_effect effect; | ||
498 | int rotation; | ||
499 | unsigned int hflip:1; | ||
500 | unsigned int vflip:1; | ||
501 | u32 flags; | ||
502 | u32 state; | ||
503 | struct fimc_dev *fimc_dev; | ||
504 | struct v4l2_m2m_ctx *m2m_ctx; | ||
505 | struct v4l2_fh fh; | ||
506 | struct fimc_ctrls ctrls; | ||
507 | }; | ||
508 | |||
509 | #define fh_to_ctx(__fh) container_of(__fh, struct fimc_ctx, fh) | ||
510 | |||
511 | static inline void set_frame_bounds(struct fimc_frame *f, u32 width, u32 height) | ||
512 | { | ||
513 | f->o_width = width; | ||
514 | f->o_height = height; | ||
515 | f->f_width = width; | ||
516 | f->f_height = height; | ||
517 | } | ||
518 | |||
519 | static inline void set_frame_crop(struct fimc_frame *f, | ||
520 | u32 left, u32 top, u32 width, u32 height) | ||
521 | { | ||
522 | f->offs_h = left; | ||
523 | f->offs_v = top; | ||
524 | f->width = width; | ||
525 | f->height = height; | ||
526 | } | ||
527 | |||
528 | static inline u32 fimc_get_format_depth(struct fimc_fmt *ff) | ||
529 | { | ||
530 | u32 i, depth = 0; | ||
531 | |||
532 | if (ff != NULL) | ||
533 | for (i = 0; i < ff->colplanes; i++) | ||
534 | depth += ff->depth[i]; | ||
535 | return depth; | ||
536 | } | ||
537 | |||
538 | static inline bool fimc_capture_active(struct fimc_dev *fimc) | ||
539 | { | ||
540 | unsigned long flags; | ||
541 | bool ret; | ||
542 | |||
543 | spin_lock_irqsave(&fimc->slock, flags); | ||
544 | ret = !!(fimc->state & (1 << ST_CAPT_RUN) || | ||
545 | fimc->state & (1 << ST_CAPT_PEND)); | ||
546 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
547 | return ret; | ||
548 | } | ||
549 | |||
550 | static inline void fimc_ctx_state_set(u32 state, struct fimc_ctx *ctx) | ||
551 | { | ||
552 | unsigned long flags; | ||
553 | |||
554 | spin_lock_irqsave(&ctx->fimc_dev->slock, flags); | ||
555 | ctx->state |= state; | ||
556 | spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags); | ||
557 | } | ||
558 | |||
559 | static inline bool fimc_ctx_state_is_set(u32 mask, struct fimc_ctx *ctx) | ||
560 | { | ||
561 | unsigned long flags; | ||
562 | bool ret; | ||
563 | |||
564 | spin_lock_irqsave(&ctx->fimc_dev->slock, flags); | ||
565 | ret = (ctx->state & mask) == mask; | ||
566 | spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags); | ||
567 | return ret; | ||
568 | } | ||
569 | |||
570 | static inline int tiled_fmt(struct fimc_fmt *fmt) | ||
571 | { | ||
572 | return fmt->fourcc == V4L2_PIX_FMT_NV12MT; | ||
573 | } | ||
574 | |||
575 | static inline bool fimc_jpeg_fourcc(u32 pixelformat) | ||
576 | { | ||
577 | return (pixelformat == V4L2_PIX_FMT_JPEG || | ||
578 | pixelformat == V4L2_PIX_FMT_S5C_UYVY_JPG); | ||
579 | } | ||
580 | |||
581 | static inline bool fimc_user_defined_mbus_fmt(u32 code) | ||
582 | { | ||
583 | return (code == V4L2_MBUS_FMT_JPEG_1X8 || | ||
584 | code == V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8); | ||
585 | } | ||
586 | |||
587 | /* Return the alpha component bit mask */ | ||
588 | static inline int fimc_get_alpha_mask(struct fimc_fmt *fmt) | ||
589 | { | ||
590 | switch (fmt->color) { | ||
591 | case FIMC_FMT_RGB444: return 0x0f; | ||
592 | case FIMC_FMT_RGB555: return 0x01; | ||
593 | case FIMC_FMT_RGB888: return 0xff; | ||
594 | default: return 0; | ||
595 | }; | ||
596 | } | ||
597 | |||
598 | static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx, | ||
599 | enum v4l2_buf_type type) | ||
600 | { | ||
601 | struct fimc_frame *frame; | ||
602 | |||
603 | if (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == type) { | ||
604 | if (fimc_ctx_state_is_set(FIMC_CTX_M2M, ctx)) | ||
605 | frame = &ctx->s_frame; | ||
606 | else | ||
607 | return ERR_PTR(-EINVAL); | ||
608 | } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) { | ||
609 | frame = &ctx->d_frame; | ||
610 | } else { | ||
611 | v4l2_err(ctx->fimc_dev->v4l2_dev, | ||
612 | "Wrong buffer/video queue type (%d)\n", type); | ||
613 | return ERR_PTR(-EINVAL); | ||
614 | } | ||
615 | |||
616 | return frame; | ||
617 | } | ||
618 | |||
619 | /* -----------------------------------------------------*/ | ||
620 | /* fimc-core.c */ | ||
621 | int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv, | ||
622 | struct v4l2_fmtdesc *f); | ||
623 | void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, | ||
624 | unsigned int caps); | ||
625 | int fimc_ctrls_create(struct fimc_ctx *ctx); | ||
626 | void fimc_ctrls_delete(struct fimc_ctx *ctx); | ||
627 | void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active); | ||
628 | void fimc_alpha_ctrl_update(struct fimc_ctx *ctx); | ||
629 | void __fimc_get_format(struct fimc_frame *frame, struct v4l2_format *f); | ||
630 | void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, | ||
631 | struct v4l2_pix_format_mplane *pix); | ||
632 | struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code, | ||
633 | unsigned int mask, int index); | ||
634 | struct fimc_fmt *fimc_get_format(unsigned int index); | ||
635 | |||
636 | int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, | ||
637 | int dw, int dh, int rotation); | ||
638 | int fimc_set_scaler_info(struct fimc_ctx *ctx); | ||
639 | int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags); | ||
640 | int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, | ||
641 | struct fimc_frame *frame, struct fimc_addr *paddr); | ||
642 | void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f); | ||
643 | void fimc_set_yuv_order(struct fimc_ctx *ctx); | ||
644 | void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf); | ||
645 | |||
646 | int fimc_register_m2m_device(struct fimc_dev *fimc, | ||
647 | struct v4l2_device *v4l2_dev); | ||
648 | void fimc_unregister_m2m_device(struct fimc_dev *fimc); | ||
649 | int fimc_register_driver(void); | ||
650 | void fimc_unregister_driver(void); | ||
651 | |||
652 | #ifdef CONFIG_MFD_SYSCON | ||
653 | static inline struct regmap * fimc_get_sysreg_regmap(struct device_node *node) | ||
654 | { | ||
655 | return syscon_regmap_lookup_by_phandle(node, "samsung,sysreg"); | ||
656 | } | ||
657 | #else | ||
658 | #define fimc_get_sysreg_regmap(node) (NULL) | ||
659 | #endif | ||
660 | |||
661 | /* -----------------------------------------------------*/ | ||
662 | /* fimc-m2m.c */ | ||
663 | void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state); | ||
664 | |||
665 | /* -----------------------------------------------------*/ | ||
666 | /* fimc-capture.c */ | ||
667 | int fimc_initialize_capture_subdev(struct fimc_dev *fimc); | ||
668 | void fimc_unregister_capture_subdev(struct fimc_dev *fimc); | ||
669 | int fimc_capture_ctrls_create(struct fimc_dev *fimc); | ||
670 | void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, | ||
671 | void *arg); | ||
672 | int fimc_capture_suspend(struct fimc_dev *fimc); | ||
673 | int fimc_capture_resume(struct fimc_dev *fimc); | ||
674 | |||
675 | /* | ||
676 | * Buffer list manipulation functions. Must be called with fimc.slock held. | ||
677 | */ | ||
678 | |||
679 | /** | ||
680 | * fimc_active_queue_add - add buffer to the capture active buffers queue | ||
681 | * @buf: buffer to add to the active buffers list | ||
682 | */ | ||
683 | static inline void fimc_active_queue_add(struct fimc_vid_cap *vid_cap, | ||
684 | struct fimc_vid_buffer *buf) | ||
685 | { | ||
686 | list_add_tail(&buf->list, &vid_cap->active_buf_q); | ||
687 | vid_cap->active_buf_cnt++; | ||
688 | } | ||
689 | |||
690 | /** | ||
691 | * fimc_active_queue_pop - pop buffer from the capture active buffers queue | ||
692 | * | ||
693 | * The caller must assure the active_buf_q list is not empty. | ||
694 | */ | ||
695 | static inline struct fimc_vid_buffer *fimc_active_queue_pop( | ||
696 | struct fimc_vid_cap *vid_cap) | ||
697 | { | ||
698 | struct fimc_vid_buffer *buf; | ||
699 | buf = list_entry(vid_cap->active_buf_q.next, | ||
700 | struct fimc_vid_buffer, list); | ||
701 | list_del(&buf->list); | ||
702 | vid_cap->active_buf_cnt--; | ||
703 | return buf; | ||
704 | } | ||
705 | |||
706 | /** | ||
707 | * fimc_pending_queue_add - add buffer to the capture pending buffers queue | ||
708 | * @buf: buffer to add to the pending buffers list | ||
709 | */ | ||
710 | static inline void fimc_pending_queue_add(struct fimc_vid_cap *vid_cap, | ||
711 | struct fimc_vid_buffer *buf) | ||
712 | { | ||
713 | list_add_tail(&buf->list, &vid_cap->pending_buf_q); | ||
714 | } | ||
715 | |||
716 | /** | ||
717 | * fimc_pending_queue_pop - pop buffer from the capture pending buffers queue | ||
718 | * | ||
719 | * The caller must assure the pending_buf_q list is not empty. | ||
720 | */ | ||
721 | static inline struct fimc_vid_buffer *fimc_pending_queue_pop( | ||
722 | struct fimc_vid_cap *vid_cap) | ||
723 | { | ||
724 | struct fimc_vid_buffer *buf; | ||
725 | buf = list_entry(vid_cap->pending_buf_q.next, | ||
726 | struct fimc_vid_buffer, list); | ||
727 | list_del(&buf->list); | ||
728 | return buf; | ||
729 | } | ||
730 | |||
731 | #endif /* FIMC_CORE_H_ */ | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-is-command.h b/drivers/media/platform/exynos4-is/fimc-is-command.h new file mode 100644 index 000000000000..0d1f52e394b1 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-command.h | |||
@@ -0,0 +1,137 @@ | |||
1 | /* | ||
2 | * Samsung Exynos4x12 FIMC-IS (Imaging Subsystem) driver | ||
3 | * | ||
4 | * FIMC-IS command set definitions | ||
5 | * | ||
6 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
7 | * | ||
8 | * Authors: Younghwan Joo <yhwan.joo@samsung.com> | ||
9 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | */ | ||
15 | |||
16 | #ifndef FIMC_IS_CMD_H_ | ||
17 | #define FIMC_IS_CMD_H_ | ||
18 | |||
19 | #define FIMC_IS_COMMAND_VER 110 /* FIMC-IS command set version 1.10 */ | ||
20 | |||
21 | /* Enumeration of commands beetween the FIMC-IS and the host processor. */ | ||
22 | |||
23 | /* HOST to FIMC-IS */ | ||
24 | #define HIC_PREVIEW_STILL 0x0001 | ||
25 | #define HIC_PREVIEW_VIDEO 0x0002 | ||
26 | #define HIC_CAPTURE_STILL 0x0003 | ||
27 | #define HIC_CAPTURE_VIDEO 0x0004 | ||
28 | #define HIC_STREAM_ON 0x0005 | ||
29 | #define HIC_STREAM_OFF 0x0006 | ||
30 | #define HIC_SET_PARAMETER 0x0007 | ||
31 | #define HIC_GET_PARAMETER 0x0008 | ||
32 | #define HIC_SET_TUNE 0x0009 | ||
33 | #define HIC_GET_STATUS 0x000b | ||
34 | /* Sensor part */ | ||
35 | #define HIC_OPEN_SENSOR 0x000c | ||
36 | #define HIC_CLOSE_SENSOR 0x000d | ||
37 | #define HIC_SIMMIAN_INIT 0x000e | ||
38 | #define HIC_SIMMIAN_WRITE 0x000f | ||
39 | #define HIC_SIMMIAN_READ 0x0010 | ||
40 | #define HIC_POWER_DOWN 0x0011 | ||
41 | #define HIC_GET_SET_FILE_ADDR 0x0012 | ||
42 | #define HIC_LOAD_SET_FILE 0x0013 | ||
43 | #define HIC_MSG_CONFIG 0x0014 | ||
44 | #define HIC_MSG_TEST 0x0015 | ||
45 | /* FIMC-IS to HOST */ | ||
46 | #define IHC_GET_SENSOR_NUM 0x1000 | ||
47 | #define IHC_SET_SHOT_MARK 0x1001 | ||
48 | /* parameter1: frame number */ | ||
49 | /* parameter2: confidence level (smile 0~100) */ | ||
50 | /* parameter3: confidence level (blink 0~100) */ | ||
51 | #define IHC_SET_FACE_MARK 0x1002 | ||
52 | /* parameter1: coordinate count */ | ||
53 | /* parameter2: coordinate buffer address */ | ||
54 | #define IHC_FRAME_DONE 0x1003 | ||
55 | /* parameter1: frame start number */ | ||
56 | /* parameter2: frame count */ | ||
57 | #define IHC_AA_DONE 0x1004 | ||
58 | #define IHC_NOT_READY 0x1005 | ||
59 | |||
60 | #define IH_REPLY_DONE 0x2000 | ||
61 | #define IH_REPLY_NOT_DONE 0x2001 | ||
62 | |||
63 | enum fimc_is_scenario { | ||
64 | IS_SC_PREVIEW_STILL, | ||
65 | IS_SC_PREVIEW_VIDEO, | ||
66 | IS_SC_CAPTURE_STILL, | ||
67 | IS_SC_CAPTURE_VIDEO, | ||
68 | IS_SC_MAX | ||
69 | }; | ||
70 | |||
71 | enum fimc_is_sub_scenario { | ||
72 | IS_SC_SUB_DEFAULT, | ||
73 | IS_SC_SUB_PS_VTCALL, | ||
74 | IS_SC_SUB_CS_VTCALL, | ||
75 | IS_SC_SUB_PV_VTCALL, | ||
76 | IS_SC_SUB_CV_VTCALL, | ||
77 | }; | ||
78 | |||
79 | struct is_common_regs { | ||
80 | u32 hicmd; | ||
81 | u32 hic_sensorid; | ||
82 | u32 hic_param[4]; | ||
83 | u32 reserved1[4]; | ||
84 | |||
85 | u32 ihcmd; | ||
86 | u32 ihc_sensorid; | ||
87 | u32 ihc_param[4]; | ||
88 | u32 reserved2[4]; | ||
89 | |||
90 | u32 isp_sensor_id; | ||
91 | u32 isp_param[2]; | ||
92 | u32 reserved3[1]; | ||
93 | |||
94 | u32 scc_sensor_id; | ||
95 | u32 scc_param[2]; | ||
96 | u32 reserved4[1]; | ||
97 | |||
98 | u32 dnr_sensor_id; | ||
99 | u32 dnr_param[2]; | ||
100 | u32 reserved5[1]; | ||
101 | |||
102 | u32 scp_sensor_id; | ||
103 | u32 scp_param[2]; | ||
104 | u32 reserved6[29]; | ||
105 | } __packed; | ||
106 | |||
107 | struct is_mcuctl_reg { | ||
108 | u32 mcuctl; | ||
109 | u32 bboar; | ||
110 | |||
111 | u32 intgr0; | ||
112 | u32 intcr0; | ||
113 | u32 intmr0; | ||
114 | u32 intsr0; | ||
115 | u32 intmsr0; | ||
116 | |||
117 | u32 intgr1; | ||
118 | u32 intcr1; | ||
119 | u32 intmr1; | ||
120 | u32 intsr1; | ||
121 | u32 intmsr1; | ||
122 | |||
123 | u32 intcr2; | ||
124 | u32 intmr2; | ||
125 | u32 intsr2; | ||
126 | u32 intmsr2; | ||
127 | |||
128 | u32 gpoctrl; | ||
129 | u32 cpoenctlr; | ||
130 | u32 gpictlr; | ||
131 | |||
132 | u32 reserved[0xd]; | ||
133 | |||
134 | struct is_common_regs common; | ||
135 | } __packed; | ||
136 | |||
137 | #endif /* FIMC_IS_CMD_H_ */ | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.c b/drivers/media/platform/exynos4-is/fimc-is-errno.c new file mode 100644 index 000000000000..e8519e151c1a --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-errno.c | |||
@@ -0,0 +1,272 @@ | |||
1 | /* | ||
2 | * Samsung Exynos4 SoC series FIMC-IS slave interface driver | ||
3 | * | ||
4 | * Error log interface functions | ||
5 | * | ||
6 | * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. | ||
7 | * | ||
8 | * Authors: Younghwan Joo <yhwan.joo@samsung.com> | ||
9 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | */ | ||
15 | |||
16 | #include "fimc-is-errno.h" | ||
17 | |||
18 | const char * const fimc_is_param_strerr(unsigned int error) | ||
19 | { | ||
20 | switch (error) { | ||
21 | case ERROR_COMMON_CMD: | ||
22 | return "ERROR_COMMON_CMD: Invalid Command"; | ||
23 | case ERROR_COMMON_PARAMETER: | ||
24 | return "ERROR_COMMON_PARAMETER: Invalid Parameter"; | ||
25 | case ERROR_COMMON_SETFILE_LOAD: | ||
26 | return "ERROR_COMMON_SETFILE_LOAD: Illegal Setfile Loading"; | ||
27 | case ERROR_COMMON_SETFILE_ADJUST: | ||
28 | return "ERROR_COMMON_SETFILE_ADJUST: Setfile isn't adjusted"; | ||
29 | case ERROR_COMMON_SETFILE_INDEX: | ||
30 | return "ERROR_COMMON_SETFILE_INDEX: Invalid setfile index"; | ||
31 | case ERROR_COMMON_INPUT_PATH: | ||
32 | return "ERROR_COMMON_INPUT_PATH: Input path can be changed in ready state"; | ||
33 | case ERROR_COMMON_INPUT_INIT: | ||
34 | return "ERROR_COMMON_INPUT_INIT: IP can not start if input path is not set"; | ||
35 | case ERROR_COMMON_OUTPUT_PATH: | ||
36 | return "ERROR_COMMON_OUTPUT_PATH: Output path can be changed in ready state (stop)"; | ||
37 | case ERROR_COMMON_OUTPUT_INIT: | ||
38 | return "ERROR_COMMON_OUTPUT_INIT: IP can not start if output path is not set"; | ||
39 | case ERROR_CONTROL_BYPASS: | ||
40 | return "ERROR_CONTROL_BYPASS"; | ||
41 | case ERROR_OTF_INPUT_FORMAT: | ||
42 | return "ERROR_OTF_INPUT_FORMAT: Invalid format (DRC: YUV444, FD: YUV444, 422, 420)"; | ||
43 | case ERROR_OTF_INPUT_WIDTH: | ||
44 | return "ERROR_OTF_INPUT_WIDTH: Invalid width (DRC: 128~8192, FD: 32~8190)"; | ||
45 | case ERROR_OTF_INPUT_HEIGHT: | ||
46 | return "ERROR_OTF_INPUT_HEIGHT: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; | ||
47 | case ERROR_OTF_INPUT_BIT_WIDTH: | ||
48 | return "ERROR_OTF_INPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; | ||
49 | case ERROR_DMA_INPUT_WIDTH: | ||
50 | return "ERROR_DMA_INPUT_WIDTH: Invalid width (DRC: 128~8192, FD: 32~8190)"; | ||
51 | case ERROR_DMA_INPUT_HEIGHT: | ||
52 | return "ERROR_DMA_INPUT_HEIGHT: Invalid height (DRC: 64~8192, FD: 16~8190)"; | ||
53 | case ERROR_DMA_INPUT_FORMAT: | ||
54 | return "ERROR_DMA_INPUT_FORMAT: Invalid format (DRC: YUV444 or YUV422, FD: YUV444,422,420)"; | ||
55 | case ERROR_DMA_INPUT_BIT_WIDTH: | ||
56 | return "ERROR_DMA_INPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; | ||
57 | case ERROR_DMA_INPUT_ORDER: | ||
58 | return "ERROR_DMA_INPUT_ORDER: Invalid order(DRC: YYCbCr,YCbYCr,FD:NO,YYCbCr,YCbYCr,CbCr,CrCb)"; | ||
59 | case ERROR_DMA_INPUT_PLANE: | ||
60 | return "ERROR_DMA_INPUT_PLANE: Invalid palne (DRC: 3, FD: 1, 2, 3)"; | ||
61 | case ERROR_OTF_OUTPUT_WIDTH: | ||
62 | return "ERROR_OTF_OUTPUT_WIDTH: Invalid width (DRC: 128~8192)"; | ||
63 | case ERROR_OTF_OUTPUT_HEIGHT: | ||
64 | return "ERROR_OTF_OUTPUT_HEIGHT: Invalid height (DRC: 64~8192)"; | ||
65 | case ERROR_OTF_OUTPUT_FORMAT: | ||
66 | return "ERROR_OTF_OUTPUT_FORMAT: Invalid format (DRC: YUV444)"; | ||
67 | case ERROR_OTF_OUTPUT_BIT_WIDTH: | ||
68 | return "ERROR_OTF_OUTPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; | ||
69 | case ERROR_DMA_OUTPUT_WIDTH: | ||
70 | return "ERROR_DMA_OUTPUT_WIDTH"; | ||
71 | case ERROR_DMA_OUTPUT_HEIGHT: | ||
72 | return "ERROR_DMA_OUTPUT_HEIGHT"; | ||
73 | case ERROR_DMA_OUTPUT_FORMAT: | ||
74 | return "ERROR_DMA_OUTPUT_FORMAT"; | ||
75 | case ERROR_DMA_OUTPUT_BIT_WIDTH: | ||
76 | return "ERROR_DMA_OUTPUT_BIT_WIDTH"; | ||
77 | case ERROR_DMA_OUTPUT_PLANE: | ||
78 | return "ERROR_DMA_OUTPUT_PLANE"; | ||
79 | case ERROR_DMA_OUTPUT_ORDER: | ||
80 | return "ERROR_DMA_OUTPUT_ORDER"; | ||
81 | |||
82 | /* Sensor Error(100~199) */ | ||
83 | case ERROR_SENSOR_I2C_FAIL: | ||
84 | return "ERROR_SENSOR_I2C_FAIL"; | ||
85 | case ERROR_SENSOR_INVALID_FRAMERATE: | ||
86 | return "ERROR_SENSOR_INVALID_FRAMERATE"; | ||
87 | case ERROR_SENSOR_INVALID_EXPOSURETIME: | ||
88 | return "ERROR_SENSOR_INVALID_EXPOSURETIME"; | ||
89 | case ERROR_SENSOR_INVALID_SIZE: | ||
90 | return "ERROR_SENSOR_INVALID_SIZE"; | ||
91 | case ERROR_SENSOR_INVALID_SETTING: | ||
92 | return "ERROR_SENSOR_INVALID_SETTING"; | ||
93 | case ERROR_SENSOR_ACTURATOR_INIT_FAIL: | ||
94 | return "ERROR_SENSOR_ACTURATOR_INIT_FAIL"; | ||
95 | case ERROR_SENSOR_INVALID_AF_POS: | ||
96 | return "ERROR_SENSOR_INVALID_AF_POS"; | ||
97 | case ERROR_SENSOR_UNSUPPORT_FUNC: | ||
98 | return "ERROR_SENSOR_UNSUPPORT_FUNC"; | ||
99 | case ERROR_SENSOR_UNSUPPORT_PERI: | ||
100 | return "ERROR_SENSOR_UNSUPPORT_PERI"; | ||
101 | case ERROR_SENSOR_UNSUPPORT_AF: | ||
102 | return "ERROR_SENSOR_UNSUPPORT_AF"; | ||
103 | |||
104 | /* ISP Error (200~299) */ | ||
105 | case ERROR_ISP_AF_BUSY: | ||
106 | return "ERROR_ISP_AF_BUSY"; | ||
107 | case ERROR_ISP_AF_INVALID_COMMAND: | ||
108 | return "ERROR_ISP_AF_INVALID_COMMAND"; | ||
109 | case ERROR_ISP_AF_INVALID_MODE: | ||
110 | return "ERROR_ISP_AF_INVALID_MODE"; | ||
111 | |||
112 | /* DRC Error (300~399) */ | ||
113 | /* FD Error (400~499) */ | ||
114 | case ERROR_FD_CONFIG_MAX_NUMBER_STATE: | ||
115 | return "ERROR_FD_CONFIG_MAX_NUMBER_STATE"; | ||
116 | case ERROR_FD_CONFIG_MAX_NUMBER_INVALID: | ||
117 | return "ERROR_FD_CONFIG_MAX_NUMBER_INVALID"; | ||
118 | case ERROR_FD_CONFIG_YAW_ANGLE_STATE: | ||
119 | return "ERROR_FD_CONFIG_YAW_ANGLE_STATE"; | ||
120 | case ERROR_FD_CONFIG_YAW_ANGLE_INVALID: | ||
121 | return "ERROR_FD_CONFIG_YAW_ANGLE_INVALID\n"; | ||
122 | case ERROR_FD_CONFIG_ROLL_ANGLE_STATE: | ||
123 | return "ERROR_FD_CONFIG_ROLL_ANGLE_STATE"; | ||
124 | case ERROR_FD_CONFIG_ROLL_ANGLE_INVALID: | ||
125 | return "ERROR_FD_CONFIG_ROLL_ANGLE_INVALID"; | ||
126 | case ERROR_FD_CONFIG_SMILE_MODE_INVALID: | ||
127 | return "ERROR_FD_CONFIG_SMILE_MODE_INVALID"; | ||
128 | case ERROR_FD_CONFIG_BLINK_MODE_INVALID: | ||
129 | return "ERROR_FD_CONFIG_BLINK_MODE_INVALID"; | ||
130 | case ERROR_FD_CONFIG_EYES_DETECT_INVALID: | ||
131 | return "ERROR_FD_CONFIG_EYES_DETECT_INVALID"; | ||
132 | case ERROR_FD_CONFIG_MOUTH_DETECT_INVALID: | ||
133 | return "ERROR_FD_CONFIG_MOUTH_DETECT_INVALID"; | ||
134 | case ERROR_FD_CONFIG_ORIENTATION_STATE: | ||
135 | return "ERROR_FD_CONFIG_ORIENTATION_STATE"; | ||
136 | case ERROR_FD_CONFIG_ORIENTATION_INVALID: | ||
137 | return "ERROR_FD_CONFIG_ORIENTATION_INVALID"; | ||
138 | case ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID: | ||
139 | return "ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID"; | ||
140 | case ERROR_FD_RESULT: | ||
141 | return "ERROR_FD_RESULT"; | ||
142 | case ERROR_FD_MODE: | ||
143 | return "ERROR_FD_MODE"; | ||
144 | default: | ||
145 | return "Unknown"; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | const char * const fimc_is_strerr(unsigned int error) | ||
150 | { | ||
151 | error &= ~IS_ERROR_TIME_OUT_FLAG; | ||
152 | |||
153 | switch (error) { | ||
154 | /* General */ | ||
155 | case IS_ERROR_INVALID_COMMAND: | ||
156 | return "IS_ERROR_INVALID_COMMAND"; | ||
157 | case IS_ERROR_REQUEST_FAIL: | ||
158 | return "IS_ERROR_REQUEST_FAIL"; | ||
159 | case IS_ERROR_INVALID_SCENARIO: | ||
160 | return "IS_ERROR_INVALID_SCENARIO"; | ||
161 | case IS_ERROR_INVALID_SENSORID: | ||
162 | return "IS_ERROR_INVALID_SENSORID"; | ||
163 | case IS_ERROR_INVALID_MODE_CHANGE: | ||
164 | return "IS_ERROR_INVALID_MODE_CHANGE"; | ||
165 | case IS_ERROR_INVALID_MAGIC_NUMBER: | ||
166 | return "IS_ERROR_INVALID_MAGIC_NUMBER"; | ||
167 | case IS_ERROR_INVALID_SETFILE_HDR: | ||
168 | return "IS_ERROR_INVALID_SETFILE_HDR"; | ||
169 | case IS_ERROR_BUSY: | ||
170 | return "IS_ERROR_BUSY"; | ||
171 | case IS_ERROR_SET_PARAMETER: | ||
172 | return "IS_ERROR_SET_PARAMETER"; | ||
173 | case IS_ERROR_INVALID_PATH: | ||
174 | return "IS_ERROR_INVALID_PATH"; | ||
175 | case IS_ERROR_OPEN_SENSOR_FAIL: | ||
176 | return "IS_ERROR_OPEN_SENSOR_FAIL"; | ||
177 | case IS_ERROR_ENTRY_MSG_THREAD_DOWN: | ||
178 | return "IS_ERROR_ENTRY_MSG_THREAD_DOWN"; | ||
179 | case IS_ERROR_ISP_FRAME_END_NOT_DONE: | ||
180 | return "IS_ERROR_ISP_FRAME_END_NOT_DONE"; | ||
181 | case IS_ERROR_DRC_FRAME_END_NOT_DONE: | ||
182 | return "IS_ERROR_DRC_FRAME_END_NOT_DONE"; | ||
183 | case IS_ERROR_SCALERC_FRAME_END_NOT_DONE: | ||
184 | return "IS_ERROR_SCALERC_FRAME_END_NOT_DONE"; | ||
185 | case IS_ERROR_ODC_FRAME_END_NOT_DONE: | ||
186 | return "IS_ERROR_ODC_FRAME_END_NOT_DONE"; | ||
187 | case IS_ERROR_DIS_FRAME_END_NOT_DONE: | ||
188 | return "IS_ERROR_DIS_FRAME_END_NOT_DONE"; | ||
189 | case IS_ERROR_TDNR_FRAME_END_NOT_DONE: | ||
190 | return "IS_ERROR_TDNR_FRAME_END_NOT_DONE"; | ||
191 | case IS_ERROR_SCALERP_FRAME_END_NOT_DONE: | ||
192 | return "IS_ERROR_SCALERP_FRAME_END_NOT_DONE"; | ||
193 | case IS_ERROR_WAIT_STREAM_OFF_NOT_DONE: | ||
194 | return "IS_ERROR_WAIT_STREAM_OFF_NOT_DONE"; | ||
195 | case IS_ERROR_NO_MSG_IS_RECEIVED: | ||
196 | return "IS_ERROR_NO_MSG_IS_RECEIVED"; | ||
197 | case IS_ERROR_SENSOR_MSG_FAIL: | ||
198 | return "IS_ERROR_SENSOR_MSG_FAIL"; | ||
199 | case IS_ERROR_ISP_MSG_FAIL: | ||
200 | return "IS_ERROR_ISP_MSG_FAIL"; | ||
201 | case IS_ERROR_DRC_MSG_FAIL: | ||
202 | return "IS_ERROR_DRC_MSG_FAIL"; | ||
203 | case IS_ERROR_LHFD_MSG_FAIL: | ||
204 | return "IS_ERROR_LHFD_MSG_FAIL"; | ||
205 | case IS_ERROR_UNKNOWN: | ||
206 | return "IS_ERROR_UNKNOWN"; | ||
207 | |||
208 | /* Sensor */ | ||
209 | case IS_ERROR_SENSOR_PWRDN_FAIL: | ||
210 | return "IS_ERROR_SENSOR_PWRDN_FAIL"; | ||
211 | |||
212 | /* ISP */ | ||
213 | case IS_ERROR_ISP_PWRDN_FAIL: | ||
214 | return "IS_ERROR_ISP_PWRDN_FAIL"; | ||
215 | case IS_ERROR_ISP_MULTIPLE_INPUT: | ||
216 | return "IS_ERROR_ISP_MULTIPLE_INPUT"; | ||
217 | case IS_ERROR_ISP_ABSENT_INPUT: | ||
218 | return "IS_ERROR_ISP_ABSENT_INPUT"; | ||
219 | case IS_ERROR_ISP_ABSENT_OUTPUT: | ||
220 | return "IS_ERROR_ISP_ABSENT_OUTPUT"; | ||
221 | case IS_ERROR_ISP_NONADJACENT_OUTPUT: | ||
222 | return "IS_ERROR_ISP_NONADJACENT_OUTPUT"; | ||
223 | case IS_ERROR_ISP_FORMAT_MISMATCH: | ||
224 | return "IS_ERROR_ISP_FORMAT_MISMATCH"; | ||
225 | case IS_ERROR_ISP_WIDTH_MISMATCH: | ||
226 | return "IS_ERROR_ISP_WIDTH_MISMATCH"; | ||
227 | case IS_ERROR_ISP_HEIGHT_MISMATCH: | ||
228 | return "IS_ERROR_ISP_HEIGHT_MISMATCH"; | ||
229 | case IS_ERROR_ISP_BITWIDTH_MISMATCH: | ||
230 | return "IS_ERROR_ISP_BITWIDTH_MISMATCH"; | ||
231 | case IS_ERROR_ISP_FRAME_END_TIME_OUT: | ||
232 | return "IS_ERROR_ISP_FRAME_END_TIME_OUT"; | ||
233 | |||
234 | /* DRC */ | ||
235 | case IS_ERROR_DRC_PWRDN_FAIL: | ||
236 | return "IS_ERROR_DRC_PWRDN_FAIL"; | ||
237 | case IS_ERROR_DRC_MULTIPLE_INPUT: | ||
238 | return "IS_ERROR_DRC_MULTIPLE_INPUT"; | ||
239 | case IS_ERROR_DRC_ABSENT_INPUT: | ||
240 | return "IS_ERROR_DRC_ABSENT_INPUT"; | ||
241 | case IS_ERROR_DRC_NONADJACENT_INPUT: | ||
242 | return "IS_ERROR_DRC_NONADJACENT_INPUT"; | ||
243 | case IS_ERROR_DRC_ABSENT_OUTPUT: | ||
244 | return "IS_ERROR_DRC_ABSENT_OUTPUT"; | ||
245 | case IS_ERROR_DRC_NONADJACENT_OUTPUT: | ||
246 | return "IS_ERROR_DRC_NONADJACENT_OUTPUT"; | ||
247 | case IS_ERROR_DRC_FORMAT_MISMATCH: | ||
248 | return "IS_ERROR_DRC_FORMAT_MISMATCH"; | ||
249 | case IS_ERROR_DRC_WIDTH_MISMATCH: | ||
250 | return "IS_ERROR_DRC_WIDTH_MISMATCH"; | ||
251 | case IS_ERROR_DRC_HEIGHT_MISMATCH: | ||
252 | return "IS_ERROR_DRC_HEIGHT_MISMATCH"; | ||
253 | case IS_ERROR_DRC_BITWIDTH_MISMATCH: | ||
254 | return "IS_ERROR_DRC_BITWIDTH_MISMATCH"; | ||
255 | case IS_ERROR_DRC_FRAME_END_TIME_OUT: | ||
256 | return "IS_ERROR_DRC_FRAME_END_TIME_OUT"; | ||
257 | |||
258 | /* FD */ | ||
259 | case IS_ERROR_FD_PWRDN_FAIL: | ||
260 | return "IS_ERROR_FD_PWRDN_FAIL"; | ||
261 | case IS_ERROR_FD_MULTIPLE_INPUT: | ||
262 | return "IS_ERROR_FD_MULTIPLE_INPUT"; | ||
263 | case IS_ERROR_FD_ABSENT_INPUT: | ||
264 | return "IS_ERROR_FD_ABSENT_INPUT"; | ||
265 | case IS_ERROR_FD_NONADJACENT_INPUT: | ||
266 | return "IS_ERROR_FD_NONADJACENT_INPUT"; | ||
267 | case IS_ERROR_LHFD_FRAME_END_TIME_OUT: | ||
268 | return "IS_ERROR_LHFD_FRAME_END_TIME_OUT"; | ||
269 | default: | ||
270 | return "Unknown"; | ||
271 | } | ||
272 | } | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.h b/drivers/media/platform/exynos4-is/fimc-is-errno.h new file mode 100644 index 000000000000..3de6f6da6f87 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-errno.h | |||
@@ -0,0 +1,248 @@ | |||
1 | /* | ||
2 | * Samsung Exynos4 SoC series FIMC-IS slave interface driver | ||
3 | * | ||
4 | * FIMC-IS error code definition | ||
5 | * | ||
6 | * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. | ||
7 | * | ||
8 | * Authors: Younghwan Joo <yhwan.joo@samsung.com> | ||
9 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | */ | ||
15 | |||
16 | #ifndef FIMC_IS_ERR_H_ | ||
17 | #define FIMC_IS_ERR_H_ | ||
18 | |||
19 | #define IS_ERROR_VER 011 /* IS ERROR VERSION 0.11 */ | ||
20 | |||
21 | enum { | ||
22 | IS_ERROR_NONE, | ||
23 | |||
24 | /* General 1 ~ 99 */ | ||
25 | IS_ERROR_INVALID_COMMAND, | ||
26 | IS_ERROR_REQUEST_FAIL, | ||
27 | IS_ERROR_INVALID_SCENARIO, | ||
28 | IS_ERROR_INVALID_SENSORID, | ||
29 | IS_ERROR_INVALID_MODE_CHANGE, | ||
30 | IS_ERROR_INVALID_MAGIC_NUMBER, | ||
31 | IS_ERROR_INVALID_SETFILE_HDR, | ||
32 | IS_ERROR_BUSY, | ||
33 | IS_ERROR_SET_PARAMETER, | ||
34 | IS_ERROR_INVALID_PATH, | ||
35 | IS_ERROR_OPEN_SENSOR_FAIL, | ||
36 | IS_ERROR_ENTRY_MSG_THREAD_DOWN, | ||
37 | IS_ERROR_ISP_FRAME_END_NOT_DONE, | ||
38 | IS_ERROR_DRC_FRAME_END_NOT_DONE, | ||
39 | IS_ERROR_SCALERC_FRAME_END_NOT_DONE, | ||
40 | IS_ERROR_ODC_FRAME_END_NOT_DONE, | ||
41 | IS_ERROR_DIS_FRAME_END_NOT_DONE, | ||
42 | IS_ERROR_TDNR_FRAME_END_NOT_DONE, | ||
43 | IS_ERROR_SCALERP_FRAME_END_NOT_DONE, | ||
44 | IS_ERROR_WAIT_STREAM_OFF_NOT_DONE, | ||
45 | IS_ERROR_NO_MSG_IS_RECEIVED, | ||
46 | IS_ERROR_SENSOR_MSG_FAIL, | ||
47 | IS_ERROR_ISP_MSG_FAIL, | ||
48 | IS_ERROR_DRC_MSG_FAIL, | ||
49 | IS_ERROR_SCALERC_MSG_FAIL, | ||
50 | IS_ERROR_ODC_MSG_FAIL, | ||
51 | IS_ERROR_DIS_MSG_FAIL, | ||
52 | IS_ERROR_TDNR_MSG_FAIL, | ||
53 | IS_ERROR_SCALERP_MSG_FAIL, | ||
54 | IS_ERROR_LHFD_MSG_FAIL, | ||
55 | IS_ERROR_LHFD_INTERNAL_STOP, | ||
56 | |||
57 | /* Sensor 100 ~ 199 */ | ||
58 | IS_ERROR_SENSOR_PWRDN_FAIL = 100, | ||
59 | IS_ERROR_SENSOR_STREAM_ON_FAIL, | ||
60 | IS_ERROR_SENSOR_STREAM_OFF_FAIL, | ||
61 | |||
62 | /* ISP 200 ~ 299 */ | ||
63 | IS_ERROR_ISP_PWRDN_FAIL = 200, | ||
64 | IS_ERROR_ISP_MULTIPLE_INPUT, | ||
65 | IS_ERROR_ISP_ABSENT_INPUT, | ||
66 | IS_ERROR_ISP_ABSENT_OUTPUT, | ||
67 | IS_ERROR_ISP_NONADJACENT_OUTPUT, | ||
68 | IS_ERROR_ISP_FORMAT_MISMATCH, | ||
69 | IS_ERROR_ISP_WIDTH_MISMATCH, | ||
70 | IS_ERROR_ISP_HEIGHT_MISMATCH, | ||
71 | IS_ERROR_ISP_BITWIDTH_MISMATCH, | ||
72 | IS_ERROR_ISP_FRAME_END_TIME_OUT, | ||
73 | |||
74 | /* DRC 300 ~ 399 */ | ||
75 | IS_ERROR_DRC_PWRDN_FAIL = 300, | ||
76 | IS_ERROR_DRC_MULTIPLE_INPUT, | ||
77 | IS_ERROR_DRC_ABSENT_INPUT, | ||
78 | IS_ERROR_DRC_NONADJACENT_INPUT, | ||
79 | IS_ERROR_DRC_ABSENT_OUTPUT, | ||
80 | IS_ERROR_DRC_NONADJACENT_OUTPUT, | ||
81 | IS_ERROR_DRC_FORMAT_MISMATCH, | ||
82 | IS_ERROR_DRC_WIDTH_MISMATCH, | ||
83 | IS_ERROR_DRC_HEIGHT_MISMATCH, | ||
84 | IS_ERROR_DRC_BITWIDTH_MISMATCH, | ||
85 | IS_ERROR_DRC_FRAME_END_TIME_OUT, | ||
86 | |||
87 | /* SCALERC 400 ~ 499 */ | ||
88 | IS_ERROR_SCALERC_PWRDN_FAIL = 400, | ||
89 | |||
90 | /* ODC 500 ~ 599 */ | ||
91 | IS_ERROR_ODC_PWRDN_FAIL = 500, | ||
92 | |||
93 | /* DIS 600 ~ 699 */ | ||
94 | IS_ERROR_DIS_PWRDN_FAIL = 600, | ||
95 | |||
96 | /* TDNR 700 ~ 799 */ | ||
97 | IS_ERROR_TDNR_PWRDN_FAIL = 700, | ||
98 | |||
99 | /* SCALERC 800 ~ 899 */ | ||
100 | IS_ERROR_SCALERP_PWRDN_FAIL = 800, | ||
101 | |||
102 | /* FD 900 ~ 999 */ | ||
103 | IS_ERROR_FD_PWRDN_FAIL = 900, | ||
104 | IS_ERROR_FD_MULTIPLE_INPUT, | ||
105 | IS_ERROR_FD_ABSENT_INPUT, | ||
106 | IS_ERROR_FD_NONADJACENT_INPUT, | ||
107 | IS_ERROR_LHFD_FRAME_END_TIME_OUT, | ||
108 | |||
109 | IS_ERROR_UNKNOWN = 1000, | ||
110 | }; | ||
111 | |||
112 | #define IS_ERROR_TIME_OUT_FLAG 0x80000000 | ||
113 | |||
114 | /* Set parameter error enum */ | ||
115 | enum fimc_is_error { | ||
116 | /* Common error (0~99) */ | ||
117 | ERROR_COMMON_NONE = 0, | ||
118 | ERROR_COMMON_CMD = 1, /* Invalid command */ | ||
119 | ERROR_COMMON_PARAMETER = 2, /* Invalid parameter */ | ||
120 | /* setfile is not loaded before adjusting */ | ||
121 | ERROR_COMMON_SETFILE_LOAD = 3, | ||
122 | /* setfile is not Adjusted before runnng. */ | ||
123 | ERROR_COMMON_SETFILE_ADJUST = 4, | ||
124 | /* Index of setfile is not valid (0~MAX_SETFILE_NUM-1) */ | ||
125 | ERROR_COMMON_SETFILE_INDEX = 5, | ||
126 | /* Input path can be changed in ready state(stop) */ | ||
127 | ERROR_COMMON_INPUT_PATH = 6, | ||
128 | /* IP can not start if input path is not set */ | ||
129 | ERROR_COMMON_INPUT_INIT = 7, | ||
130 | /* Output path can be changed in ready state (stop) */ | ||
131 | ERROR_COMMON_OUTPUT_PATH = 8, | ||
132 | /* IP can not start if output path is not set */ | ||
133 | ERROR_COMMON_OUTPUT_INIT = 9, | ||
134 | |||
135 | ERROR_CONTROL_NONE = ERROR_COMMON_NONE, | ||
136 | ERROR_CONTROL_BYPASS = 11, /* Enable or Disable */ | ||
137 | |||
138 | ERROR_OTF_INPUT_NONE = ERROR_COMMON_NONE, | ||
139 | ERROR_OTF_INPUT_CMD = 21, | ||
140 | /* invalid format (DRC: YUV444, FD: YUV444, 422, 420) */ | ||
141 | ERROR_OTF_INPUT_FORMAT = 22, | ||
142 | /* invalid width (DRC: 128~8192, FD: 32~8190) */ | ||
143 | ERROR_OTF_INPUT_WIDTH = 23, | ||
144 | /* invalid height (DRC: 64~8192, FD: 16~8190) */ | ||
145 | ERROR_OTF_INPUT_HEIGHT = 24, | ||
146 | /* invalid bit-width (DRC: 8~12bits, FD: 8bit) */ | ||
147 | ERROR_OTF_INPUT_BIT_WIDTH = 25, | ||
148 | /* invalid FrameTime for ISP */ | ||
149 | ERROR_OTF_INPUT_USER_FRAMETIIME = 26, | ||
150 | |||
151 | ERROR_DMA_INPUT_NONE = ERROR_COMMON_NONE, | ||
152 | /* invalid width (DRC: 128~8192, FD: 32~8190) */ | ||
153 | ERROR_DMA_INPUT_WIDTH = 31, | ||
154 | /* invalid height (DRC: 64~8192, FD: 16~8190) */ | ||
155 | ERROR_DMA_INPUT_HEIGHT = 32, | ||
156 | /* invalid format (DRC: YUV444 or YUV422, FD: YUV444, 422, 420) */ | ||
157 | ERROR_DMA_INPUT_FORMAT = 33, | ||
158 | /* invalid bit-width (DRC: 8~12bit, FD: 8bit) */ | ||
159 | ERROR_DMA_INPUT_BIT_WIDTH = 34, | ||
160 | /* invalid order(DRC: YYCbCrorYCbYCr, FD:NO,YYCbCr,YCbYCr,CbCr,CrCb) */ | ||
161 | ERROR_DMA_INPUT_ORDER = 35, | ||
162 | /* invalid palne (DRC: 3, FD: 1, 2, 3) */ | ||
163 | ERROR_DMA_INPUT_PLANE = 36, | ||
164 | |||
165 | ERROR_OTF_OUTPUT_NONE = ERROR_COMMON_NONE, | ||
166 | /* invalid width (DRC: 128~8192) */ | ||
167 | ERROR_OTF_OUTPUT_WIDTH = 41, | ||
168 | /* invalid height (DRC: 64~8192) */ | ||
169 | ERROR_OTF_OUTPUT_HEIGHT = 42, | ||
170 | /* invalid format (DRC: YUV444) */ | ||
171 | ERROR_OTF_OUTPUT_FORMAT = 43, | ||
172 | /* invalid bit-width (DRC: 8~12bits) */ | ||
173 | ERROR_OTF_OUTPUT_BIT_WIDTH = 44, | ||
174 | |||
175 | ERROR_DMA_OUTPUT_NONE = ERROR_COMMON_NONE, | ||
176 | ERROR_DMA_OUTPUT_WIDTH = 51, /* invalid width */ | ||
177 | ERROR_DMA_OUTPUT_HEIGHT = 52, /* invalid height */ | ||
178 | ERROR_DMA_OUTPUT_FORMAT = 53, /* invalid format */ | ||
179 | ERROR_DMA_OUTPUT_BIT_WIDTH = 54, /* invalid bit-width */ | ||
180 | ERROR_DMA_OUTPUT_PLANE = 55, /* invalid plane */ | ||
181 | ERROR_DMA_OUTPUT_ORDER = 56, /* invalid order */ | ||
182 | |||
183 | ERROR_GLOBAL_SHOTMODE_NONE = ERROR_COMMON_NONE, | ||
184 | |||
185 | /* SENSOR Error(100~199) */ | ||
186 | ERROR_SENSOR_NONE = ERROR_COMMON_NONE, | ||
187 | ERROR_SENSOR_I2C_FAIL = 101, | ||
188 | ERROR_SENSOR_INVALID_FRAMERATE, | ||
189 | ERROR_SENSOR_INVALID_EXPOSURETIME, | ||
190 | ERROR_SENSOR_INVALID_SIZE, | ||
191 | ERROR_SENSOR_INVALID_SETTING, | ||
192 | ERROR_SENSOR_ACTURATOR_INIT_FAIL, | ||
193 | ERROR_SENSOR_INVALID_AF_POS, | ||
194 | ERROR_SENSOR_UNSUPPORT_FUNC, | ||
195 | ERROR_SENSOR_UNSUPPORT_PERI, | ||
196 | ERROR_SENSOR_UNSUPPORT_AF, | ||
197 | |||
198 | /* ISP Error (200~299) */ | ||
199 | ERROR_ISP_AF_NONE = ERROR_COMMON_NONE, | ||
200 | ERROR_ISP_AF_BUSY = 201, | ||
201 | ERROR_ISP_AF_INVALID_COMMAND = 202, | ||
202 | ERROR_ISP_AF_INVALID_MODE = 203, | ||
203 | ERROR_ISP_FLASH_NONE = ERROR_COMMON_NONE, | ||
204 | ERROR_ISP_AWB_NONE = ERROR_COMMON_NONE, | ||
205 | ERROR_ISP_IMAGE_EFFECT_NONE = ERROR_COMMON_NONE, | ||
206 | ERROR_ISP_ISO_NONE = ERROR_COMMON_NONE, | ||
207 | ERROR_ISP_ADJUST_NONE = ERROR_COMMON_NONE, | ||
208 | ERROR_ISP_METERING_NONE = ERROR_COMMON_NONE, | ||
209 | ERROR_ISP_AFC_NONE = ERROR_COMMON_NONE, | ||
210 | |||
211 | /* DRC Error (300~399) */ | ||
212 | |||
213 | /* FD Error (400~499) */ | ||
214 | ERROR_FD_NONE = ERROR_COMMON_NONE, | ||
215 | /* Invalid max number (1~16) */ | ||
216 | ERROR_FD_CONFIG_MAX_NUMBER_STATE = 401, | ||
217 | ERROR_FD_CONFIG_MAX_NUMBER_INVALID = 402, | ||
218 | ERROR_FD_CONFIG_YAW_ANGLE_STATE = 403, | ||
219 | ERROR_FD_CONFIG_YAW_ANGLE_INVALID = 404, | ||
220 | ERROR_FD_CONFIG_ROLL_ANGLE_STATE = 405, | ||
221 | ERROR_FD_CONFIG_ROLL_ANGLE_INVALID = 406, | ||
222 | ERROR_FD_CONFIG_SMILE_MODE_INVALID = 407, | ||
223 | ERROR_FD_CONFIG_BLINK_MODE_INVALID = 408, | ||
224 | ERROR_FD_CONFIG_EYES_DETECT_INVALID = 409, | ||
225 | ERROR_FD_CONFIG_MOUTH_DETECT_INVALID = 410, | ||
226 | ERROR_FD_CONFIG_ORIENTATION_STATE = 411, | ||
227 | ERROR_FD_CONFIG_ORIENTATION_INVALID = 412, | ||
228 | ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID = 413, | ||
229 | /* PARAM_FdResultStr can be only applied in ready-state or stream off */ | ||
230 | ERROR_FD_RESULT = 414, | ||
231 | /* PARAM_FdModeStr can be only applied in ready-state or stream off */ | ||
232 | ERROR_FD_MODE = 415, | ||
233 | /* Scaler Error (500 ~ 599) */ | ||
234 | ERROR_SCALER_NO_NONE = ERROR_COMMON_NONE, | ||
235 | ERROR_SCALER_DMA_OUTSEL = 501, | ||
236 | ERROR_SCALER_H_RATIO = 502, | ||
237 | ERROR_SCALER_V_RATIO = 503, | ||
238 | |||
239 | ERROR_SCALER_IMAGE_EFFECT = 510, | ||
240 | |||
241 | ERROR_SCALER_ROTATE = 520, | ||
242 | ERROR_SCALER_FLIP = 521, | ||
243 | }; | ||
244 | |||
245 | const char * const fimc_is_strerr(unsigned int error); | ||
246 | const char * const fimc_is_param_strerr(unsigned int error); | ||
247 | |||
248 | #endif /* FIMC_IS_ERR_H_ */ | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/exynos4-is/fimc-is-i2c.c new file mode 100644 index 000000000000..c397777d7cbb --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.c | |||
@@ -0,0 +1,126 @@ | |||
1 | /* | ||
2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/clk.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/of_i2c.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/pm_runtime.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include "fimc-is-i2c.h" | ||
20 | |||
21 | struct fimc_is_i2c { | ||
22 | struct i2c_adapter adapter; | ||
23 | struct clk *clock; | ||
24 | }; | ||
25 | |||
26 | /* | ||
27 | * An empty algorithm is used as the actual I2C bus controller driver | ||
28 | * is implemented in the FIMC-IS subsystem firmware and the host CPU | ||
29 | * doesn't access the I2C bus controller. | ||
30 | */ | ||
31 | static const struct i2c_algorithm fimc_is_i2c_algorithm; | ||
32 | |||
33 | static int fimc_is_i2c_probe(struct platform_device *pdev) | ||
34 | { | ||
35 | struct device_node *node = pdev->dev.of_node; | ||
36 | struct fimc_is_i2c *isp_i2c; | ||
37 | struct i2c_adapter *i2c_adap; | ||
38 | int ret; | ||
39 | |||
40 | isp_i2c = devm_kzalloc(&pdev->dev, sizeof(*isp_i2c), GFP_KERNEL); | ||
41 | if (!isp_i2c) | ||
42 | return -ENOMEM; | ||
43 | |||
44 | isp_i2c->clock = devm_clk_get(&pdev->dev, "i2c_isp"); | ||
45 | if (IS_ERR(isp_i2c->clock)) { | ||
46 | dev_err(&pdev->dev, "failed to get the clock\n"); | ||
47 | return PTR_ERR(isp_i2c->clock); | ||
48 | } | ||
49 | |||
50 | i2c_adap = &isp_i2c->adapter; | ||
51 | i2c_adap->dev.of_node = node; | ||
52 | i2c_adap->dev.parent = &pdev->dev; | ||
53 | strlcpy(i2c_adap->name, "exynos4x12-isp-i2c", sizeof(i2c_adap->name)); | ||
54 | i2c_adap->owner = THIS_MODULE; | ||
55 | i2c_adap->algo = &fimc_is_i2c_algorithm; | ||
56 | i2c_adap->class = I2C_CLASS_SPD; | ||
57 | |||
58 | ret = i2c_add_adapter(i2c_adap); | ||
59 | if (ret < 0) { | ||
60 | dev_err(&pdev->dev, "failed to add I2C bus %s\n", | ||
61 | node->full_name); | ||
62 | return ret; | ||
63 | } | ||
64 | |||
65 | platform_set_drvdata(pdev, isp_i2c); | ||
66 | |||
67 | pm_runtime_enable(&pdev->dev); | ||
68 | pm_runtime_enable(&i2c_adap->dev); | ||
69 | |||
70 | of_i2c_register_devices(i2c_adap); | ||
71 | |||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static int fimc_is_i2c_remove(struct platform_device *pdev) | ||
76 | { | ||
77 | struct fimc_is_i2c *isp_i2c = platform_get_drvdata(pdev); | ||
78 | |||
79 | pm_runtime_disable(&isp_i2c->adapter.dev); | ||
80 | pm_runtime_disable(&pdev->dev); | ||
81 | i2c_del_adapter(&isp_i2c->adapter); | ||
82 | |||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | static int fimc_is_i2c_suspend(struct device *dev) | ||
87 | { | ||
88 | struct fimc_is_i2c *isp_i2c = dev_get_drvdata(dev); | ||
89 | clk_disable_unprepare(isp_i2c->clock); | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static int fimc_is_i2c_resume(struct device *dev) | ||
94 | { | ||
95 | struct fimc_is_i2c *isp_i2c = dev_get_drvdata(dev); | ||
96 | return clk_prepare_enable(isp_i2c->clock); | ||
97 | } | ||
98 | |||
99 | UNIVERSAL_DEV_PM_OPS(fimc_is_i2c_pm_ops, fimc_is_i2c_suspend, | ||
100 | fimc_is_i2c_resume, NULL); | ||
101 | |||
102 | static const struct of_device_id fimc_is_i2c_of_match[] = { | ||
103 | { .compatible = FIMC_IS_I2C_COMPATIBLE }, | ||
104 | { }, | ||
105 | }; | ||
106 | |||
107 | static struct platform_driver fimc_is_i2c_driver = { | ||
108 | .probe = fimc_is_i2c_probe, | ||
109 | .remove = fimc_is_i2c_remove, | ||
110 | .driver = { | ||
111 | .of_match_table = fimc_is_i2c_of_match, | ||
112 | .name = "fimc-isp-i2c", | ||
113 | .owner = THIS_MODULE, | ||
114 | .pm = &fimc_is_i2c_pm_ops, | ||
115 | } | ||
116 | }; | ||
117 | |||
118 | int fimc_is_register_i2c_driver(void) | ||
119 | { | ||
120 | return platform_driver_register(&fimc_is_i2c_driver); | ||
121 | } | ||
122 | |||
123 | void fimc_is_unregister_i2c_driver(void) | ||
124 | { | ||
125 | platform_driver_unregister(&fimc_is_i2c_driver); | ||
126 | } | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.h b/drivers/media/platform/exynos4-is/fimc-is-i2c.h new file mode 100644 index 000000000000..0d38d6bb963b --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.h | |||
@@ -0,0 +1,15 @@ | |||
1 | /* | ||
2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #define FIMC_IS_I2C_COMPATIBLE "samsung,exynos4212-i2c-isp" | ||
13 | |||
14 | int fimc_is_register_i2c_driver(void); | ||
15 | void fimc_is_unregister_i2c_driver(void); | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/exynos4-is/fimc-is-param.c new file mode 100644 index 000000000000..53fe2a2b4db3 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-param.c | |||
@@ -0,0 +1,900 @@ | |||
1 | /* | ||
2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Authors: Younghwan Joo <yhwan.joo@samsung.com> | ||
7 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ | ||
14 | |||
15 | #include <linux/bitops.h> | ||
16 | #include <linux/bug.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/errno.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/types.h> | ||
24 | #include <linux/videodev2.h> | ||
25 | |||
26 | #include <media/v4l2-device.h> | ||
27 | #include <media/v4l2-ioctl.h> | ||
28 | |||
29 | #include "fimc-is.h" | ||
30 | #include "fimc-is-command.h" | ||
31 | #include "fimc-is-errno.h" | ||
32 | #include "fimc-is-param.h" | ||
33 | #include "fimc-is-regs.h" | ||
34 | #include "fimc-is-sensor.h" | ||
35 | |||
36 | static void __hw_param_copy(void *dst, void *src) | ||
37 | { | ||
38 | memcpy(dst, src, FIMC_IS_PARAM_MAX_SIZE); | ||
39 | } | ||
40 | |||
41 | void __fimc_is_hw_update_param_global_shotmode(struct fimc_is *is) | ||
42 | { | ||
43 | struct param_global_shotmode *dst, *src; | ||
44 | |||
45 | dst = &is->is_p_region->parameter.global.shotmode; | ||
46 | src = &is->config[is->config_index].global.shotmode; | ||
47 | __hw_param_copy(dst, src); | ||
48 | } | ||
49 | |||
50 | void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is) | ||
51 | { | ||
52 | struct param_sensor_framerate *dst, *src; | ||
53 | |||
54 | dst = &is->is_p_region->parameter.sensor.frame_rate; | ||
55 | src = &is->config[is->config_index].sensor.frame_rate; | ||
56 | __hw_param_copy(dst, src); | ||
57 | } | ||
58 | |||
59 | int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset) | ||
60 | { | ||
61 | struct is_param_region *par = &is->is_p_region->parameter; | ||
62 | struct chain_config *cfg = &is->config[is->config_index]; | ||
63 | |||
64 | switch (offset) { | ||
65 | case PARAM_ISP_CONTROL: | ||
66 | __hw_param_copy(&par->isp.control, &cfg->isp.control); | ||
67 | break; | ||
68 | |||
69 | case PARAM_ISP_OTF_INPUT: | ||
70 | __hw_param_copy(&par->isp.otf_input, &cfg->isp.otf_input); | ||
71 | break; | ||
72 | |||
73 | case PARAM_ISP_DMA1_INPUT: | ||
74 | __hw_param_copy(&par->isp.dma1_input, &cfg->isp.dma1_input); | ||
75 | break; | ||
76 | |||
77 | case PARAM_ISP_DMA2_INPUT: | ||
78 | __hw_param_copy(&par->isp.dma2_input, &cfg->isp.dma2_input); | ||
79 | break; | ||
80 | |||
81 | case PARAM_ISP_AA: | ||
82 | __hw_param_copy(&par->isp.aa, &cfg->isp.aa); | ||
83 | break; | ||
84 | |||
85 | case PARAM_ISP_FLASH: | ||
86 | __hw_param_copy(&par->isp.flash, &cfg->isp.flash); | ||
87 | break; | ||
88 | |||
89 | case PARAM_ISP_AWB: | ||
90 | __hw_param_copy(&par->isp.awb, &cfg->isp.awb); | ||
91 | break; | ||
92 | |||
93 | case PARAM_ISP_IMAGE_EFFECT: | ||
94 | __hw_param_copy(&par->isp.effect, &cfg->isp.effect); | ||
95 | break; | ||
96 | |||
97 | case PARAM_ISP_ISO: | ||
98 | __hw_param_copy(&par->isp.iso, &cfg->isp.iso); | ||
99 | break; | ||
100 | |||
101 | case PARAM_ISP_ADJUST: | ||
102 | __hw_param_copy(&par->isp.adjust, &cfg->isp.adjust); | ||
103 | break; | ||
104 | |||
105 | case PARAM_ISP_METERING: | ||
106 | __hw_param_copy(&par->isp.metering, &cfg->isp.metering); | ||
107 | break; | ||
108 | |||
109 | case PARAM_ISP_AFC: | ||
110 | __hw_param_copy(&par->isp.afc, &cfg->isp.afc); | ||
111 | break; | ||
112 | |||
113 | case PARAM_ISP_OTF_OUTPUT: | ||
114 | __hw_param_copy(&par->isp.otf_output, &cfg->isp.otf_output); | ||
115 | break; | ||
116 | |||
117 | case PARAM_ISP_DMA1_OUTPUT: | ||
118 | __hw_param_copy(&par->isp.dma1_output, &cfg->isp.dma1_output); | ||
119 | break; | ||
120 | |||
121 | case PARAM_ISP_DMA2_OUTPUT: | ||
122 | __hw_param_copy(&par->isp.dma2_output, &cfg->isp.dma2_output); | ||
123 | break; | ||
124 | |||
125 | case PARAM_DRC_CONTROL: | ||
126 | __hw_param_copy(&par->drc.control, &cfg->drc.control); | ||
127 | break; | ||
128 | |||
129 | case PARAM_DRC_OTF_INPUT: | ||
130 | __hw_param_copy(&par->drc.otf_input, &cfg->drc.otf_input); | ||
131 | break; | ||
132 | |||
133 | case PARAM_DRC_DMA_INPUT: | ||
134 | __hw_param_copy(&par->drc.dma_input, &cfg->drc.dma_input); | ||
135 | break; | ||
136 | |||
137 | case PARAM_DRC_OTF_OUTPUT: | ||
138 | __hw_param_copy(&par->drc.otf_output, &cfg->drc.otf_output); | ||
139 | break; | ||
140 | |||
141 | case PARAM_FD_CONTROL: | ||
142 | __hw_param_copy(&par->fd.control, &cfg->fd.control); | ||
143 | break; | ||
144 | |||
145 | case PARAM_FD_OTF_INPUT: | ||
146 | __hw_param_copy(&par->fd.otf_input, &cfg->fd.otf_input); | ||
147 | break; | ||
148 | |||
149 | case PARAM_FD_DMA_INPUT: | ||
150 | __hw_param_copy(&par->fd.dma_input, &cfg->fd.dma_input); | ||
151 | break; | ||
152 | |||
153 | case PARAM_FD_CONFIG: | ||
154 | __hw_param_copy(&par->fd.config, &cfg->fd.config); | ||
155 | break; | ||
156 | |||
157 | default: | ||
158 | return -EINVAL; | ||
159 | } | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | unsigned int __get_pending_param_count(struct fimc_is *is) | ||
165 | { | ||
166 | struct chain_config *config = &is->config[is->config_index]; | ||
167 | unsigned long flags; | ||
168 | unsigned int count; | ||
169 | |||
170 | spin_lock_irqsave(&is->slock, flags); | ||
171 | count = hweight32(config->p_region_index1); | ||
172 | count += hweight32(config->p_region_index2); | ||
173 | spin_unlock_irqrestore(&is->slock, flags); | ||
174 | |||
175 | return count; | ||
176 | } | ||
177 | |||
178 | int __is_hw_update_params(struct fimc_is *is) | ||
179 | { | ||
180 | unsigned long *p_index1, *p_index2; | ||
181 | int i, id, ret = 0; | ||
182 | |||
183 | id = is->config_index; | ||
184 | p_index1 = &is->config[id].p_region_index1; | ||
185 | p_index2 = &is->config[id].p_region_index2; | ||
186 | |||
187 | if (test_bit(PARAM_GLOBAL_SHOTMODE, p_index1)) | ||
188 | __fimc_is_hw_update_param_global_shotmode(is); | ||
189 | |||
190 | if (test_bit(PARAM_SENSOR_FRAME_RATE, p_index1)) | ||
191 | __fimc_is_hw_update_param_sensor_framerate(is); | ||
192 | |||
193 | for (i = PARAM_ISP_CONTROL; i < PARAM_DRC_CONTROL; i++) { | ||
194 | if (test_bit(i, p_index1)) | ||
195 | ret = __fimc_is_hw_update_param(is, i); | ||
196 | } | ||
197 | |||
198 | for (i = PARAM_DRC_CONTROL; i < PARAM_SCALERC_CONTROL; i++) { | ||
199 | if (test_bit(i, p_index1)) | ||
200 | ret = __fimc_is_hw_update_param(is, i); | ||
201 | } | ||
202 | |||
203 | for (i = PARAM_FD_CONTROL; i <= PARAM_FD_CONFIG; i++) { | ||
204 | if (test_bit((i - 32), p_index2)) | ||
205 | ret = __fimc_is_hw_update_param(is, i); | ||
206 | } | ||
207 | |||
208 | return ret; | ||
209 | } | ||
210 | |||
211 | void __is_get_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf) | ||
212 | { | ||
213 | struct isp_param *isp; | ||
214 | |||
215 | isp = &is->config[is->config_index].isp; | ||
216 | mf->width = isp->otf_input.width; | ||
217 | mf->height = isp->otf_input.height; | ||
218 | } | ||
219 | |||
220 | void __is_set_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf) | ||
221 | { | ||
222 | unsigned int index = is->config_index; | ||
223 | struct isp_param *isp; | ||
224 | struct drc_param *drc; | ||
225 | struct fd_param *fd; | ||
226 | |||
227 | isp = &is->config[index].isp; | ||
228 | drc = &is->config[index].drc; | ||
229 | fd = &is->config[index].fd; | ||
230 | |||
231 | /* Update isp size info (OTF only) */ | ||
232 | isp->otf_input.width = mf->width; | ||
233 | isp->otf_input.height = mf->height; | ||
234 | isp->otf_output.width = mf->width; | ||
235 | isp->otf_output.height = mf->height; | ||
236 | /* Update drc size info (OTF only) */ | ||
237 | drc->otf_input.width = mf->width; | ||
238 | drc->otf_input.height = mf->height; | ||
239 | drc->otf_output.width = mf->width; | ||
240 | drc->otf_output.height = mf->height; | ||
241 | /* Update fd size info (OTF only) */ | ||
242 | fd->otf_input.width = mf->width; | ||
243 | fd->otf_input.height = mf->height; | ||
244 | |||
245 | if (test_bit(PARAM_ISP_OTF_INPUT, | ||
246 | &is->config[index].p_region_index1)) | ||
247 | return; | ||
248 | |||
249 | /* Update field */ | ||
250 | fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT); | ||
251 | fimc_is_set_param_bit(is, PARAM_ISP_OTF_OUTPUT); | ||
252 | fimc_is_set_param_bit(is, PARAM_DRC_OTF_INPUT); | ||
253 | fimc_is_set_param_bit(is, PARAM_DRC_OTF_OUTPUT); | ||
254 | fimc_is_set_param_bit(is, PARAM_FD_OTF_INPUT); | ||
255 | } | ||
256 | |||
257 | int fimc_is_hw_get_sensor_max_framerate(struct fimc_is *is) | ||
258 | { | ||
259 | switch (is->sensor->drvdata->id) { | ||
260 | case FIMC_IS_SENSOR_ID_S5K6A3: | ||
261 | return 30; | ||
262 | default: | ||
263 | return 15; | ||
264 | } | ||
265 | } | ||
266 | |||
267 | void __is_set_sensor(struct fimc_is *is, int fps) | ||
268 | { | ||
269 | unsigned int index = is->config_index; | ||
270 | struct sensor_param *sensor; | ||
271 | struct isp_param *isp; | ||
272 | |||
273 | sensor = &is->config[index].sensor; | ||
274 | isp = &is->config[index].isp; | ||
275 | |||
276 | if (fps == 0) { | ||
277 | sensor->frame_rate.frame_rate = | ||
278 | fimc_is_hw_get_sensor_max_framerate(is); | ||
279 | isp->otf_input.frametime_min = 0; | ||
280 | isp->otf_input.frametime_max = 66666; | ||
281 | } else { | ||
282 | sensor->frame_rate.frame_rate = fps; | ||
283 | isp->otf_input.frametime_min = 0; | ||
284 | isp->otf_input.frametime_max = (u32)1000000 / fps; | ||
285 | } | ||
286 | |||
287 | fimc_is_set_param_bit(is, PARAM_SENSOR_FRAME_RATE); | ||
288 | fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT); | ||
289 | } | ||
290 | |||
291 | void __is_set_init_isp_aa(struct fimc_is *is) | ||
292 | { | ||
293 | struct isp_param *isp; | ||
294 | |||
295 | isp = &is->config[is->config_index].isp; | ||
296 | |||
297 | isp->aa.cmd = ISP_AA_COMMAND_START; | ||
298 | isp->aa.target = ISP_AA_TARGET_AF | ISP_AA_TARGET_AE | | ||
299 | ISP_AA_TARGET_AWB; | ||
300 | isp->aa.mode = 0; | ||
301 | isp->aa.scene = 0; | ||
302 | isp->aa.sleep = 0; | ||
303 | isp->aa.face = 0; | ||
304 | isp->aa.touch_x = 0; | ||
305 | isp->aa.touch_y = 0; | ||
306 | isp->aa.manual_af_setting = 0; | ||
307 | isp->aa.err = ISP_AF_ERROR_NONE; | ||
308 | |||
309 | fimc_is_set_param_bit(is, PARAM_ISP_AA); | ||
310 | } | ||
311 | |||
312 | void __is_set_isp_flash(struct fimc_is *is, u32 cmd, u32 redeye) | ||
313 | { | ||
314 | unsigned int index = is->config_index; | ||
315 | struct isp_param *isp = &is->config[index].isp; | ||
316 | |||
317 | isp->flash.cmd = cmd; | ||
318 | isp->flash.redeye = redeye; | ||
319 | isp->flash.err = ISP_FLASH_ERROR_NONE; | ||
320 | |||
321 | fimc_is_set_param_bit(is, PARAM_ISP_FLASH); | ||
322 | } | ||
323 | |||
324 | void __is_set_isp_awb(struct fimc_is *is, u32 cmd, u32 val) | ||
325 | { | ||
326 | unsigned int index = is->config_index; | ||
327 | struct isp_param *isp; | ||
328 | |||
329 | isp = &is->config[index].isp; | ||
330 | |||
331 | isp->awb.cmd = cmd; | ||
332 | isp->awb.illumination = val; | ||
333 | isp->awb.err = ISP_AWB_ERROR_NONE; | ||
334 | |||
335 | fimc_is_set_param_bit(is, PARAM_ISP_AWB); | ||
336 | } | ||
337 | |||
338 | void __is_set_isp_effect(struct fimc_is *is, u32 cmd) | ||
339 | { | ||
340 | unsigned int index = is->config_index; | ||
341 | struct isp_param *isp; | ||
342 | |||
343 | isp = &is->config[index].isp; | ||
344 | |||
345 | isp->effect.cmd = cmd; | ||
346 | isp->effect.err = ISP_IMAGE_EFFECT_ERROR_NONE; | ||
347 | |||
348 | fimc_is_set_param_bit(is, PARAM_ISP_IMAGE_EFFECT); | ||
349 | } | ||
350 | |||
351 | void __is_set_isp_iso(struct fimc_is *is, u32 cmd, u32 val) | ||
352 | { | ||
353 | unsigned int index = is->config_index; | ||
354 | struct isp_param *isp; | ||
355 | |||
356 | isp = &is->config[index].isp; | ||
357 | |||
358 | isp->iso.cmd = cmd; | ||
359 | isp->iso.value = val; | ||
360 | isp->iso.err = ISP_ISO_ERROR_NONE; | ||
361 | |||
362 | fimc_is_set_param_bit(is, PARAM_ISP_ISO); | ||
363 | } | ||
364 | |||
365 | void __is_set_isp_adjust(struct fimc_is *is, u32 cmd, u32 val) | ||
366 | { | ||
367 | unsigned int index = is->config_index; | ||
368 | unsigned long *p_index; | ||
369 | struct isp_param *isp; | ||
370 | |||
371 | p_index = &is->config[index].p_region_index1; | ||
372 | isp = &is->config[index].isp; | ||
373 | |||
374 | switch (cmd) { | ||
375 | case ISP_ADJUST_COMMAND_MANUAL_CONTRAST: | ||
376 | isp->adjust.contrast = val; | ||
377 | break; | ||
378 | case ISP_ADJUST_COMMAND_MANUAL_SATURATION: | ||
379 | isp->adjust.saturation = val; | ||
380 | break; | ||
381 | case ISP_ADJUST_COMMAND_MANUAL_SHARPNESS: | ||
382 | isp->adjust.sharpness = val; | ||
383 | break; | ||
384 | case ISP_ADJUST_COMMAND_MANUAL_EXPOSURE: | ||
385 | isp->adjust.exposure = val; | ||
386 | break; | ||
387 | case ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS: | ||
388 | isp->adjust.brightness = val; | ||
389 | break; | ||
390 | case ISP_ADJUST_COMMAND_MANUAL_HUE: | ||
391 | isp->adjust.hue = val; | ||
392 | break; | ||
393 | case ISP_ADJUST_COMMAND_AUTO: | ||
394 | isp->adjust.contrast = 0; | ||
395 | isp->adjust.saturation = 0; | ||
396 | isp->adjust.sharpness = 0; | ||
397 | isp->adjust.exposure = 0; | ||
398 | isp->adjust.brightness = 0; | ||
399 | isp->adjust.hue = 0; | ||
400 | break; | ||
401 | } | ||
402 | |||
403 | if (!test_bit(PARAM_ISP_ADJUST, p_index)) { | ||
404 | isp->adjust.cmd = cmd; | ||
405 | isp->adjust.err = ISP_ADJUST_ERROR_NONE; | ||
406 | fimc_is_set_param_bit(is, PARAM_ISP_ADJUST); | ||
407 | } else { | ||
408 | isp->adjust.cmd |= cmd; | ||
409 | } | ||
410 | } | ||
411 | |||
412 | void __is_set_isp_metering(struct fimc_is *is, u32 id, u32 val) | ||
413 | { | ||
414 | unsigned int index = is->config_index; | ||
415 | struct isp_param *isp; | ||
416 | unsigned long *p_index; | ||
417 | |||
418 | p_index = &is->config[index].p_region_index1; | ||
419 | isp = &is->config[index].isp; | ||
420 | |||
421 | switch (id) { | ||
422 | case IS_METERING_CONFIG_CMD: | ||
423 | isp->metering.cmd = val; | ||
424 | break; | ||
425 | case IS_METERING_CONFIG_WIN_POS_X: | ||
426 | isp->metering.win_pos_x = val; | ||
427 | break; | ||
428 | case IS_METERING_CONFIG_WIN_POS_Y: | ||
429 | isp->metering.win_pos_y = val; | ||
430 | break; | ||
431 | case IS_METERING_CONFIG_WIN_WIDTH: | ||
432 | isp->metering.win_width = val; | ||
433 | break; | ||
434 | case IS_METERING_CONFIG_WIN_HEIGHT: | ||
435 | isp->metering.win_height = val; | ||
436 | break; | ||
437 | default: | ||
438 | return; | ||
439 | } | ||
440 | |||
441 | if (!test_bit(PARAM_ISP_METERING, p_index)) { | ||
442 | isp->metering.err = ISP_METERING_ERROR_NONE; | ||
443 | fimc_is_set_param_bit(is, PARAM_ISP_METERING); | ||
444 | } | ||
445 | } | ||
446 | |||
447 | void __is_set_isp_afc(struct fimc_is *is, u32 cmd, u32 val) | ||
448 | { | ||
449 | unsigned int index = is->config_index; | ||
450 | struct isp_param *isp; | ||
451 | |||
452 | isp = &is->config[index].isp; | ||
453 | |||
454 | isp->afc.cmd = cmd; | ||
455 | isp->afc.manual = val; | ||
456 | isp->afc.err = ISP_AFC_ERROR_NONE; | ||
457 | |||
458 | fimc_is_set_param_bit(is, PARAM_ISP_AFC); | ||
459 | } | ||
460 | |||
461 | void __is_set_drc_control(struct fimc_is *is, u32 val) | ||
462 | { | ||
463 | unsigned int index = is->config_index; | ||
464 | struct drc_param *drc; | ||
465 | |||
466 | drc = &is->config[index].drc; | ||
467 | |||
468 | drc->control.bypass = val; | ||
469 | |||
470 | fimc_is_set_param_bit(is, PARAM_DRC_CONTROL); | ||
471 | } | ||
472 | |||
473 | void __is_set_fd_control(struct fimc_is *is, u32 val) | ||
474 | { | ||
475 | unsigned int index = is->config_index; | ||
476 | struct fd_param *fd; | ||
477 | unsigned long *p_index; | ||
478 | |||
479 | p_index = &is->config[index].p_region_index2; | ||
480 | fd = &is->config[index].fd; | ||
481 | |||
482 | fd->control.cmd = val; | ||
483 | |||
484 | if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) | ||
485 | fimc_is_set_param_bit(is, PARAM_FD_CONTROL); | ||
486 | } | ||
487 | |||
488 | void __is_set_fd_config_maxface(struct fimc_is *is, u32 val) | ||
489 | { | ||
490 | unsigned int index = is->config_index; | ||
491 | struct fd_param *fd; | ||
492 | unsigned long *p_index; | ||
493 | |||
494 | p_index = &is->config[index].p_region_index2; | ||
495 | fd = &is->config[index].fd; | ||
496 | |||
497 | fd->config.max_number = val; | ||
498 | |||
499 | if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { | ||
500 | fd->config.cmd = FD_CONFIG_COMMAND_MAXIMUM_NUMBER; | ||
501 | fd->config.err = ERROR_FD_NONE; | ||
502 | fimc_is_set_param_bit(is, PARAM_FD_CONFIG); | ||
503 | } else { | ||
504 | fd->config.cmd |= FD_CONFIG_COMMAND_MAXIMUM_NUMBER; | ||
505 | } | ||
506 | } | ||
507 | |||
508 | void __is_set_fd_config_rollangle(struct fimc_is *is, u32 val) | ||
509 | { | ||
510 | unsigned int index = is->config_index; | ||
511 | struct fd_param *fd; | ||
512 | unsigned long *p_index; | ||
513 | |||
514 | p_index = &is->config[index].p_region_index2; | ||
515 | fd = &is->config[index].fd; | ||
516 | |||
517 | fd->config.roll_angle = val; | ||
518 | |||
519 | if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { | ||
520 | fd->config.cmd = FD_CONFIG_COMMAND_ROLL_ANGLE; | ||
521 | fd->config.err = ERROR_FD_NONE; | ||
522 | fimc_is_set_param_bit(is, PARAM_FD_CONFIG); | ||
523 | } else { | ||
524 | fd->config.cmd |= FD_CONFIG_COMMAND_ROLL_ANGLE; | ||
525 | } | ||
526 | } | ||
527 | |||
528 | void __is_set_fd_config_yawangle(struct fimc_is *is, u32 val) | ||
529 | { | ||
530 | unsigned int index = is->config_index; | ||
531 | struct fd_param *fd; | ||
532 | unsigned long *p_index; | ||
533 | |||
534 | p_index = &is->config[index].p_region_index2; | ||
535 | fd = &is->config[index].fd; | ||
536 | |||
537 | fd->config.yaw_angle = val; | ||
538 | |||
539 | if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { | ||
540 | fd->config.cmd = FD_CONFIG_COMMAND_YAW_ANGLE; | ||
541 | fd->config.err = ERROR_FD_NONE; | ||
542 | fimc_is_set_param_bit(is, PARAM_FD_CONFIG); | ||
543 | } else { | ||
544 | fd->config.cmd |= FD_CONFIG_COMMAND_YAW_ANGLE; | ||
545 | } | ||
546 | } | ||
547 | |||
548 | void __is_set_fd_config_smilemode(struct fimc_is *is, u32 val) | ||
549 | { | ||
550 | unsigned int index = is->config_index; | ||
551 | struct fd_param *fd; | ||
552 | unsigned long *p_index; | ||
553 | |||
554 | p_index = &is->config[index].p_region_index2; | ||
555 | fd = &is->config[index].fd; | ||
556 | |||
557 | fd->config.smile_mode = val; | ||
558 | |||
559 | if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { | ||
560 | fd->config.cmd = FD_CONFIG_COMMAND_SMILE_MODE; | ||
561 | fd->config.err = ERROR_FD_NONE; | ||
562 | fimc_is_set_param_bit(is, PARAM_FD_CONFIG); | ||
563 | } else { | ||
564 | fd->config.cmd |= FD_CONFIG_COMMAND_SMILE_MODE; | ||
565 | } | ||
566 | } | ||
567 | |||
568 | void __is_set_fd_config_blinkmode(struct fimc_is *is, u32 val) | ||
569 | { | ||
570 | unsigned int index = is->config_index; | ||
571 | struct fd_param *fd; | ||
572 | unsigned long *p_index; | ||
573 | |||
574 | p_index = &is->config[index].p_region_index2; | ||
575 | fd = &is->config[index].fd; | ||
576 | |||
577 | fd->config.blink_mode = val; | ||
578 | |||
579 | if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { | ||
580 | fd->config.cmd = FD_CONFIG_COMMAND_BLINK_MODE; | ||
581 | fd->config.err = ERROR_FD_NONE; | ||
582 | fimc_is_set_param_bit(is, PARAM_FD_CONFIG); | ||
583 | } else { | ||
584 | fd->config.cmd |= FD_CONFIG_COMMAND_BLINK_MODE; | ||
585 | } | ||
586 | } | ||
587 | |||
588 | void __is_set_fd_config_eyedetect(struct fimc_is *is, u32 val) | ||
589 | { | ||
590 | unsigned int index = is->config_index; | ||
591 | struct fd_param *fd; | ||
592 | unsigned long *p_index; | ||
593 | |||
594 | p_index = &is->config[index].p_region_index2; | ||
595 | fd = &is->config[index].fd; | ||
596 | |||
597 | fd->config.eye_detect = val; | ||
598 | |||
599 | if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { | ||
600 | fd->config.cmd = FD_CONFIG_COMMAND_EYES_DETECT; | ||
601 | fd->config.err = ERROR_FD_NONE; | ||
602 | fimc_is_set_param_bit(is, PARAM_FD_CONFIG); | ||
603 | } else { | ||
604 | fd->config.cmd |= FD_CONFIG_COMMAND_EYES_DETECT; | ||
605 | } | ||
606 | } | ||
607 | |||
608 | void __is_set_fd_config_mouthdetect(struct fimc_is *is, u32 val) | ||
609 | { | ||
610 | unsigned int index = is->config_index; | ||
611 | struct fd_param *fd; | ||
612 | unsigned long *p_index; | ||
613 | |||
614 | p_index = &is->config[index].p_region_index2; | ||
615 | fd = &is->config[index].fd; | ||
616 | |||
617 | fd->config.mouth_detect = val; | ||
618 | |||
619 | if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { | ||
620 | fd->config.cmd = FD_CONFIG_COMMAND_MOUTH_DETECT; | ||
621 | fd->config.err = ERROR_FD_NONE; | ||
622 | fimc_is_set_param_bit(is, PARAM_FD_CONFIG); | ||
623 | } else { | ||
624 | fd->config.cmd |= FD_CONFIG_COMMAND_MOUTH_DETECT; | ||
625 | } | ||
626 | } | ||
627 | |||
628 | void __is_set_fd_config_orientation(struct fimc_is *is, u32 val) | ||
629 | { | ||
630 | unsigned int index = is->config_index; | ||
631 | struct fd_param *fd; | ||
632 | unsigned long *p_index; | ||
633 | |||
634 | p_index = &is->config[index].p_region_index2; | ||
635 | fd = &is->config[index].fd; | ||
636 | |||
637 | fd->config.orientation = val; | ||
638 | |||
639 | if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { | ||
640 | fd->config.cmd = FD_CONFIG_COMMAND_ORIENTATION; | ||
641 | fd->config.err = ERROR_FD_NONE; | ||
642 | fimc_is_set_param_bit(is, PARAM_FD_CONFIG); | ||
643 | } else { | ||
644 | fd->config.cmd |= FD_CONFIG_COMMAND_ORIENTATION; | ||
645 | } | ||
646 | } | ||
647 | |||
648 | void __is_set_fd_config_orientation_val(struct fimc_is *is, u32 val) | ||
649 | { | ||
650 | unsigned int index = is->config_index; | ||
651 | struct fd_param *fd; | ||
652 | unsigned long *p_index; | ||
653 | |||
654 | p_index = &is->config[index].p_region_index2; | ||
655 | fd = &is->config[index].fd; | ||
656 | |||
657 | fd->config.orientation_value = val; | ||
658 | |||
659 | if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) { | ||
660 | fd->config.cmd = FD_CONFIG_COMMAND_ORIENTATION_VALUE; | ||
661 | fd->config.err = ERROR_FD_NONE; | ||
662 | fimc_is_set_param_bit(is, PARAM_FD_CONFIG); | ||
663 | } else { | ||
664 | fd->config.cmd |= FD_CONFIG_COMMAND_ORIENTATION_VALUE; | ||
665 | } | ||
666 | } | ||
667 | |||
668 | void fimc_is_set_initial_params(struct fimc_is *is) | ||
669 | { | ||
670 | struct global_param *global; | ||
671 | struct sensor_param *sensor; | ||
672 | struct isp_param *isp; | ||
673 | struct drc_param *drc; | ||
674 | struct fd_param *fd; | ||
675 | unsigned long *p_index1, *p_index2; | ||
676 | unsigned int index; | ||
677 | |||
678 | index = is->config_index; | ||
679 | global = &is->config[index].global; | ||
680 | sensor = &is->config[index].sensor; | ||
681 | isp = &is->config[index].isp; | ||
682 | drc = &is->config[index].drc; | ||
683 | fd = &is->config[index].fd; | ||
684 | p_index1 = &is->config[index].p_region_index1; | ||
685 | p_index2 = &is->config[index].p_region_index2; | ||
686 | |||
687 | /* Global */ | ||
688 | global->shotmode.cmd = 1; | ||
689 | fimc_is_set_param_bit(is, PARAM_GLOBAL_SHOTMODE); | ||
690 | |||
691 | /* ISP */ | ||
692 | isp->control.cmd = CONTROL_COMMAND_START; | ||
693 | isp->control.bypass = CONTROL_BYPASS_DISABLE; | ||
694 | isp->control.err = CONTROL_ERROR_NONE; | ||
695 | fimc_is_set_param_bit(is, PARAM_ISP_CONTROL); | ||
696 | |||
697 | isp->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE; | ||
698 | if (!test_bit(PARAM_ISP_OTF_INPUT, p_index1)) { | ||
699 | isp->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH; | ||
700 | isp->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT; | ||
701 | fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT); | ||
702 | } | ||
703 | if (is->sensor->test_pattern) | ||
704 | isp->otf_input.format = OTF_INPUT_FORMAT_STRGEN_COLORBAR_BAYER; | ||
705 | else | ||
706 | isp->otf_input.format = OTF_INPUT_FORMAT_BAYER; | ||
707 | isp->otf_input.bitwidth = 10; | ||
708 | isp->otf_input.order = OTF_INPUT_ORDER_BAYER_GR_BG; | ||
709 | isp->otf_input.crop_offset_x = 0; | ||
710 | isp->otf_input.crop_offset_y = 0; | ||
711 | isp->otf_input.err = OTF_INPUT_ERROR_NONE; | ||
712 | |||
713 | isp->dma1_input.cmd = DMA_INPUT_COMMAND_DISABLE; | ||
714 | isp->dma1_input.width = 0; | ||
715 | isp->dma1_input.height = 0; | ||
716 | isp->dma1_input.format = 0; | ||
717 | isp->dma1_input.bitwidth = 0; | ||
718 | isp->dma1_input.plane = 0; | ||
719 | isp->dma1_input.order = 0; | ||
720 | isp->dma1_input.buffer_number = 0; | ||
721 | isp->dma1_input.width = 0; | ||
722 | isp->dma1_input.err = DMA_INPUT_ERROR_NONE; | ||
723 | fimc_is_set_param_bit(is, PARAM_ISP_DMA1_INPUT); | ||
724 | |||
725 | isp->dma2_input.cmd = DMA_INPUT_COMMAND_DISABLE; | ||
726 | isp->dma2_input.width = 0; | ||
727 | isp->dma2_input.height = 0; | ||
728 | isp->dma2_input.format = 0; | ||
729 | isp->dma2_input.bitwidth = 0; | ||
730 | isp->dma2_input.plane = 0; | ||
731 | isp->dma2_input.order = 0; | ||
732 | isp->dma2_input.buffer_number = 0; | ||
733 | isp->dma2_input.width = 0; | ||
734 | isp->dma2_input.err = DMA_INPUT_ERROR_NONE; | ||
735 | fimc_is_set_param_bit(is, PARAM_ISP_DMA2_INPUT); | ||
736 | |||
737 | isp->aa.cmd = ISP_AA_COMMAND_START; | ||
738 | isp->aa.target = ISP_AA_TARGET_AE | ISP_AA_TARGET_AWB; | ||
739 | fimc_is_set_param_bit(is, PARAM_ISP_AA); | ||
740 | |||
741 | if (!test_bit(PARAM_ISP_FLASH, p_index1)) | ||
742 | __is_set_isp_flash(is, ISP_FLASH_COMMAND_DISABLE, | ||
743 | ISP_FLASH_REDEYE_DISABLE); | ||
744 | |||
745 | if (!test_bit(PARAM_ISP_AWB, p_index1)) | ||
746 | __is_set_isp_awb(is, ISP_AWB_COMMAND_AUTO, 0); | ||
747 | |||
748 | if (!test_bit(PARAM_ISP_IMAGE_EFFECT, p_index1)) | ||
749 | __is_set_isp_effect(is, ISP_IMAGE_EFFECT_DISABLE); | ||
750 | |||
751 | if (!test_bit(PARAM_ISP_ISO, p_index1)) | ||
752 | __is_set_isp_iso(is, ISP_ISO_COMMAND_AUTO, 0); | ||
753 | |||
754 | if (!test_bit(PARAM_ISP_ADJUST, p_index1)) { | ||
755 | __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_CONTRAST, 0); | ||
756 | __is_set_isp_adjust(is, | ||
757 | ISP_ADJUST_COMMAND_MANUAL_SATURATION, 0); | ||
758 | __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SHARPNESS, 0); | ||
759 | __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_EXPOSURE, 0); | ||
760 | __is_set_isp_adjust(is, | ||
761 | ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS, 0); | ||
762 | __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_HUE, 0); | ||
763 | } | ||
764 | |||
765 | if (!test_bit(PARAM_ISP_METERING, p_index1)) { | ||
766 | __is_set_isp_metering(is, 0, ISP_METERING_COMMAND_CENTER); | ||
767 | __is_set_isp_metering(is, 1, 0); | ||
768 | __is_set_isp_metering(is, 2, 0); | ||
769 | __is_set_isp_metering(is, 3, 0); | ||
770 | __is_set_isp_metering(is, 4, 0); | ||
771 | } | ||
772 | |||
773 | if (!test_bit(PARAM_ISP_AFC, p_index1)) | ||
774 | __is_set_isp_afc(is, ISP_AFC_COMMAND_AUTO, 0); | ||
775 | |||
776 | isp->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE; | ||
777 | if (!test_bit(PARAM_ISP_OTF_OUTPUT, p_index1)) { | ||
778 | isp->otf_output.width = DEFAULT_PREVIEW_STILL_WIDTH; | ||
779 | isp->otf_output.height = DEFAULT_PREVIEW_STILL_HEIGHT; | ||
780 | fimc_is_set_param_bit(is, PARAM_ISP_OTF_OUTPUT); | ||
781 | } | ||
782 | isp->otf_output.format = OTF_OUTPUT_FORMAT_YUV444; | ||
783 | isp->otf_output.bitwidth = 12; | ||
784 | isp->otf_output.order = 0; | ||
785 | isp->otf_output.err = OTF_OUTPUT_ERROR_NONE; | ||
786 | |||
787 | if (!test_bit(PARAM_ISP_DMA1_OUTPUT, p_index1)) { | ||
788 | isp->dma1_output.cmd = DMA_OUTPUT_COMMAND_DISABLE; | ||
789 | isp->dma1_output.width = 0; | ||
790 | isp->dma1_output.height = 0; | ||
791 | isp->dma1_output.format = 0; | ||
792 | isp->dma1_output.bitwidth = 0; | ||
793 | isp->dma1_output.plane = 0; | ||
794 | isp->dma1_output.order = 0; | ||
795 | isp->dma1_output.buffer_number = 0; | ||
796 | isp->dma1_output.buffer_address = 0; | ||
797 | isp->dma1_output.notify_dma_done = 0; | ||
798 | isp->dma1_output.dma_out_mask = 0; | ||
799 | isp->dma1_output.err = DMA_OUTPUT_ERROR_NONE; | ||
800 | fimc_is_set_param_bit(is, PARAM_ISP_DMA1_OUTPUT); | ||
801 | } | ||
802 | |||
803 | if (!test_bit(PARAM_ISP_DMA2_OUTPUT, p_index1)) { | ||
804 | isp->dma2_output.cmd = DMA_OUTPUT_COMMAND_DISABLE; | ||
805 | isp->dma2_output.width = 0; | ||
806 | isp->dma2_output.height = 0; | ||
807 | isp->dma2_output.format = 0; | ||
808 | isp->dma2_output.bitwidth = 0; | ||
809 | isp->dma2_output.plane = 0; | ||
810 | isp->dma2_output.order = 0; | ||
811 | isp->dma2_output.buffer_number = 0; | ||
812 | isp->dma2_output.buffer_address = 0; | ||
813 | isp->dma2_output.notify_dma_done = 0; | ||
814 | isp->dma2_output.dma_out_mask = 0; | ||
815 | isp->dma2_output.err = DMA_OUTPUT_ERROR_NONE; | ||
816 | fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT); | ||
817 | } | ||
818 | |||
819 | /* Sensor */ | ||
820 | if (!test_bit(PARAM_SENSOR_FRAME_RATE, p_index1)) { | ||
821 | if (is->config_index == 0) | ||
822 | __is_set_sensor(is, 0); | ||
823 | } | ||
824 | |||
825 | /* DRC */ | ||
826 | drc->control.cmd = CONTROL_COMMAND_START; | ||
827 | __is_set_drc_control(is, CONTROL_BYPASS_ENABLE); | ||
828 | |||
829 | drc->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE; | ||
830 | if (!test_bit(PARAM_DRC_OTF_INPUT, p_index1)) { | ||
831 | drc->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH; | ||
832 | drc->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT; | ||
833 | fimc_is_set_param_bit(is, PARAM_DRC_OTF_INPUT); | ||
834 | } | ||
835 | drc->otf_input.format = OTF_INPUT_FORMAT_YUV444; | ||
836 | drc->otf_input.bitwidth = 12; | ||
837 | drc->otf_input.order = 0; | ||
838 | drc->otf_input.err = OTF_INPUT_ERROR_NONE; | ||
839 | |||
840 | drc->dma_input.cmd = DMA_INPUT_COMMAND_DISABLE; | ||
841 | drc->dma_input.width = 0; | ||
842 | drc->dma_input.height = 0; | ||
843 | drc->dma_input.format = 0; | ||
844 | drc->dma_input.bitwidth = 0; | ||
845 | drc->dma_input.plane = 0; | ||
846 | drc->dma_input.order = 0; | ||
847 | drc->dma_input.buffer_number = 0; | ||
848 | drc->dma_input.width = 0; | ||
849 | drc->dma_input.err = DMA_INPUT_ERROR_NONE; | ||
850 | fimc_is_set_param_bit(is, PARAM_DRC_DMA_INPUT); | ||
851 | |||
852 | drc->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE; | ||
853 | if (!test_bit(PARAM_DRC_OTF_OUTPUT, p_index1)) { | ||
854 | drc->otf_output.width = DEFAULT_PREVIEW_STILL_WIDTH; | ||
855 | drc->otf_output.height = DEFAULT_PREVIEW_STILL_HEIGHT; | ||
856 | fimc_is_set_param_bit(is, PARAM_DRC_OTF_OUTPUT); | ||
857 | } | ||
858 | drc->otf_output.format = OTF_OUTPUT_FORMAT_YUV444; | ||
859 | drc->otf_output.bitwidth = 8; | ||
860 | drc->otf_output.order = 0; | ||
861 | drc->otf_output.err = OTF_OUTPUT_ERROR_NONE; | ||
862 | |||
863 | /* FD */ | ||
864 | __is_set_fd_control(is, CONTROL_COMMAND_STOP); | ||
865 | fd->control.bypass = CONTROL_BYPASS_DISABLE; | ||
866 | |||
867 | fd->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE; | ||
868 | if (!test_bit((PARAM_FD_OTF_INPUT - 32), p_index2)) { | ||
869 | fd->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH; | ||
870 | fd->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT; | ||
871 | fimc_is_set_param_bit(is, PARAM_FD_OTF_INPUT); | ||
872 | } | ||
873 | |||
874 | fd->otf_input.format = OTF_INPUT_FORMAT_YUV444; | ||
875 | fd->otf_input.bitwidth = 8; | ||
876 | fd->otf_input.order = 0; | ||
877 | fd->otf_input.err = OTF_INPUT_ERROR_NONE; | ||
878 | |||
879 | fd->dma_input.cmd = DMA_INPUT_COMMAND_DISABLE; | ||
880 | fd->dma_input.width = 0; | ||
881 | fd->dma_input.height = 0; | ||
882 | fd->dma_input.format = 0; | ||
883 | fd->dma_input.bitwidth = 0; | ||
884 | fd->dma_input.plane = 0; | ||
885 | fd->dma_input.order = 0; | ||
886 | fd->dma_input.buffer_number = 0; | ||
887 | fd->dma_input.width = 0; | ||
888 | fd->dma_input.err = DMA_INPUT_ERROR_NONE; | ||
889 | fimc_is_set_param_bit(is, PARAM_FD_DMA_INPUT); | ||
890 | |||
891 | __is_set_fd_config_maxface(is, 5); | ||
892 | __is_set_fd_config_rollangle(is, FD_CONFIG_ROLL_ANGLE_FULL); | ||
893 | __is_set_fd_config_yawangle(is, FD_CONFIG_YAW_ANGLE_45_90); | ||
894 | __is_set_fd_config_smilemode(is, FD_CONFIG_SMILE_MODE_DISABLE); | ||
895 | __is_set_fd_config_blinkmode(is, FD_CONFIG_BLINK_MODE_DISABLE); | ||
896 | __is_set_fd_config_eyedetect(is, FD_CONFIG_EYES_DETECT_ENABLE); | ||
897 | __is_set_fd_config_mouthdetect(is, FD_CONFIG_MOUTH_DETECT_DISABLE); | ||
898 | __is_set_fd_config_orientation(is, FD_CONFIG_ORIENTATION_DISABLE); | ||
899 | __is_set_fd_config_orientation_val(is, 0); | ||
900 | } | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.h b/drivers/media/platform/exynos4-is/fimc-is-param.h new file mode 100644 index 000000000000..f9358c27ae2d --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-param.h | |||
@@ -0,0 +1,1020 @@ | |||
1 | /* | ||
2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver | ||
3 | * | ||
4 | * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Authors: Younghwan Joo <yhwan.joo@samsung.com> | ||
7 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #ifndef FIMC_IS_PARAM_H_ | ||
14 | #define FIMC_IS_PARAM_H_ | ||
15 | |||
16 | #include <linux/compiler.h> | ||
17 | |||
18 | #define FIMC_IS_CONFIG_TIMEOUT 3000 /* ms */ | ||
19 | #define IS_DEFAULT_WIDTH 1280 | ||
20 | #define IS_DEFAULT_HEIGHT 720 | ||
21 | |||
22 | #define DEFAULT_PREVIEW_STILL_WIDTH IS_DEFAULT_WIDTH | ||
23 | #define DEFAULT_PREVIEW_STILL_HEIGHT IS_DEFAULT_HEIGHT | ||
24 | #define DEFAULT_CAPTURE_STILL_WIDTH IS_DEFAULT_WIDTH | ||
25 | #define DEFAULT_CAPTURE_STILL_HEIGHT IS_DEFAULT_HEIGHT | ||
26 | #define DEFAULT_PREVIEW_VIDEO_WIDTH IS_DEFAULT_WIDTH | ||
27 | #define DEFAULT_PREVIEW_VIDEO_HEIGHT IS_DEFAULT_HEIGHT | ||
28 | #define DEFAULT_CAPTURE_VIDEO_WIDTH IS_DEFAULT_WIDTH | ||
29 | #define DEFAULT_CAPTURE_VIDEO_HEIGHT IS_DEFAULT_HEIGHT | ||
30 | |||
31 | #define DEFAULT_PREVIEW_STILL_FRAMERATE 30 | ||
32 | #define DEFAULT_CAPTURE_STILL_FRAMERATE 15 | ||
33 | #define DEFAULT_PREVIEW_VIDEO_FRAMERATE 30 | ||
34 | #define DEFAULT_CAPTURE_VIDEO_FRAMERATE 30 | ||
35 | |||
36 | #define FIMC_IS_REGION_VER 124 /* IS REGION VERSION 1.24 */ | ||
37 | #define FIMC_IS_PARAM_SIZE (FIMC_IS_REGION_SIZE + 1) | ||
38 | #define FIMC_IS_MAGIC_NUMBER 0x01020304 | ||
39 | #define FIMC_IS_PARAM_MAX_SIZE 64 /* in bytes */ | ||
40 | #define FIMC_IS_PARAM_MAX_ENTRIES (FIMC_IS_PARAM_MAX_SIZE / 4) | ||
41 | |||
42 | /* The parameter bitmask bit definitions. */ | ||
43 | enum is_param_bit { | ||
44 | PARAM_GLOBAL_SHOTMODE, | ||
45 | PARAM_SENSOR_CONTROL, | ||
46 | PARAM_SENSOR_OTF_OUTPUT, | ||
47 | PARAM_SENSOR_FRAME_RATE, | ||
48 | PARAM_BUFFER_CONTROL, | ||
49 | PARAM_BUFFER_OTF_INPUT, | ||
50 | PARAM_BUFFER_OTF_OUTPUT, | ||
51 | PARAM_ISP_CONTROL, | ||
52 | PARAM_ISP_OTF_INPUT, | ||
53 | PARAM_ISP_DMA1_INPUT, | ||
54 | /* 10 */ | ||
55 | PARAM_ISP_DMA2_INPUT, | ||
56 | PARAM_ISP_AA, | ||
57 | PARAM_ISP_FLASH, | ||
58 | PARAM_ISP_AWB, | ||
59 | PARAM_ISP_IMAGE_EFFECT, | ||
60 | PARAM_ISP_ISO, | ||
61 | PARAM_ISP_ADJUST, | ||
62 | PARAM_ISP_METERING, | ||
63 | PARAM_ISP_AFC, | ||
64 | PARAM_ISP_OTF_OUTPUT, | ||
65 | /* 20 */ | ||
66 | PARAM_ISP_DMA1_OUTPUT, | ||
67 | PARAM_ISP_DMA2_OUTPUT, | ||
68 | PARAM_DRC_CONTROL, | ||
69 | PARAM_DRC_OTF_INPUT, | ||
70 | PARAM_DRC_DMA_INPUT, | ||
71 | PARAM_DRC_OTF_OUTPUT, | ||
72 | PARAM_SCALERC_CONTROL, | ||
73 | PARAM_SCALERC_OTF_INPUT, | ||
74 | PARAM_SCALERC_IMAGE_EFFECT, | ||
75 | PARAM_SCALERC_INPUT_CROP, | ||
76 | /* 30 */ | ||
77 | PARAM_SCALERC_OUTPUT_CROP, | ||
78 | PARAM_SCALERC_OTF_OUTPUT, | ||
79 | PARAM_SCALERC_DMA_OUTPUT, | ||
80 | PARAM_ODC_CONTROL, | ||
81 | PARAM_ODC_OTF_INPUT, | ||
82 | PARAM_ODC_OTF_OUTPUT, | ||
83 | PARAM_DIS_CONTROL, | ||
84 | PARAM_DIS_OTF_INPUT, | ||
85 | PARAM_DIS_OTF_OUTPUT, | ||
86 | PARAM_TDNR_CONTROL, | ||
87 | /* 40 */ | ||
88 | PARAM_TDNR_OTF_INPUT, | ||
89 | PARAM_TDNR_1ST_FRAME, | ||
90 | PARAM_TDNR_OTF_OUTPUT, | ||
91 | PARAM_TDNR_DMA_OUTPUT, | ||
92 | PARAM_SCALERP_CONTROL, | ||
93 | PARAM_SCALERP_OTF_INPUT, | ||
94 | PARAM_SCALERP_IMAGE_EFFECT, | ||
95 | PARAM_SCALERP_INPUT_CROP, | ||
96 | PARAM_SCALERP_OUTPUT_CROP, | ||
97 | PARAM_SCALERP_ROTATION, | ||
98 | /* 50 */ | ||
99 | PARAM_SCALERP_FLIP, | ||
100 | PARAM_SCALERP_OTF_OUTPUT, | ||
101 | PARAM_SCALERP_DMA_OUTPUT, | ||
102 | PARAM_FD_CONTROL, | ||
103 | PARAM_FD_OTF_INPUT, | ||
104 | PARAM_FD_DMA_INPUT, | ||
105 | PARAM_FD_CONFIG, | ||
106 | }; | ||
107 | |||
108 | /* Interrupt map */ | ||
109 | #define FIMC_IS_INT_GENERAL 0 | ||
110 | #define FIMC_IS_INT_FRAME_DONE_ISP 1 | ||
111 | |||
112 | /* Input */ | ||
113 | |||
114 | #define CONTROL_COMMAND_STOP 0 | ||
115 | #define CONTROL_COMMAND_START 1 | ||
116 | |||
117 | #define CONTROL_BYPASS_DISABLE 0 | ||
118 | #define CONTROL_BYPASS_ENABLE 1 | ||
119 | |||
120 | #define CONTROL_ERROR_NONE 0 | ||
121 | |||
122 | /* OTF (On-The-Fly) input interface commands */ | ||
123 | #define OTF_INPUT_COMMAND_DISABLE 0 | ||
124 | #define OTF_INPUT_COMMAND_ENABLE 1 | ||
125 | |||
126 | /* OTF input interface color formats */ | ||
127 | enum oft_input_fmt { | ||
128 | OTF_INPUT_FORMAT_BAYER = 0, /* 1 channel */ | ||
129 | OTF_INPUT_FORMAT_YUV444 = 1, /* 3 channels */ | ||
130 | OTF_INPUT_FORMAT_YUV422 = 2, /* 3 channels */ | ||
131 | OTF_INPUT_FORMAT_YUV420 = 3, /* 3 channels */ | ||
132 | OTF_INPUT_FORMAT_STRGEN_COLORBAR_BAYER = 10, | ||
133 | OTF_INPUT_FORMAT_BAYER_DMA = 11, | ||
134 | }; | ||
135 | |||
136 | #define OTF_INPUT_ORDER_BAYER_GR_BG 0 | ||
137 | |||
138 | /* OTF input error codes */ | ||
139 | #define OTF_INPUT_ERROR_NONE 0 /* Input setting is done */ | ||
140 | |||
141 | /* DMA input commands */ | ||
142 | #define DMA_INPUT_COMMAND_DISABLE 0 | ||
143 | #define DMA_INPUT_COMMAND_ENABLE 1 | ||
144 | |||
145 | /* DMA input color formats */ | ||
146 | enum dma_input_fmt { | ||
147 | DMA_INPUT_FORMAT_BAYER = 0, | ||
148 | DMA_INPUT_FORMAT_YUV444 = 1, | ||
149 | DMA_INPUT_FORMAT_YUV422 = 2, | ||
150 | DMA_INPUT_FORMAT_YUV420 = 3, | ||
151 | }; | ||
152 | |||
153 | enum dma_input_order { | ||
154 | /* (for DMA_INPUT_PLANE_3) */ | ||
155 | DMA_INPUT_ORDER_NO = 0, | ||
156 | /* (only valid at DMA_INPUT_PLANE_2) */ | ||
157 | DMA_INPUT_ORDER_CBCR = 1, | ||
158 | /* (only valid at DMA_INPUT_PLANE_2) */ | ||
159 | DMA_INPUT_ORDER_CRCB = 2, | ||
160 | /* (only valid at DMA_INPUT_PLANE_1 & DMA_INPUT_FORMAT_YUV444) */ | ||
161 | DMA_INPUT_ORDER_YCBCR = 3, | ||
162 | /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ | ||
163 | DMA_INPUT_ORDER_YYCBCR = 4, | ||
164 | /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ | ||
165 | DMA_INPUT_ORDER_YCBYCR = 5, | ||
166 | /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ | ||
167 | DMA_INPUT_ORDER_YCRYCB = 6, | ||
168 | /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ | ||
169 | DMA_INPUT_ORDER_CBYCRY = 7, | ||
170 | /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */ | ||
171 | DMA_INPUT_ORDER_CRYCBY = 8, | ||
172 | /* (only valid at DMA_INPUT_FORMAT_BAYER) */ | ||
173 | DMA_INPUT_ORDER_GR_BG = 9 | ||
174 | }; | ||
175 | |||
176 | #define DMA_INPUT_ERROR_NONE 0 /* DMA input setting | ||
177 | is done */ | ||
178 | /* | ||
179 | * Data output parameter definitions | ||
180 | */ | ||
181 | #define OTF_OUTPUT_CROP_DISABLE 0 | ||
182 | #define OTF_OUTPUT_CROP_ENABLE 1 | ||
183 | |||
184 | #define OTF_OUTPUT_COMMAND_DISABLE 0 | ||
185 | #define OTF_OUTPUT_COMMAND_ENABLE 1 | ||
186 | |||
187 | enum otf_output_fmt { | ||
188 | OTF_OUTPUT_FORMAT_YUV444 = 1, | ||
189 | OTF_OUTPUT_FORMAT_YUV422 = 2, | ||
190 | OTF_OUTPUT_FORMAT_YUV420 = 3, | ||
191 | OTF_OUTPUT_FORMAT_RGB = 4, | ||
192 | }; | ||
193 | |||
194 | #define OTF_OUTPUT_ORDER_BAYER_GR_BG 0 | ||
195 | |||
196 | #define OTF_OUTPUT_ERROR_NONE 0 /* Output Setting is done */ | ||
197 | |||
198 | #define DMA_OUTPUT_COMMAND_DISABLE 0 | ||
199 | #define DMA_OUTPUT_COMMAND_ENABLE 1 | ||
200 | |||
201 | enum dma_output_fmt { | ||
202 | DMA_OUTPUT_FORMAT_BAYER = 0, | ||
203 | DMA_OUTPUT_FORMAT_YUV444 = 1, | ||
204 | DMA_OUTPUT_FORMAT_YUV422 = 2, | ||
205 | DMA_OUTPUT_FORMAT_YUV420 = 3, | ||
206 | DMA_OUTPUT_FORMAT_RGB = 4, | ||
207 | }; | ||
208 | |||
209 | enum dma_output_order { | ||
210 | DMA_OUTPUT_ORDER_NO = 0, | ||
211 | /* for DMA_OUTPUT_PLANE_3 */ | ||
212 | DMA_OUTPUT_ORDER_CBCR = 1, | ||
213 | /* only valid at DMA_INPUT_PLANE_2) */ | ||
214 | DMA_OUTPUT_ORDER_CRCB = 2, | ||
215 | /* only valid at DMA_OUTPUT_PLANE_2) */ | ||
216 | DMA_OUTPUT_ORDER_YYCBCR = 3, | ||
217 | /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */ | ||
218 | DMA_OUTPUT_ORDER_YCBYCR = 4, | ||
219 | /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */ | ||
220 | DMA_OUTPUT_ORDER_YCRYCB = 5, | ||
221 | /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */ | ||
222 | DMA_OUTPUT_ORDER_CBYCRY = 6, | ||
223 | /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */ | ||
224 | DMA_OUTPUT_ORDER_CRYCBY = 7, | ||
225 | /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */ | ||
226 | DMA_OUTPUT_ORDER_YCBCR = 8, | ||
227 | /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ | ||
228 | DMA_OUTPUT_ORDER_CRYCB = 9, | ||
229 | /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ | ||
230 | DMA_OUTPUT_ORDER_CRCBY = 10, | ||
231 | /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ | ||
232 | DMA_OUTPUT_ORDER_CBYCR = 11, | ||
233 | /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ | ||
234 | DMA_OUTPUT_ORDER_YCRCB = 12, | ||
235 | /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ | ||
236 | DMA_OUTPUT_ORDER_CBCRY = 13, | ||
237 | /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */ | ||
238 | DMA_OUTPUT_ORDER_BGR = 14, | ||
239 | /* only valid at DMA_OUTPUT_FORMAT_RGB */ | ||
240 | DMA_OUTPUT_ORDER_GB_BG = 15 | ||
241 | /* only valid at DMA_OUTPUT_FORMAT_BAYER */ | ||
242 | }; | ||
243 | |||
244 | /* enum dma_output_notify_dma_done */ | ||
245 | #define DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE 0 | ||
246 | #define DMA_OUTPUT_NOTIFY_DMA_DONE_ENABLE 1 | ||
247 | |||
248 | /* DMA output error codes */ | ||
249 | #define DMA_OUTPUT_ERROR_NONE 0 /* DMA output setting | ||
250 | is done */ | ||
251 | |||
252 | /* ---------------------- Global ----------------------------------- */ | ||
253 | #define GLOBAL_SHOTMODE_ERROR_NONE 0 /* shot-mode setting | ||
254 | is done */ | ||
255 | /* 3A lock commands */ | ||
256 | #define ISP_AA_COMMAND_START 0 | ||
257 | #define ISP_AA_COMMAND_STOP 1 | ||
258 | |||
259 | /* 3A lock target */ | ||
260 | #define ISP_AA_TARGET_AF 1 | ||
261 | #define ISP_AA_TARGET_AE 2 | ||
262 | #define ISP_AA_TARGET_AWB 4 | ||
263 | |||
264 | enum isp_af_mode { | ||
265 | ISP_AF_MODE_MANUAL = 0, | ||
266 | ISP_AF_MODE_SINGLE = 1, | ||
267 | ISP_AF_MODE_CONTINUOUS = 2, | ||
268 | ISP_AF_MODE_TOUCH = 3, | ||
269 | ISP_AF_MODE_SLEEP = 4, | ||
270 | ISP_AF_MODE_INIT = 5, | ||
271 | ISP_AF_MODE_SET_CENTER_WINDOW = 6, | ||
272 | ISP_AF_MODE_SET_TOUCH_WINDOW = 7 | ||
273 | }; | ||
274 | |||
275 | /* Face AF commands */ | ||
276 | #define ISP_AF_FACE_DISABLE 0 | ||
277 | #define ISP_AF_FACE_ENABLE 1 | ||
278 | |||
279 | /* AF range */ | ||
280 | #define ISP_AF_RANGE_NORMAL 0 | ||
281 | #define ISP_AF_RANGE_MACRO 1 | ||
282 | |||
283 | /* AF sleep */ | ||
284 | #define ISP_AF_SLEEP_OFF 0 | ||
285 | #define ISP_AF_SLEEP_ON 1 | ||
286 | |||
287 | /* Continuous AF commands */ | ||
288 | #define ISP_AF_CONTINUOUS_DISABLE 0 | ||
289 | #define ISP_AF_CONTINUOUS_ENABLE 1 | ||
290 | |||
291 | /* ISP AF error codes */ | ||
292 | #define ISP_AF_ERROR_NONE 0 /* AF mode change is done */ | ||
293 | #define ISP_AF_ERROR_NONE_LOCK_DONE 1 /* AF lock is done */ | ||
294 | |||
295 | /* Flash commands */ | ||
296 | #define ISP_FLASH_COMMAND_DISABLE 0 | ||
297 | #define ISP_FLASH_COMMAND_MANUAL_ON 1 /* (forced flash) */ | ||
298 | #define ISP_FLASH_COMMAND_AUTO 2 | ||
299 | #define ISP_FLASH_COMMAND_TORCH 3 /* 3 sec */ | ||
300 | |||
301 | /* Flash red-eye commads */ | ||
302 | #define ISP_FLASH_REDEYE_DISABLE 0 | ||
303 | #define ISP_FLASH_REDEYE_ENABLE 1 | ||
304 | |||
305 | /* Flash error codes */ | ||
306 | #define ISP_FLASH_ERROR_NONE 0 /* Flash setting is done */ | ||
307 | |||
308 | /* -------------------------- AWB ------------------------------------ */ | ||
309 | enum isp_awb_command { | ||
310 | ISP_AWB_COMMAND_AUTO = 0, | ||
311 | ISP_AWB_COMMAND_ILLUMINATION = 1, | ||
312 | ISP_AWB_COMMAND_MANUAL = 2 | ||
313 | }; | ||
314 | |||
315 | enum isp_awb_illumination { | ||
316 | ISP_AWB_ILLUMINATION_DAYLIGHT = 0, | ||
317 | ISP_AWB_ILLUMINATION_CLOUDY = 1, | ||
318 | ISP_AWB_ILLUMINATION_TUNGSTEN = 2, | ||
319 | ISP_AWB_ILLUMINATION_FLUORESCENT = 3 | ||
320 | }; | ||
321 | |||
322 | /* ISP AWN error codes */ | ||
323 | #define ISP_AWB_ERROR_NONE 0 /* AWB setting is done */ | ||
324 | |||
325 | /* -------------------------- Effect ----------------------------------- */ | ||
326 | enum isp_imageeffect_command { | ||
327 | ISP_IMAGE_EFFECT_DISABLE = 0, | ||
328 | ISP_IMAGE_EFFECT_MONOCHROME = 1, | ||
329 | ISP_IMAGE_EFFECT_NEGATIVE_MONO = 2, | ||
330 | ISP_IMAGE_EFFECT_NEGATIVE_COLOR = 3, | ||
331 | ISP_IMAGE_EFFECT_SEPIA = 4 | ||
332 | }; | ||
333 | |||
334 | /* Image effect error codes */ | ||
335 | #define ISP_IMAGE_EFFECT_ERROR_NONE 0 /* Image effect setting | ||
336 | is done */ | ||
337 | /* ISO commands */ | ||
338 | #define ISP_ISO_COMMAND_AUTO 0 | ||
339 | #define ISP_ISO_COMMAND_MANUAL 1 | ||
340 | |||
341 | /* ISO error codes */ | ||
342 | #define ISP_ISO_ERROR_NONE 0 /* ISO setting is done */ | ||
343 | |||
344 | /* ISP adjust commands */ | ||
345 | #define ISP_ADJUST_COMMAND_AUTO (0 << 0) | ||
346 | #define ISP_ADJUST_COMMAND_MANUAL_CONTRAST (1 << 0) | ||
347 | #define ISP_ADJUST_COMMAND_MANUAL_SATURATION (1 << 1) | ||
348 | #define ISP_ADJUST_COMMAND_MANUAL_SHARPNESS (1 << 2) | ||
349 | #define ISP_ADJUST_COMMAND_MANUAL_EXPOSURE (1 << 3) | ||
350 | #define ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS (1 << 4) | ||
351 | #define ISP_ADJUST_COMMAND_MANUAL_HUE (1 << 5) | ||
352 | #define ISP_ADJUST_COMMAND_MANUAL_ALL 0x7f | ||
353 | |||
354 | /* ISP adjustment error codes */ | ||
355 | #define ISP_ADJUST_ERROR_NONE 0 /* Adjust setting is done */ | ||
356 | |||
357 | /* | ||
358 | * Exposure metering | ||
359 | */ | ||
360 | enum isp_metering_command { | ||
361 | ISP_METERING_COMMAND_AVERAGE = 0, | ||
362 | ISP_METERING_COMMAND_SPOT = 1, | ||
363 | ISP_METERING_COMMAND_MATRIX = 2, | ||
364 | ISP_METERING_COMMAND_CENTER = 3 | ||
365 | }; | ||
366 | |||
367 | /* ISP metering error codes */ | ||
368 | #define ISP_METERING_ERROR_NONE 0 /* Metering setting is done */ | ||
369 | |||
370 | /* | ||
371 | * AFC | ||
372 | */ | ||
373 | enum isp_afc_command { | ||
374 | ISP_AFC_COMMAND_DISABLE = 0, | ||
375 | ISP_AFC_COMMAND_AUTO = 1, | ||
376 | ISP_AFC_COMMAND_MANUAL = 2, | ||
377 | }; | ||
378 | |||
379 | #define ISP_AFC_MANUAL_50HZ 50 | ||
380 | #define ISP_AFC_MANUAL_60HZ 60 | ||
381 | |||
382 | /* ------------------------ SCENE MODE--------------------------------- */ | ||
383 | enum isp_scene_mode { | ||
384 | ISP_SCENE_NONE = 0, | ||
385 | ISP_SCENE_PORTRAIT = 1, | ||
386 | ISP_SCENE_LANDSCAPE = 2, | ||
387 | ISP_SCENE_SPORTS = 3, | ||
388 | ISP_SCENE_PARTYINDOOR = 4, | ||
389 | ISP_SCENE_BEACHSNOW = 5, | ||
390 | ISP_SCENE_SUNSET = 6, | ||
391 | ISP_SCENE_DAWN = 7, | ||
392 | ISP_SCENE_FALL = 8, | ||
393 | ISP_SCENE_NIGHT = 9, | ||
394 | ISP_SCENE_AGAINSTLIGHTWLIGHT = 10, | ||
395 | ISP_SCENE_AGAINSTLIGHTWOLIGHT = 11, | ||
396 | ISP_SCENE_FIRE = 12, | ||
397 | ISP_SCENE_TEXT = 13, | ||
398 | ISP_SCENE_CANDLE = 14 | ||
399 | }; | ||
400 | |||
401 | /* AFC error codes */ | ||
402 | #define ISP_AFC_ERROR_NONE 0 /* AFC setting is done */ | ||
403 | |||
404 | /* ---------------------------- FD ------------------------------------- */ | ||
405 | enum fd_config_command { | ||
406 | FD_CONFIG_COMMAND_MAXIMUM_NUMBER = 0x1, | ||
407 | FD_CONFIG_COMMAND_ROLL_ANGLE = 0x2, | ||
408 | FD_CONFIG_COMMAND_YAW_ANGLE = 0x4, | ||
409 | FD_CONFIG_COMMAND_SMILE_MODE = 0x8, | ||
410 | FD_CONFIG_COMMAND_BLINK_MODE = 0x10, | ||
411 | FD_CONFIG_COMMAND_EYES_DETECT = 0x20, | ||
412 | FD_CONFIG_COMMAND_MOUTH_DETECT = 0x40, | ||
413 | FD_CONFIG_COMMAND_ORIENTATION = 0x80, | ||
414 | FD_CONFIG_COMMAND_ORIENTATION_VALUE = 0x100 | ||
415 | }; | ||
416 | |||
417 | enum fd_config_roll_angle { | ||
418 | FD_CONFIG_ROLL_ANGLE_BASIC = 0, | ||
419 | FD_CONFIG_ROLL_ANGLE_PRECISE_BASIC = 1, | ||
420 | FD_CONFIG_ROLL_ANGLE_SIDES = 2, | ||
421 | FD_CONFIG_ROLL_ANGLE_PRECISE_SIDES = 3, | ||
422 | FD_CONFIG_ROLL_ANGLE_FULL = 4, | ||
423 | FD_CONFIG_ROLL_ANGLE_PRECISE_FULL = 5, | ||
424 | }; | ||
425 | |||
426 | enum fd_config_yaw_angle { | ||
427 | FD_CONFIG_YAW_ANGLE_0 = 0, | ||
428 | FD_CONFIG_YAW_ANGLE_45 = 1, | ||
429 | FD_CONFIG_YAW_ANGLE_90 = 2, | ||
430 | FD_CONFIG_YAW_ANGLE_45_90 = 3, | ||
431 | }; | ||
432 | |||
433 | /* Smile mode configuration */ | ||
434 | #define FD_CONFIG_SMILE_MODE_DISABLE 0 | ||
435 | #define FD_CONFIG_SMILE_MODE_ENABLE 1 | ||
436 | |||
437 | /* Blink mode configuration */ | ||
438 | #define FD_CONFIG_BLINK_MODE_DISABLE 0 | ||
439 | #define FD_CONFIG_BLINK_MODE_ENABLE 1 | ||
440 | |||
441 | /* Eyes detection configuration */ | ||
442 | #define FD_CONFIG_EYES_DETECT_DISABLE 0 | ||
443 | #define FD_CONFIG_EYES_DETECT_ENABLE 1 | ||
444 | |||
445 | /* Mouth detection configuration */ | ||
446 | #define FD_CONFIG_MOUTH_DETECT_DISABLE 0 | ||
447 | #define FD_CONFIG_MOUTH_DETECT_ENABLE 1 | ||
448 | |||
449 | #define FD_CONFIG_ORIENTATION_DISABLE 0 | ||
450 | #define FD_CONFIG_ORIENTATION_ENABLE 1 | ||
451 | |||
452 | struct param_control { | ||
453 | u32 cmd; | ||
454 | u32 bypass; | ||
455 | u32 buffer_address; | ||
456 | u32 buffer_size; | ||
457 | u32 skip_frames; /* only valid at ISP */ | ||
458 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 6]; | ||
459 | u32 err; | ||
460 | }; | ||
461 | |||
462 | struct param_otf_input { | ||
463 | u32 cmd; | ||
464 | u32 width; | ||
465 | u32 height; | ||
466 | u32 format; | ||
467 | u32 bitwidth; | ||
468 | u32 order; | ||
469 | u32 crop_offset_x; | ||
470 | u32 crop_offset_y; | ||
471 | u32 crop_width; | ||
472 | u32 crop_height; | ||
473 | u32 frametime_min; | ||
474 | u32 frametime_max; | ||
475 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 13]; | ||
476 | u32 err; | ||
477 | }; | ||
478 | |||
479 | struct param_dma_input { | ||
480 | u32 cmd; | ||
481 | u32 width; | ||
482 | u32 height; | ||
483 | u32 format; | ||
484 | u32 bitwidth; | ||
485 | u32 plane; | ||
486 | u32 order; | ||
487 | u32 buffer_number; | ||
488 | u32 buffer_address; | ||
489 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 10]; | ||
490 | u32 err; | ||
491 | }; | ||
492 | |||
493 | struct param_otf_output { | ||
494 | u32 cmd; | ||
495 | u32 width; | ||
496 | u32 height; | ||
497 | u32 format; | ||
498 | u32 bitwidth; | ||
499 | u32 order; | ||
500 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 7]; | ||
501 | u32 err; | ||
502 | }; | ||
503 | |||
504 | struct param_dma_output { | ||
505 | u32 cmd; | ||
506 | u32 width; | ||
507 | u32 height; | ||
508 | u32 format; | ||
509 | u32 bitwidth; | ||
510 | u32 plane; | ||
511 | u32 order; | ||
512 | u32 buffer_number; | ||
513 | u32 buffer_address; | ||
514 | u32 notify_dma_done; | ||
515 | u32 dma_out_mask; | ||
516 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 12]; | ||
517 | u32 err; | ||
518 | }; | ||
519 | |||
520 | struct param_global_shotmode { | ||
521 | u32 cmd; | ||
522 | u32 skip_frames; | ||
523 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3]; | ||
524 | u32 err; | ||
525 | }; | ||
526 | |||
527 | struct param_sensor_framerate { | ||
528 | u32 frame_rate; | ||
529 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2]; | ||
530 | u32 err; | ||
531 | }; | ||
532 | |||
533 | struct param_isp_aa { | ||
534 | u32 cmd; | ||
535 | u32 target; | ||
536 | u32 mode; | ||
537 | u32 scene; | ||
538 | u32 sleep; | ||
539 | u32 face; | ||
540 | u32 touch_x; | ||
541 | u32 touch_y; | ||
542 | u32 manual_af_setting; | ||
543 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 10]; | ||
544 | u32 err; | ||
545 | }; | ||
546 | |||
547 | struct param_isp_flash { | ||
548 | u32 cmd; | ||
549 | u32 redeye; | ||
550 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3]; | ||
551 | u32 err; | ||
552 | }; | ||
553 | |||
554 | struct param_isp_awb { | ||
555 | u32 cmd; | ||
556 | u32 illumination; | ||
557 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3]; | ||
558 | u32 err; | ||
559 | }; | ||
560 | |||
561 | struct param_isp_imageeffect { | ||
562 | u32 cmd; | ||
563 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2]; | ||
564 | u32 err; | ||
565 | }; | ||
566 | |||
567 | struct param_isp_iso { | ||
568 | u32 cmd; | ||
569 | u32 value; | ||
570 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3]; | ||
571 | u32 err; | ||
572 | }; | ||
573 | |||
574 | struct param_isp_adjust { | ||
575 | u32 cmd; | ||
576 | s32 contrast; | ||
577 | s32 saturation; | ||
578 | s32 sharpness; | ||
579 | s32 exposure; | ||
580 | s32 brightness; | ||
581 | s32 hue; | ||
582 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 8]; | ||
583 | u32 err; | ||
584 | }; | ||
585 | |||
586 | struct param_isp_metering { | ||
587 | u32 cmd; | ||
588 | u32 win_pos_x; | ||
589 | u32 win_pos_y; | ||
590 | u32 win_width; | ||
591 | u32 win_height; | ||
592 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 6]; | ||
593 | u32 err; | ||
594 | }; | ||
595 | |||
596 | struct param_isp_afc { | ||
597 | u32 cmd; | ||
598 | u32 manual; | ||
599 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3]; | ||
600 | u32 err; | ||
601 | }; | ||
602 | |||
603 | struct param_scaler_imageeffect { | ||
604 | u32 cmd; | ||
605 | u32 arbitrary_cb; | ||
606 | u32 arbitrary_cr; | ||
607 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 4]; | ||
608 | u32 err; | ||
609 | }; | ||
610 | |||
611 | struct param_scaler_input_crop { | ||
612 | u32 cmd; | ||
613 | u32 crop_offset_x; | ||
614 | u32 crop_offset_y; | ||
615 | u32 crop_width; | ||
616 | u32 crop_height; | ||
617 | u32 in_width; | ||
618 | u32 in_height; | ||
619 | u32 out_width; | ||
620 | u32 out_height; | ||
621 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 10]; | ||
622 | u32 err; | ||
623 | }; | ||
624 | |||
625 | struct param_scaler_output_crop { | ||
626 | u32 cmd; | ||
627 | u32 crop_offset_x; | ||
628 | u32 crop_offset_y; | ||
629 | u32 crop_width; | ||
630 | u32 crop_height; | ||
631 | u32 out_format; | ||
632 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 7]; | ||
633 | u32 err; | ||
634 | }; | ||
635 | |||
636 | struct param_scaler_rotation { | ||
637 | u32 cmd; | ||
638 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2]; | ||
639 | u32 err; | ||
640 | }; | ||
641 | |||
642 | struct param_scaler_flip { | ||
643 | u32 cmd; | ||
644 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2]; | ||
645 | u32 err; | ||
646 | }; | ||
647 | |||
648 | struct param_3dnr_1stframe { | ||
649 | u32 cmd; | ||
650 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2]; | ||
651 | u32 err; | ||
652 | }; | ||
653 | |||
654 | struct param_fd_config { | ||
655 | u32 cmd; | ||
656 | u32 max_number; | ||
657 | u32 roll_angle; | ||
658 | u32 yaw_angle; | ||
659 | u32 smile_mode; | ||
660 | u32 blink_mode; | ||
661 | u32 eye_detect; | ||
662 | u32 mouth_detect; | ||
663 | u32 orientation; | ||
664 | u32 orientation_value; | ||
665 | u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 11]; | ||
666 | u32 err; | ||
667 | }; | ||
668 | |||
669 | struct global_param { | ||
670 | struct param_global_shotmode shotmode; | ||
671 | }; | ||
672 | |||
673 | struct sensor_param { | ||
674 | struct param_control control; | ||
675 | struct param_otf_output otf_output; | ||
676 | struct param_sensor_framerate frame_rate; | ||
677 | } __packed; | ||
678 | |||
679 | struct buffer_param { | ||
680 | struct param_control control; | ||
681 | struct param_otf_input otf_input; | ||
682 | struct param_otf_output otf_output; | ||
683 | } __packed; | ||
684 | |||
685 | struct isp_param { | ||
686 | struct param_control control; | ||
687 | struct param_otf_input otf_input; | ||
688 | struct param_dma_input dma1_input; | ||
689 | struct param_dma_input dma2_input; | ||
690 | struct param_isp_aa aa; | ||
691 | struct param_isp_flash flash; | ||
692 | struct param_isp_awb awb; | ||
693 | struct param_isp_imageeffect effect; | ||
694 | struct param_isp_iso iso; | ||
695 | struct param_isp_adjust adjust; | ||
696 | struct param_isp_metering metering; | ||
697 | struct param_isp_afc afc; | ||
698 | struct param_otf_output otf_output; | ||
699 | struct param_dma_output dma1_output; | ||
700 | struct param_dma_output dma2_output; | ||
701 | } __packed; | ||
702 | |||
703 | struct drc_param { | ||
704 | struct param_control control; | ||
705 | struct param_otf_input otf_input; | ||
706 | struct param_dma_input dma_input; | ||
707 | struct param_otf_output otf_output; | ||
708 | } __packed; | ||
709 | |||
710 | struct scalerc_param { | ||
711 | struct param_control control; | ||
712 | struct param_otf_input otf_input; | ||
713 | struct param_scaler_imageeffect effect; | ||
714 | struct param_scaler_input_crop input_crop; | ||
715 | struct param_scaler_output_crop output_crop; | ||
716 | struct param_otf_output otf_output; | ||
717 | struct param_dma_output dma_output; | ||
718 | } __packed; | ||
719 | |||
720 | struct odc_param { | ||
721 | struct param_control control; | ||
722 | struct param_otf_input otf_input; | ||
723 | struct param_otf_output otf_output; | ||
724 | } __packed; | ||
725 | |||
726 | struct dis_param { | ||
727 | struct param_control control; | ||
728 | struct param_otf_output otf_input; | ||
729 | struct param_otf_output otf_output; | ||
730 | } __packed; | ||
731 | |||
732 | struct tdnr_param { | ||
733 | struct param_control control; | ||
734 | struct param_otf_input otf_input; | ||
735 | struct param_3dnr_1stframe frame; | ||
736 | struct param_otf_output otf_output; | ||
737 | struct param_dma_output dma_output; | ||
738 | } __packed; | ||
739 | |||
740 | struct scalerp_param { | ||
741 | struct param_control control; | ||
742 | struct param_otf_input otf_input; | ||
743 | struct param_scaler_imageeffect effect; | ||
744 | struct param_scaler_input_crop input_crop; | ||
745 | struct param_scaler_output_crop output_crop; | ||
746 | struct param_scaler_rotation rotation; | ||
747 | struct param_scaler_flip flip; | ||
748 | struct param_otf_output otf_output; | ||
749 | struct param_dma_output dma_output; | ||
750 | } __packed; | ||
751 | |||
752 | struct fd_param { | ||
753 | struct param_control control; | ||
754 | struct param_otf_input otf_input; | ||
755 | struct param_dma_input dma_input; | ||
756 | struct param_fd_config config; | ||
757 | } __packed; | ||
758 | |||
759 | struct is_param_region { | ||
760 | struct global_param global; | ||
761 | struct sensor_param sensor; | ||
762 | struct buffer_param buf; | ||
763 | struct isp_param isp; | ||
764 | struct drc_param drc; | ||
765 | struct scalerc_param scalerc; | ||
766 | struct odc_param odc; | ||
767 | struct dis_param dis; | ||
768 | struct tdnr_param tdnr; | ||
769 | struct scalerp_param scalerp; | ||
770 | struct fd_param fd; | ||
771 | } __packed; | ||
772 | |||
773 | #define NUMBER_OF_GAMMA_CURVE_POINTS 32 | ||
774 | |||
775 | struct is_tune_sensor { | ||
776 | u32 exposure; | ||
777 | u32 analog_gain; | ||
778 | u32 frame_rate; | ||
779 | u32 actuator_position; | ||
780 | }; | ||
781 | |||
782 | struct is_tune_gammacurve { | ||
783 | u32 num_pts_x[NUMBER_OF_GAMMA_CURVE_POINTS]; | ||
784 | u32 num_pts_y_r[NUMBER_OF_GAMMA_CURVE_POINTS]; | ||
785 | u32 num_pts_y_g[NUMBER_OF_GAMMA_CURVE_POINTS]; | ||
786 | u32 num_pts_y_b[NUMBER_OF_GAMMA_CURVE_POINTS]; | ||
787 | }; | ||
788 | |||
789 | struct is_tune_isp { | ||
790 | /* Brightness level: range 0...100, default 7. */ | ||
791 | u32 brightness_level; | ||
792 | /* Contrast level: range -127...127, default 0. */ | ||
793 | s32 contrast_level; | ||
794 | /* Saturation level: range -127...127, default 0. */ | ||
795 | s32 saturation_level; | ||
796 | s32 gamma_level; | ||
797 | struct is_tune_gammacurve gamma_curve[4]; | ||
798 | /* Hue: range -127...127, default 0. */ | ||
799 | s32 hue; | ||
800 | /* Sharpness blur: range -127...127, default 0. */ | ||
801 | s32 sharpness_blur; | ||
802 | /* Despeckle : range -127~127, default : 0 */ | ||
803 | s32 despeckle; | ||
804 | /* Edge color supression: range -127...127, default 0. */ | ||
805 | s32 edge_color_supression; | ||
806 | /* Noise reduction: range -127...127, default 0. */ | ||
807 | s32 noise_reduction; | ||
808 | /* (32 * 4 + 9) * 4 = 548 bytes */ | ||
809 | } __packed; | ||
810 | |||
811 | struct is_tune_region { | ||
812 | struct is_tune_sensor sensor; | ||
813 | struct is_tune_isp isp; | ||
814 | } __packed; | ||
815 | |||
816 | struct rational { | ||
817 | u32 num; | ||
818 | u32 den; | ||
819 | }; | ||
820 | |||
821 | struct srational { | ||
822 | s32 num; | ||
823 | s32 den; | ||
824 | }; | ||
825 | |||
826 | #define FLASH_FIRED_SHIFT 0 | ||
827 | #define FLASH_NOT_FIRED 0 | ||
828 | #define FLASH_FIRED 1 | ||
829 | |||
830 | #define FLASH_STROBE_SHIFT 1 | ||
831 | #define FLASH_STROBE_NO_DETECTION 0 | ||
832 | #define FLASH_STROBE_RESERVED 1 | ||
833 | #define FLASH_STROBE_RETURN_LIGHT_NOT_DETECTED 2 | ||
834 | #define FLASH_STROBE_RETURN_LIGHT_DETECTED 3 | ||
835 | |||
836 | #define FLASH_MODE_SHIFT 3 | ||
837 | #define FLASH_MODE_UNKNOWN 0 | ||
838 | #define FLASH_MODE_COMPULSORY_FLASH_FIRING 1 | ||
839 | #define FLASH_MODE_COMPULSORY_FLASH_SUPPRESSION 2 | ||
840 | #define FLASH_MODE_AUTO_MODE 3 | ||
841 | |||
842 | #define FLASH_FUNCTION_SHIFT 5 | ||
843 | #define FLASH_FUNCTION_PRESENT 0 | ||
844 | #define FLASH_FUNCTION_NONE 1 | ||
845 | |||
846 | #define FLASH_RED_EYE_SHIFT 6 | ||
847 | #define FLASH_RED_EYE_DISABLED 0 | ||
848 | #define FLASH_RED_EYE_SUPPORTED 1 | ||
849 | |||
850 | enum apex_aperture_value { | ||
851 | F1_0 = 0, | ||
852 | F1_4 = 1, | ||
853 | F2_0 = 2, | ||
854 | F2_8 = 3, | ||
855 | F4_0 = 4, | ||
856 | F5_6 = 5, | ||
857 | F8_9 = 6, | ||
858 | F11_0 = 7, | ||
859 | F16_0 = 8, | ||
860 | F22_0 = 9, | ||
861 | F32_0 = 10, | ||
862 | }; | ||
863 | |||
864 | struct exif_attribute { | ||
865 | struct rational exposure_time; | ||
866 | struct srational shutter_speed; | ||
867 | u32 iso_speed_rating; | ||
868 | u32 flash; | ||
869 | struct srational brightness; | ||
870 | } __packed; | ||
871 | |||
872 | struct is_frame_header { | ||
873 | u32 valid; | ||
874 | u32 bad_mark; | ||
875 | u32 captured; | ||
876 | u32 frame_number; | ||
877 | struct exif_attribute exif; | ||
878 | } __packed; | ||
879 | |||
880 | struct is_fd_rect { | ||
881 | u32 offset_x; | ||
882 | u32 offset_y; | ||
883 | u32 width; | ||
884 | u32 height; | ||
885 | }; | ||
886 | |||
887 | struct is_face_marker { | ||
888 | u32 frame_number; | ||
889 | struct is_fd_rect face; | ||
890 | struct is_fd_rect left_eye; | ||
891 | struct is_fd_rect right_eye; | ||
892 | struct is_fd_rect mouth; | ||
893 | u32 roll_angle; | ||
894 | u32 yaw_angle; | ||
895 | u32 confidence; | ||
896 | s32 smile_level; | ||
897 | s32 blink_level; | ||
898 | } __packed; | ||
899 | |||
900 | #define MAX_FRAME_COUNT 8 | ||
901 | #define MAX_FRAME_COUNT_PREVIEW 4 | ||
902 | #define MAX_FRAME_COUNT_CAPTURE 1 | ||
903 | #define MAX_FACE_COUNT 16 | ||
904 | #define MAX_SHARED_COUNT 500 | ||
905 | |||
906 | struct is_region { | ||
907 | struct is_param_region parameter; | ||
908 | struct is_tune_region tune; | ||
909 | struct is_frame_header header[MAX_FRAME_COUNT]; | ||
910 | struct is_face_marker face[MAX_FACE_COUNT]; | ||
911 | u32 shared[MAX_SHARED_COUNT]; | ||
912 | } __packed; | ||
913 | |||
914 | struct is_debug_frame_descriptor { | ||
915 | u32 sensor_frame_time; | ||
916 | u32 sensor_exposure_time; | ||
917 | s32 sensor_analog_gain; | ||
918 | /* monitor for AA */ | ||
919 | u32 req_lei; | ||
920 | |||
921 | u32 next_next_lei_exp; | ||
922 | u32 next_next_lei_a_gain; | ||
923 | u32 next_next_lei_d_gain; | ||
924 | u32 next_next_lei_statlei; | ||
925 | u32 next_next_lei_lei; | ||
926 | |||
927 | u32 dummy0; | ||
928 | }; | ||
929 | |||
930 | #define MAX_FRAMEDESCRIPTOR_CONTEXT_NUM (30*20) /* 600 frames */ | ||
931 | #define MAX_VERSION_DISPLAY_BUF 32 | ||
932 | |||
933 | struct is_share_region { | ||
934 | u32 frame_time; | ||
935 | u32 exposure_time; | ||
936 | s32 analog_gain; | ||
937 | |||
938 | u32 r_gain; | ||
939 | u32 g_gain; | ||
940 | u32 b_gain; | ||
941 | |||
942 | u32 af_position; | ||
943 | u32 af_status; | ||
944 | /* 0 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_NOMESSAGE */ | ||
945 | /* 1 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_REACHED */ | ||
946 | /* 2 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_UNABLETOREACH */ | ||
947 | /* 3 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_LOST */ | ||
948 | /* default : unknown */ | ||
949 | u32 af_scene_type; | ||
950 | |||
951 | u32 frame_descp_onoff_control; | ||
952 | u32 frame_descp_update_done; | ||
953 | u32 frame_descp_idx; | ||
954 | u32 frame_descp_max_idx; | ||
955 | struct is_debug_frame_descriptor | ||
956 | dbg_frame_descp_ctx[MAX_FRAMEDESCRIPTOR_CONTEXT_NUM]; | ||
957 | |||
958 | u32 chip_id; | ||
959 | u32 chip_rev_no; | ||
960 | u8 isp_fw_ver_no[MAX_VERSION_DISPLAY_BUF]; | ||
961 | u8 isp_fw_ver_date[MAX_VERSION_DISPLAY_BUF]; | ||
962 | u8 sirc_sdk_ver_no[MAX_VERSION_DISPLAY_BUF]; | ||
963 | u8 sirc_sdk_rev_no[MAX_VERSION_DISPLAY_BUF]; | ||
964 | u8 sirc_sdk_rev_date[MAX_VERSION_DISPLAY_BUF]; | ||
965 | } __packed; | ||
966 | |||
967 | struct is_debug_control { | ||
968 | u32 write_point; /* 0~ 500KB boundary */ | ||
969 | u32 assert_flag; /* 0: Not invoked, 1: Invoked */ | ||
970 | u32 pabort_flag; /* 0: Not invoked, 1: Invoked */ | ||
971 | u32 dabort_flag; /* 0: Not invoked, 1: Invoked */ | ||
972 | }; | ||
973 | |||
974 | struct sensor_open_extended { | ||
975 | u32 actuator_type; | ||
976 | u32 mclk; | ||
977 | u32 mipi_lane_num; | ||
978 | u32 mipi_speed; | ||
979 | /* Skip setfile loading when fast_open_sensor is not 0 */ | ||
980 | u32 fast_open_sensor; | ||
981 | /* Activating sensor self calibration mode (6A3) */ | ||
982 | u32 self_calibration_mode; | ||
983 | /* This field is to adjust I2c clock based on ACLK200 */ | ||
984 | /* This value is varied in case of rev 0.2 */ | ||
985 | u32 i2c_sclk; | ||
986 | }; | ||
987 | |||
988 | struct fimc_is; | ||
989 | |||
990 | int fimc_is_hw_get_sensor_max_framerate(struct fimc_is *is); | ||
991 | void fimc_is_set_initial_params(struct fimc_is *is); | ||
992 | unsigned int __get_pending_param_count(struct fimc_is *is); | ||
993 | |||
994 | int __is_hw_update_params(struct fimc_is *is); | ||
995 | void __is_get_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf); | ||
996 | void __is_set_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf); | ||
997 | void __is_set_sensor(struct fimc_is *is, int fps); | ||
998 | void __is_set_isp_aa_ae(struct fimc_is *is); | ||
999 | void __is_set_isp_flash(struct fimc_is *is, u32 cmd, u32 redeye); | ||
1000 | void __is_set_isp_awb(struct fimc_is *is, u32 cmd, u32 val); | ||
1001 | void __is_set_isp_effect(struct fimc_is *is, u32 cmd); | ||
1002 | void __is_set_isp_iso(struct fimc_is *is, u32 cmd, u32 val); | ||
1003 | void __is_set_isp_adjust(struct fimc_is *is, u32 cmd, u32 val); | ||
1004 | void __is_set_isp_metering(struct fimc_is *is, u32 id, u32 val); | ||
1005 | void __is_set_isp_afc(struct fimc_is *is, u32 cmd, u32 val); | ||
1006 | void __is_set_drc_control(struct fimc_is *is, u32 val); | ||
1007 | void __is_set_fd_control(struct fimc_is *is, u32 val); | ||
1008 | void __is_set_fd_config_maxface(struct fimc_is *is, u32 val); | ||
1009 | void __is_set_fd_config_rollangle(struct fimc_is *is, u32 val); | ||
1010 | void __is_set_fd_config_yawangle(struct fimc_is *is, u32 val); | ||
1011 | void __is_set_fd_config_smilemode(struct fimc_is *is, u32 val); | ||
1012 | void __is_set_fd_config_blinkmode(struct fimc_is *is, u32 val); | ||
1013 | void __is_set_fd_config_eyedetect(struct fimc_is *is, u32 val); | ||
1014 | void __is_set_fd_config_mouthdetect(struct fimc_is *is, u32 val); | ||
1015 | void __is_set_fd_config_orientation(struct fimc_is *is, u32 val); | ||
1016 | void __is_set_fd_config_orientation_val(struct fimc_is *is, u32 val); | ||
1017 | void __is_set_isp_aa_af_mode(struct fimc_is *is, int cmd); | ||
1018 | void __is_set_isp_aa_af_start_stop(struct fimc_is *is, int cmd); | ||
1019 | |||
1020 | #endif | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c new file mode 100644 index 000000000000..b0ff67bc1b05 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c | |||
@@ -0,0 +1,243 @@ | |||
1 | /* | ||
2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver | ||
3 | * | ||
4 | * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Authors: Younghwan Joo <yhwan.joo@samsung.com> | ||
7 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #include <linux/delay.h> | ||
14 | |||
15 | #include "fimc-is.h" | ||
16 | #include "fimc-is-command.h" | ||
17 | #include "fimc-is-regs.h" | ||
18 | #include "fimc-is-sensor.h" | ||
19 | |||
20 | void fimc_is_fw_clear_irq1(struct fimc_is *is, unsigned int nr) | ||
21 | { | ||
22 | mcuctl_write(1UL << nr, is, MCUCTL_REG_INTCR1); | ||
23 | } | ||
24 | |||
25 | void fimc_is_fw_clear_irq2(struct fimc_is *is) | ||
26 | { | ||
27 | u32 cfg = mcuctl_read(is, MCUCTL_REG_INTSR2); | ||
28 | mcuctl_write(cfg, is, MCUCTL_REG_INTCR2); | ||
29 | } | ||
30 | |||
31 | void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is) | ||
32 | { | ||
33 | mcuctl_write(INTGR0_INTGD(0), is, MCUCTL_REG_INTGR0); | ||
34 | } | ||
35 | |||
36 | int fimc_is_hw_wait_intsr0_intsd0(struct fimc_is *is) | ||
37 | { | ||
38 | unsigned int timeout = 2000; | ||
39 | u32 cfg, status; | ||
40 | |||
41 | cfg = mcuctl_read(is, MCUCTL_REG_INTSR0); | ||
42 | status = INTSR0_GET_INTSD(0, cfg); | ||
43 | |||
44 | while (status) { | ||
45 | cfg = mcuctl_read(is, MCUCTL_REG_INTSR0); | ||
46 | status = INTSR0_GET_INTSD(0, cfg); | ||
47 | if (timeout == 0) { | ||
48 | dev_warn(&is->pdev->dev, "%s timeout\n", | ||
49 | __func__); | ||
50 | return -ETIME; | ||
51 | } | ||
52 | timeout--; | ||
53 | udelay(1); | ||
54 | } | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is) | ||
59 | { | ||
60 | unsigned int timeout = 2000; | ||
61 | u32 cfg, status; | ||
62 | |||
63 | cfg = mcuctl_read(is, MCUCTL_REG_INTMSR0); | ||
64 | status = INTMSR0_GET_INTMSD(0, cfg); | ||
65 | |||
66 | while (status) { | ||
67 | cfg = mcuctl_read(is, MCUCTL_REG_INTMSR0); | ||
68 | status = INTMSR0_GET_INTMSD(0, cfg); | ||
69 | if (timeout == 0) { | ||
70 | dev_warn(&is->pdev->dev, "%s timeout\n", | ||
71 | __func__); | ||
72 | return -ETIME; | ||
73 | } | ||
74 | timeout--; | ||
75 | udelay(1); | ||
76 | } | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | int fimc_is_hw_set_param(struct fimc_is *is) | ||
81 | { | ||
82 | struct chain_config *config = &is->config[is->config_index]; | ||
83 | unsigned int param_count = __get_pending_param_count(is); | ||
84 | |||
85 | fimc_is_hw_wait_intmsr0_intmsd0(is); | ||
86 | |||
87 | mcuctl_write(HIC_SET_PARAMETER, is, MCUCTL_REG_ISSR(0)); | ||
88 | mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); | ||
89 | mcuctl_write(is->config_index, is, MCUCTL_REG_ISSR(2)); | ||
90 | |||
91 | mcuctl_write(param_count, is, MCUCTL_REG_ISSR(3)); | ||
92 | mcuctl_write(config->p_region_index1, is, MCUCTL_REG_ISSR(4)); | ||
93 | mcuctl_write(config->p_region_index2, is, MCUCTL_REG_ISSR(5)); | ||
94 | |||
95 | fimc_is_hw_set_intgr0_gd0(is); | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | int fimc_is_hw_set_tune(struct fimc_is *is) | ||
100 | { | ||
101 | fimc_is_hw_wait_intmsr0_intmsd0(is); | ||
102 | |||
103 | mcuctl_write(HIC_SET_TUNE, is, MCUCTL_REG_ISSR(0)); | ||
104 | mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); | ||
105 | mcuctl_write(is->h2i_cmd.entry_id, is, MCUCTL_REG_ISSR(2)); | ||
106 | |||
107 | fimc_is_hw_set_intgr0_gd0(is); | ||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | #define FIMC_IS_MAX_PARAMS 4 | ||
112 | |||
113 | int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num_args) | ||
114 | { | ||
115 | int i; | ||
116 | |||
117 | if (num_args > FIMC_IS_MAX_PARAMS) | ||
118 | return -EINVAL; | ||
119 | |||
120 | is->i2h_cmd.num_args = num_args; | ||
121 | |||
122 | for (i = 0; i < FIMC_IS_MAX_PARAMS; i++) { | ||
123 | if (i < num_args) | ||
124 | is->i2h_cmd.args[i] = mcuctl_read(is, | ||
125 | MCUCTL_REG_ISSR(12 + i)); | ||
126 | else | ||
127 | is->i2h_cmd.args[i] = 0; | ||
128 | } | ||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | void fimc_is_hw_set_sensor_num(struct fimc_is *is) | ||
133 | { | ||
134 | pr_debug("setting sensor index to: %d\n", is->sensor_index); | ||
135 | |||
136 | mcuctl_write(IH_REPLY_DONE, is, MCUCTL_REG_ISSR(0)); | ||
137 | mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); | ||
138 | mcuctl_write(IHC_GET_SENSOR_NUM, is, MCUCTL_REG_ISSR(2)); | ||
139 | mcuctl_write(FIMC_IS_SENSOR_NUM, is, MCUCTL_REG_ISSR(3)); | ||
140 | } | ||
141 | |||
142 | void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index) | ||
143 | { | ||
144 | if (is->sensor_index != index) | ||
145 | return; | ||
146 | |||
147 | fimc_is_hw_wait_intmsr0_intmsd0(is); | ||
148 | mcuctl_write(HIC_CLOSE_SENSOR, is, MCUCTL_REG_ISSR(0)); | ||
149 | mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); | ||
150 | mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(2)); | ||
151 | fimc_is_hw_set_intgr0_gd0(is); | ||
152 | } | ||
153 | |||
154 | void fimc_is_hw_get_setfile_addr(struct fimc_is *is) | ||
155 | { | ||
156 | fimc_is_hw_wait_intmsr0_intmsd0(is); | ||
157 | mcuctl_write(HIC_GET_SET_FILE_ADDR, is, MCUCTL_REG_ISSR(0)); | ||
158 | mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); | ||
159 | fimc_is_hw_set_intgr0_gd0(is); | ||
160 | } | ||
161 | |||
162 | void fimc_is_hw_load_setfile(struct fimc_is *is) | ||
163 | { | ||
164 | fimc_is_hw_wait_intmsr0_intmsd0(is); | ||
165 | mcuctl_write(HIC_LOAD_SET_FILE, is, MCUCTL_REG_ISSR(0)); | ||
166 | mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); | ||
167 | fimc_is_hw_set_intgr0_gd0(is); | ||
168 | } | ||
169 | |||
170 | int fimc_is_hw_change_mode(struct fimc_is *is) | ||
171 | { | ||
172 | const u8 cmd[] = { | ||
173 | HIC_PREVIEW_STILL, HIC_PREVIEW_VIDEO, | ||
174 | HIC_CAPTURE_STILL, HIC_CAPTURE_VIDEO, | ||
175 | }; | ||
176 | |||
177 | if (WARN_ON(is->config_index > ARRAY_SIZE(cmd))) | ||
178 | return -EINVAL; | ||
179 | |||
180 | mcuctl_write(cmd[is->config_index], is, MCUCTL_REG_ISSR(0)); | ||
181 | mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); | ||
182 | mcuctl_write(is->setfile.sub_index, is, MCUCTL_REG_ISSR(2)); | ||
183 | fimc_is_hw_set_intgr0_gd0(is); | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | void fimc_is_hw_stream_on(struct fimc_is *is) | ||
188 | { | ||
189 | fimc_is_hw_wait_intmsr0_intmsd0(is); | ||
190 | mcuctl_write(HIC_STREAM_ON, is, MCUCTL_REG_ISSR(0)); | ||
191 | mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); | ||
192 | mcuctl_write(0, is, MCUCTL_REG_ISSR(2)); | ||
193 | fimc_is_hw_set_intgr0_gd0(is); | ||
194 | } | ||
195 | |||
196 | void fimc_is_hw_stream_off(struct fimc_is *is) | ||
197 | { | ||
198 | fimc_is_hw_wait_intmsr0_intmsd0(is); | ||
199 | mcuctl_write(HIC_STREAM_OFF, is, MCUCTL_REG_ISSR(0)); | ||
200 | mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); | ||
201 | fimc_is_hw_set_intgr0_gd0(is); | ||
202 | } | ||
203 | |||
204 | void fimc_is_hw_subip_power_off(struct fimc_is *is) | ||
205 | { | ||
206 | fimc_is_hw_wait_intmsr0_intmsd0(is); | ||
207 | mcuctl_write(HIC_POWER_DOWN, is, MCUCTL_REG_ISSR(0)); | ||
208 | mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); | ||
209 | fimc_is_hw_set_intgr0_gd0(is); | ||
210 | } | ||
211 | |||
212 | int fimc_is_itf_s_param(struct fimc_is *is, bool update) | ||
213 | { | ||
214 | int ret; | ||
215 | |||
216 | if (update) | ||
217 | __is_hw_update_params(is); | ||
218 | |||
219 | fimc_is_mem_barrier(); | ||
220 | |||
221 | clear_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state); | ||
222 | fimc_is_hw_set_param(is); | ||
223 | ret = fimc_is_wait_event(is, IS_ST_BLOCK_CMD_CLEARED, 1, | ||
224 | FIMC_IS_CONFIG_TIMEOUT); | ||
225 | if (ret < 0) | ||
226 | dev_err(&is->pdev->dev, "%s() timeout\n", __func__); | ||
227 | |||
228 | return ret; | ||
229 | } | ||
230 | |||
231 | int fimc_is_itf_mode_change(struct fimc_is *is) | ||
232 | { | ||
233 | int ret; | ||
234 | |||
235 | clear_bit(IS_ST_CHANGE_MODE, &is->state); | ||
236 | fimc_is_hw_change_mode(is); | ||
237 | ret = fimc_is_wait_event(is, IS_ST_CHANGE_MODE, 1, | ||
238 | FIMC_IS_CONFIG_TIMEOUT); | ||
239 | if (!ret < 0) | ||
240 | dev_err(&is->pdev->dev, "%s(): mode change (%d) timeout\n", | ||
241 | __func__, is->config_index); | ||
242 | return ret; | ||
243 | } | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.h b/drivers/media/platform/exynos4-is/fimc-is-regs.h new file mode 100644 index 000000000000..5fa2fda46742 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.h | |||
@@ -0,0 +1,164 @@ | |||
1 | /* | ||
2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
7 | * Younghwan Joo <yhwan.joo@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #ifndef FIMC_IS_REG_H_ | ||
14 | #define FIMC_IS_REG_H_ | ||
15 | |||
16 | /* WDT_ISP register */ | ||
17 | #define REG_WDT_ISP 0x00170000 | ||
18 | |||
19 | /* MCUCTL registers base offset */ | ||
20 | #define MCUCTL_BASE 0x00180000 | ||
21 | |||
22 | /* MCU Controller Register */ | ||
23 | #define MCUCTL_REG_MCUCTRL (MCUCTL_BASE + 0x00) | ||
24 | #define MCUCTRL_MSWRST (1 << 0) | ||
25 | |||
26 | /* Boot Base Offset Address Register */ | ||
27 | #define MCUCTL_REG_BBOAR (MCUCTL_BASE + 0x04) | ||
28 | |||
29 | /* Interrupt Generation Register 0 from Host CPU to VIC */ | ||
30 | #define MCUCTL_REG_INTGR0 (MCUCTL_BASE + 0x08) | ||
31 | /* __n = 0...9 */ | ||
32 | #define INTGR0_INTGC(__n) (1 << ((__n) + 16)) | ||
33 | /* __n = 0...5 */ | ||
34 | #define INTGR0_INTGD(__n) (1 << (__n)) | ||
35 | |||
36 | /* Interrupt Clear Register 0 from Host CPU to VIC */ | ||
37 | #define MCUCTL_REG_INTCR0 (MCUCTL_BASE + 0x0c) | ||
38 | /* __n = 0...9 */ | ||
39 | #define INTCR0_INTGC(__n) (1 << ((__n) + 16)) | ||
40 | /* __n = 0...5 */ | ||
41 | #define INTCR0_INTCD(__n) (1 << ((__n) + 16)) | ||
42 | |||
43 | /* Interrupt Mask Register 0 from Host CPU to VIC */ | ||
44 | #define MCUCTL_REG_INTMR0 (MCUCTL_BASE + 0x10) | ||
45 | /* __n = 0...9 */ | ||
46 | #define INTMR0_INTMC(__n) (1 << ((__n) + 16)) | ||
47 | /* __n = 0...5 */ | ||
48 | #define INTMR0_INTMD(__n) (1 << (__n)) | ||
49 | |||
50 | /* Interrupt Status Register 0 from Host CPU to VIC */ | ||
51 | #define MCUCTL_REG_INTSR0 (MCUCTL_BASE + 0x14) | ||
52 | /* __n (bit number) = 0...4 */ | ||
53 | #define INTSR0_GET_INTSD(x, __n) (((x) >> (__n)) & 0x1) | ||
54 | /* __n (bit number) = 0...9 */ | ||
55 | #define INTSR0_GET_INTSC(x, __n) (((x) >> ((__n) + 16)) & 0x1) | ||
56 | |||
57 | /* Interrupt Mask Status Register 0 from Host CPU to VIC */ | ||
58 | #define MCUCTL_REG_INTMSR0 (MCUCTL_BASE + 0x18) | ||
59 | /* __n (bit number) = 0...4 */ | ||
60 | #define INTMSR0_GET_INTMSD(x, __n) (((x) >> (__n)) & 0x1) | ||
61 | /* __n (bit number) = 0...9 */ | ||
62 | #define INTMSR0_GET_INTMSC(x, __n) (((x) >> ((__n) + 16)) & 0x1) | ||
63 | |||
64 | /* Interrupt Generation Register 1 from ISP CPU to Host IC */ | ||
65 | #define MCUCTL_REG_INTGR1 (MCUCTL_BASE + 0x1c) | ||
66 | /* __n = 0...9 */ | ||
67 | #define INTGR1_INTGC(__n) (1 << (__n)) | ||
68 | |||
69 | /* Interrupt Clear Register 1 from ISP CPU to Host IC */ | ||
70 | #define MCUCTL_REG_INTCR1 (MCUCTL_BASE + 0x20) | ||
71 | /* __n = 0...9 */ | ||
72 | #define INTCR1_INTCC(__n) (1 << (__n)) | ||
73 | |||
74 | /* Interrupt Mask Register 1 from ISP CPU to Host IC */ | ||
75 | #define MCUCTL_REG_INTMR1 (MCUCTL_BASE + 0x24) | ||
76 | /* __n = 0...9 */ | ||
77 | #define INTMR1_INTMC(__n) (1 << (__n)) | ||
78 | |||
79 | /* Interrupt Status Register 1 from ISP CPU to Host IC */ | ||
80 | #define MCUCTL_REG_INTSR1 (MCUCTL_BASE + 0x28) | ||
81 | /* Interrupt Mask Status Register 1 from ISP CPU to Host IC */ | ||
82 | #define MCUCTL_REG_INTMSR1 (MCUCTL_BASE + 0x2c) | ||
83 | |||
84 | /* Interrupt Clear Register 2 from ISP BLK's interrupts to Host IC */ | ||
85 | #define MCUCTL_REG_INTCR2 (MCUCTL_BASE + 0x30) | ||
86 | /* __n = 0...5 */ | ||
87 | #define INTCR2_INTCC(__n) (1 << ((__n) + 16)) | ||
88 | |||
89 | /* Interrupt Mask Register 2 from ISP BLK's interrupts to Host IC */ | ||
90 | #define MCUCTL_REG_INTMR2 (MCUCTL_BASE + 0x34) | ||
91 | /* __n = 0...25 */ | ||
92 | #define INTMR2_INTMCIS(__n) (1 << (__n)) | ||
93 | |||
94 | /* Interrupt Status Register 2 from ISP BLK's interrupts to Host IC */ | ||
95 | #define MCUCTL_REG_INTSR2 (MCUCTL_BASE + 0x38) | ||
96 | /* Interrupt Mask Status Register 2 from ISP BLK's interrupts to Host IC */ | ||
97 | #define MCUCTL_REG_INTMSR2 (MCUCTL_BASE + 0x3c) | ||
98 | |||
99 | /* General Purpose Output Control Register (0~17) */ | ||
100 | #define MCUCTL_REG_GPOCTLR (MCUCTL_BASE + 0x40) | ||
101 | /* __n = 0...17 */ | ||
102 | #define GPOCTLR_GPOG(__n) (1 << (__n)) | ||
103 | |||
104 | /* General Purpose Pad Output Enable Register (0~17) */ | ||
105 | #define MCUCTL_REG_GPOENCTLR (MCUCTL_BASE + 0x44) | ||
106 | /* __n = 0...17 */ | ||
107 | #define GPOENCTLR_GPOEN(__n) (1 << (__n)) | ||
108 | |||
109 | /* General Purpose Input Control Register (0~17) */ | ||
110 | #define MCUCTL_REG_GPICTLR (MCUCTL_BASE + 0x48) | ||
111 | |||
112 | /* Shared registers between ISP CPU and the host CPU - ISSRxx */ | ||
113 | |||
114 | /* ISSR(1): Command Host -> IS */ | ||
115 | /* ISSR(1): Sensor ID for Command, ISSR2...5 = Parameter 1...4 */ | ||
116 | |||
117 | /* ISSR(10): Reply IS -> Host */ | ||
118 | /* ISSR(11): Sensor ID for Reply, ISSR12...15 = Parameter 1...4 */ | ||
119 | |||
120 | /* ISSR(20): ISP_FRAME_DONE : SENSOR ID */ | ||
121 | /* ISSR(21): ISP_FRAME_DONE : PARAMETER 1 */ | ||
122 | |||
123 | /* ISSR(24): SCALERC_FRAME_DONE : SENSOR ID */ | ||
124 | /* ISSR(25): SCALERC_FRAME_DONE : PARAMETER 1 */ | ||
125 | |||
126 | /* ISSR(28): 3DNR_FRAME_DONE : SENSOR ID */ | ||
127 | /* ISSR(29): 3DNR_FRAME_DONE : PARAMETER 1 */ | ||
128 | |||
129 | /* ISSR(32): SCALERP_FRAME_DONE : SENSOR ID */ | ||
130 | /* ISSR(33): SCALERP_FRAME_DONE : PARAMETER 1 */ | ||
131 | |||
132 | /* __n = 0...63 */ | ||
133 | #define MCUCTL_REG_ISSR(__n) (MCUCTL_BASE + 0x80 + ((__n) * 4)) | ||
134 | |||
135 | /* PMU ISP register offsets */ | ||
136 | #define REG_CMU_RESET_ISP_SYS_PWR_REG 0x1174 | ||
137 | #define REG_CMU_SYSCLK_ISP_SYS_PWR_REG 0x13b8 | ||
138 | #define REG_PMU_ISP_ARM_SYS 0x1050 | ||
139 | #define REG_PMU_ISP_ARM_CONFIGURATION 0x2280 | ||
140 | #define REG_PMU_ISP_ARM_STATUS 0x2284 | ||
141 | #define REG_PMU_ISP_ARM_OPTION 0x2288 | ||
142 | |||
143 | void fimc_is_fw_clear_irq1(struct fimc_is *is, unsigned int bit); | ||
144 | void fimc_is_fw_clear_irq2(struct fimc_is *is); | ||
145 | int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num); | ||
146 | |||
147 | void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is); | ||
148 | int fimc_is_hw_wait_intsr0_intsd0(struct fimc_is *is); | ||
149 | int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is); | ||
150 | void fimc_is_hw_set_sensor_num(struct fimc_is *is); | ||
151 | void fimc_is_hw_stream_on(struct fimc_is *is); | ||
152 | void fimc_is_hw_stream_off(struct fimc_is *is); | ||
153 | int fimc_is_hw_set_param(struct fimc_is *is); | ||
154 | int fimc_is_hw_change_mode(struct fimc_is *is); | ||
155 | |||
156 | void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index); | ||
157 | void fimc_is_hw_get_setfile_addr(struct fimc_is *is); | ||
158 | void fimc_is_hw_load_setfile(struct fimc_is *is); | ||
159 | void fimc_is_hw_subip_power_off(struct fimc_is *is); | ||
160 | |||
161 | int fimc_is_itf_s_param(struct fimc_is *is, bool update); | ||
162 | int fimc_is_itf_mode_change(struct fimc_is *is); | ||
163 | |||
164 | #endif /* FIMC_IS_REG_H_ */ | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.c b/drivers/media/platform/exynos4-is/fimc-is-sensor.c new file mode 100644 index 000000000000..6647421e5d3a --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.c | |||
@@ -0,0 +1,305 @@ | |||
1 | /* | ||
2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #include <linux/delay.h> | ||
13 | #include <linux/device.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/gpio.h> | ||
16 | #include <linux/i2c.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/of_gpio.h> | ||
20 | #include <linux/pm_runtime.h> | ||
21 | #include <linux/regulator/consumer.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <media/v4l2-subdev.h> | ||
24 | |||
25 | #include "fimc-is.h" | ||
26 | #include "fimc-is-sensor.h" | ||
27 | |||
28 | #define DRIVER_NAME "FIMC-IS-SENSOR" | ||
29 | |||
30 | static const char * const sensor_supply_names[] = { | ||
31 | "svdda", | ||
32 | "svddio", | ||
33 | }; | ||
34 | |||
35 | static const struct v4l2_mbus_framefmt fimc_is_sensor_formats[] = { | ||
36 | { | ||
37 | .code = V4L2_MBUS_FMT_SGRBG10_1X10, | ||
38 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
39 | .field = V4L2_FIELD_NONE, | ||
40 | } | ||
41 | }; | ||
42 | |||
43 | static const struct v4l2_mbus_framefmt *find_sensor_format( | ||
44 | struct v4l2_mbus_framefmt *mf) | ||
45 | { | ||
46 | int i; | ||
47 | |||
48 | for (i = 0; i < ARRAY_SIZE(fimc_is_sensor_formats); i++) | ||
49 | if (mf->code == fimc_is_sensor_formats[i].code) | ||
50 | return &fimc_is_sensor_formats[i]; | ||
51 | |||
52 | return &fimc_is_sensor_formats[0]; | ||
53 | } | ||
54 | |||
55 | static int fimc_is_sensor_enum_mbus_code(struct v4l2_subdev *sd, | ||
56 | struct v4l2_subdev_fh *fh, | ||
57 | struct v4l2_subdev_mbus_code_enum *code) | ||
58 | { | ||
59 | if (code->index >= ARRAY_SIZE(fimc_is_sensor_formats)) | ||
60 | return -EINVAL; | ||
61 | |||
62 | code->code = fimc_is_sensor_formats[code->index].code; | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static void fimc_is_sensor_try_format(struct fimc_is_sensor *sensor, | ||
67 | struct v4l2_mbus_framefmt *mf) | ||
68 | { | ||
69 | const struct sensor_drv_data *dd = sensor->drvdata; | ||
70 | const struct v4l2_mbus_framefmt *fmt; | ||
71 | |||
72 | fmt = find_sensor_format(mf); | ||
73 | mf->code = fmt->code; | ||
74 | v4l_bound_align_image(&mf->width, 16 + 8, dd->width, 0, | ||
75 | &mf->height, 12 + 8, dd->height, 0, 0); | ||
76 | } | ||
77 | |||
78 | static struct v4l2_mbus_framefmt *__fimc_is_sensor_get_format( | ||
79 | struct fimc_is_sensor *sensor, struct v4l2_subdev_fh *fh, | ||
80 | u32 pad, enum v4l2_subdev_format_whence which) | ||
81 | { | ||
82 | if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
83 | return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL; | ||
84 | |||
85 | return &sensor->format; | ||
86 | } | ||
87 | |||
88 | static int fimc_is_sensor_set_fmt(struct v4l2_subdev *sd, | ||
89 | struct v4l2_subdev_fh *fh, | ||
90 | struct v4l2_subdev_format *fmt) | ||
91 | { | ||
92 | struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); | ||
93 | struct v4l2_mbus_framefmt *mf; | ||
94 | |||
95 | fimc_is_sensor_try_format(sensor, &fmt->format); | ||
96 | |||
97 | mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which); | ||
98 | if (mf) { | ||
99 | mutex_lock(&sensor->lock); | ||
100 | if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) | ||
101 | *mf = fmt->format; | ||
102 | mutex_unlock(&sensor->lock); | ||
103 | } | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static int fimc_is_sensor_get_fmt(struct v4l2_subdev *sd, | ||
108 | struct v4l2_subdev_fh *fh, | ||
109 | struct v4l2_subdev_format *fmt) | ||
110 | { | ||
111 | struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); | ||
112 | struct v4l2_mbus_framefmt *mf; | ||
113 | |||
114 | mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which); | ||
115 | |||
116 | mutex_lock(&sensor->lock); | ||
117 | fmt->format = *mf; | ||
118 | mutex_unlock(&sensor->lock); | ||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | static struct v4l2_subdev_pad_ops fimc_is_sensor_pad_ops = { | ||
123 | .enum_mbus_code = fimc_is_sensor_enum_mbus_code, | ||
124 | .get_fmt = fimc_is_sensor_get_fmt, | ||
125 | .set_fmt = fimc_is_sensor_set_fmt, | ||
126 | }; | ||
127 | |||
128 | static int fimc_is_sensor_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
129 | { | ||
130 | struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); | ||
131 | |||
132 | *format = fimc_is_sensor_formats[0]; | ||
133 | format->width = FIMC_IS_SENSOR_DEF_PIX_WIDTH; | ||
134 | format->height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT; | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | static const struct v4l2_subdev_internal_ops fimc_is_sensor_sd_internal_ops = { | ||
140 | .open = fimc_is_sensor_open, | ||
141 | }; | ||
142 | |||
143 | static int fimc_is_sensor_s_power(struct v4l2_subdev *sd, int on) | ||
144 | { | ||
145 | struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); | ||
146 | int gpio = sensor->gpio_reset; | ||
147 | int ret; | ||
148 | |||
149 | if (on) { | ||
150 | ret = pm_runtime_get(sensor->dev); | ||
151 | if (ret < 0) | ||
152 | return ret; | ||
153 | |||
154 | ret = regulator_bulk_enable(SENSOR_NUM_SUPPLIES, | ||
155 | sensor->supplies); | ||
156 | if (ret < 0) { | ||
157 | pm_runtime_put(sensor->dev); | ||
158 | return ret; | ||
159 | } | ||
160 | if (gpio_is_valid(gpio)) { | ||
161 | gpio_set_value(gpio, 1); | ||
162 | usleep_range(600, 800); | ||
163 | gpio_set_value(gpio, 0); | ||
164 | usleep_range(10000, 11000); | ||
165 | gpio_set_value(gpio, 1); | ||
166 | } | ||
167 | |||
168 | /* A delay needed for the sensor initialization. */ | ||
169 | msleep(20); | ||
170 | } else { | ||
171 | if (gpio_is_valid(gpio)) | ||
172 | gpio_set_value(gpio, 0); | ||
173 | |||
174 | ret = regulator_bulk_disable(SENSOR_NUM_SUPPLIES, | ||
175 | sensor->supplies); | ||
176 | if (!ret) | ||
177 | pm_runtime_put(sensor->dev); | ||
178 | } | ||
179 | |||
180 | pr_info("%s:%d: on: %d, ret: %d\n", __func__, __LINE__, on, ret); | ||
181 | |||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | static struct v4l2_subdev_core_ops fimc_is_sensor_core_ops = { | ||
186 | .s_power = fimc_is_sensor_s_power, | ||
187 | }; | ||
188 | |||
189 | static struct v4l2_subdev_ops fimc_is_sensor_subdev_ops = { | ||
190 | .core = &fimc_is_sensor_core_ops, | ||
191 | .pad = &fimc_is_sensor_pad_ops, | ||
192 | }; | ||
193 | |||
194 | static const struct of_device_id fimc_is_sensor_of_match[]; | ||
195 | |||
196 | static int fimc_is_sensor_probe(struct i2c_client *client, | ||
197 | const struct i2c_device_id *id) | ||
198 | { | ||
199 | struct device *dev = &client->dev; | ||
200 | struct fimc_is_sensor *sensor; | ||
201 | const struct of_device_id *of_id; | ||
202 | struct v4l2_subdev *sd; | ||
203 | int gpio, i, ret; | ||
204 | |||
205 | sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); | ||
206 | if (!sensor) | ||
207 | return -ENOMEM; | ||
208 | |||
209 | mutex_init(&sensor->lock); | ||
210 | sensor->gpio_reset = -EINVAL; | ||
211 | |||
212 | gpio = of_get_gpio_flags(dev->of_node, 0, NULL); | ||
213 | if (gpio_is_valid(gpio)) { | ||
214 | ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW, | ||
215 | DRIVER_NAME); | ||
216 | if (ret < 0) | ||
217 | return ret; | ||
218 | } | ||
219 | sensor->gpio_reset = gpio; | ||
220 | |||
221 | for (i = 0; i < SENSOR_NUM_SUPPLIES; i++) | ||
222 | sensor->supplies[i].supply = sensor_supply_names[i]; | ||
223 | |||
224 | ret = devm_regulator_bulk_get(&client->dev, SENSOR_NUM_SUPPLIES, | ||
225 | sensor->supplies); | ||
226 | if (ret < 0) | ||
227 | return ret; | ||
228 | |||
229 | of_id = of_match_node(fimc_is_sensor_of_match, dev->of_node); | ||
230 | if (!of_id) | ||
231 | return -ENODEV; | ||
232 | |||
233 | sensor->drvdata = of_id->data; | ||
234 | sensor->dev = dev; | ||
235 | |||
236 | sd = &sensor->subdev; | ||
237 | v4l2_i2c_subdev_init(sd, client, &fimc_is_sensor_subdev_ops); | ||
238 | snprintf(sd->name, sizeof(sd->name), sensor->drvdata->subdev_name); | ||
239 | sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
240 | |||
241 | sensor->format.code = fimc_is_sensor_formats[0].code; | ||
242 | sensor->format.width = FIMC_IS_SENSOR_DEF_PIX_WIDTH; | ||
243 | sensor->format.height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT; | ||
244 | |||
245 | sensor->pad.flags = MEDIA_PAD_FL_SOURCE; | ||
246 | ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0); | ||
247 | if (ret < 0) | ||
248 | return ret; | ||
249 | |||
250 | pm_runtime_no_callbacks(dev); | ||
251 | pm_runtime_enable(dev); | ||
252 | |||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | static int fimc_is_sensor_remove(struct i2c_client *client) | ||
257 | { | ||
258 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
259 | media_entity_cleanup(&sd->entity); | ||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | static const struct i2c_device_id fimc_is_sensor_ids[] = { | ||
264 | { } | ||
265 | }; | ||
266 | |||
267 | static const struct sensor_drv_data s5k6a3_drvdata = { | ||
268 | .id = FIMC_IS_SENSOR_ID_S5K6A3, | ||
269 | .subdev_name = "S5K6A3", | ||
270 | .width = S5K6A3_SENSOR_WIDTH, | ||
271 | .height = S5K6A3_SENSOR_HEIGHT, | ||
272 | }; | ||
273 | |||
274 | static const struct of_device_id fimc_is_sensor_of_match[] = { | ||
275 | { | ||
276 | .compatible = "samsung,s5k6a3", | ||
277 | .data = &s5k6a3_drvdata, | ||
278 | }, | ||
279 | { } | ||
280 | }; | ||
281 | |||
282 | static struct i2c_driver fimc_is_sensor_driver = { | ||
283 | .driver = { | ||
284 | .of_match_table = fimc_is_sensor_of_match, | ||
285 | .name = DRIVER_NAME, | ||
286 | .owner = THIS_MODULE, | ||
287 | }, | ||
288 | .probe = fimc_is_sensor_probe, | ||
289 | .remove = fimc_is_sensor_remove, | ||
290 | .id_table = fimc_is_sensor_ids, | ||
291 | }; | ||
292 | |||
293 | int fimc_is_register_sensor_driver(void) | ||
294 | { | ||
295 | return i2c_add_driver(&fimc_is_sensor_driver); | ||
296 | } | ||
297 | |||
298 | void fimc_is_unregister_sensor_driver(void) | ||
299 | { | ||
300 | i2c_del_driver(&fimc_is_sensor_driver); | ||
301 | } | ||
302 | |||
303 | MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); | ||
304 | MODULE_DESCRIPTION("Exynos4x12 FIMC-IS image sensor subdev driver"); | ||
305 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.h b/drivers/media/platform/exynos4-is/fimc-is-sensor.h new file mode 100644 index 000000000000..6036d49a6c68 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.h | |||
@@ -0,0 +1,89 @@ | |||
1 | /* | ||
2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
7 | * Younghwan Joo <yhwan.joo@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #ifndef FIMC_IS_SENSOR_H_ | ||
14 | #define FIMC_IS_SENSOR_H_ | ||
15 | |||
16 | #include <linux/clk.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/regulator/consumer.h> | ||
21 | #include <linux/videodev2.h> | ||
22 | #include <media/v4l2-subdev.h> | ||
23 | |||
24 | #define FIMC_IS_SENSOR_OPEN_TIMEOUT 2000 /* ms */ | ||
25 | |||
26 | #define FIMC_IS_SENSOR_DEF_PIX_WIDTH 1296 | ||
27 | #define FIMC_IS_SENSOR_DEF_PIX_HEIGHT 732 | ||
28 | |||
29 | #define S5K6A3_SENSOR_WIDTH 1392 | ||
30 | #define S5K6A3_SENSOR_HEIGHT 1392 | ||
31 | |||
32 | #define SENSOR_NUM_SUPPLIES 2 | ||
33 | |||
34 | enum fimc_is_sensor_id { | ||
35 | FIMC_IS_SENSOR_ID_S5K3H2 = 1, | ||
36 | FIMC_IS_SENSOR_ID_S5K6A3, | ||
37 | FIMC_IS_SENSOR_ID_S5K4E5, | ||
38 | FIMC_IS_SENSOR_ID_S5K3H7, | ||
39 | FIMC_IS_SENSOR_ID_CUSTOM, | ||
40 | FIMC_IS_SENSOR_ID_END | ||
41 | }; | ||
42 | |||
43 | #define IS_SENSOR_CTRL_BUS_I2C0 0 | ||
44 | #define IS_SENSOR_CTRL_BUS_I2C1 1 | ||
45 | |||
46 | struct sensor_drv_data { | ||
47 | enum fimc_is_sensor_id id; | ||
48 | const char * const subdev_name; | ||
49 | unsigned int width; | ||
50 | unsigned int height; | ||
51 | }; | ||
52 | |||
53 | /** | ||
54 | * struct fimc_is_sensor - fimc-is sensor data structure | ||
55 | * @dev: pointer to this I2C client device structure | ||
56 | * @subdev: the image sensor's v4l2 subdev | ||
57 | * @pad: subdev media source pad | ||
58 | * @supplies: image sensor's voltage regulator supplies | ||
59 | * @gpio_reset: GPIO connected to the sensor's reset pin | ||
60 | * @drvdata: a pointer to the sensor's parameters data structure | ||
61 | * @i2c_bus: ISP I2C bus index (0...1) | ||
62 | * @test_pattern: true to enable video test pattern | ||
63 | * @lock: mutex protecting the structure's members below | ||
64 | * @format: media bus format at the sensor's source pad | ||
65 | */ | ||
66 | struct fimc_is_sensor { | ||
67 | struct device *dev; | ||
68 | struct v4l2_subdev subdev; | ||
69 | struct media_pad pad; | ||
70 | struct regulator_bulk_data supplies[SENSOR_NUM_SUPPLIES]; | ||
71 | int gpio_reset; | ||
72 | const struct sensor_drv_data *drvdata; | ||
73 | unsigned int i2c_bus; | ||
74 | bool test_pattern; | ||
75 | |||
76 | struct mutex lock; | ||
77 | struct v4l2_mbus_framefmt format; | ||
78 | }; | ||
79 | |||
80 | static inline | ||
81 | struct fimc_is_sensor *sd_to_fimc_is_sensor(struct v4l2_subdev *sd) | ||
82 | { | ||
83 | return container_of(sd, struct fimc_is_sensor, subdev); | ||
84 | } | ||
85 | |||
86 | int fimc_is_register_sensor_driver(void); | ||
87 | void fimc_is_unregister_sensor_driver(void); | ||
88 | |||
89 | #endif /* FIMC_IS_SENSOR_H_ */ | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c new file mode 100644 index 000000000000..47c6363d04e2 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is.c | |||
@@ -0,0 +1,1007 @@ | |||
1 | /* | ||
2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
7 | * Younghwan Joo <yhwan.joo@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ | ||
14 | |||
15 | #include <linux/device.h> | ||
16 | #include <linux/debugfs.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/dma-contiguous.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/firmware.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/of_i2c.h> | ||
25 | #include <linux/of_irq.h> | ||
26 | #include <linux/of_address.h> | ||
27 | #include <linux/of_platform.h> | ||
28 | #include <linux/platform_device.h> | ||
29 | #include <linux/pm_runtime.h> | ||
30 | #include <linux/slab.h> | ||
31 | #include <linux/types.h> | ||
32 | #include <linux/videodev2.h> | ||
33 | #include <media/v4l2-of.h> | ||
34 | #include <media/videobuf2-dma-contig.h> | ||
35 | |||
36 | #include "media-dev.h" | ||
37 | #include "fimc-is.h" | ||
38 | #include "fimc-is-command.h" | ||
39 | #include "fimc-is-errno.h" | ||
40 | #include "fimc-is-i2c.h" | ||
41 | #include "fimc-is-param.h" | ||
42 | #include "fimc-is-regs.h" | ||
43 | |||
44 | |||
45 | static char *fimc_is_clocks[ISS_CLKS_MAX] = { | ||
46 | [ISS_CLK_PPMUISPX] = "ppmuispx", | ||
47 | [ISS_CLK_PPMUISPMX] = "ppmuispmx", | ||
48 | [ISS_CLK_LITE0] = "lite0", | ||
49 | [ISS_CLK_LITE1] = "lite1", | ||
50 | [ISS_CLK_MPLL] = "mpll", | ||
51 | [ISS_CLK_SYSREG] = "sysreg", | ||
52 | [ISS_CLK_ISP] = "isp", | ||
53 | [ISS_CLK_DRC] = "drc", | ||
54 | [ISS_CLK_FD] = "fd", | ||
55 | [ISS_CLK_MCUISP] = "mcuisp", | ||
56 | [ISS_CLK_UART] = "uart", | ||
57 | [ISS_CLK_ISP_DIV0] = "ispdiv0", | ||
58 | [ISS_CLK_ISP_DIV1] = "ispdiv1", | ||
59 | [ISS_CLK_MCUISP_DIV0] = "mcuispdiv0", | ||
60 | [ISS_CLK_MCUISP_DIV1] = "mcuispdiv1", | ||
61 | [ISS_CLK_ACLK200] = "aclk200", | ||
62 | [ISS_CLK_ACLK200_DIV] = "div_aclk200", | ||
63 | [ISS_CLK_ACLK400MCUISP] = "aclk400mcuisp", | ||
64 | [ISS_CLK_ACLK400MCUISP_DIV] = "div_aclk400mcuisp", | ||
65 | }; | ||
66 | |||
67 | static void fimc_is_put_clocks(struct fimc_is *is) | ||
68 | { | ||
69 | int i; | ||
70 | |||
71 | for (i = 0; i < ISS_CLKS_MAX; i++) { | ||
72 | if (IS_ERR(is->clocks[i])) | ||
73 | continue; | ||
74 | clk_unprepare(is->clocks[i]); | ||
75 | clk_put(is->clocks[i]); | ||
76 | is->clocks[i] = ERR_PTR(-EINVAL); | ||
77 | } | ||
78 | } | ||
79 | |||
80 | static int fimc_is_get_clocks(struct fimc_is *is) | ||
81 | { | ||
82 | int i, ret; | ||
83 | |||
84 | for (i = 0; i < ISS_CLKS_MAX; i++) | ||
85 | is->clocks[i] = ERR_PTR(-EINVAL); | ||
86 | |||
87 | for (i = 0; i < ISS_CLKS_MAX; i++) { | ||
88 | is->clocks[i] = clk_get(&is->pdev->dev, fimc_is_clocks[i]); | ||
89 | if (IS_ERR(is->clocks[i])) { | ||
90 | ret = PTR_ERR(is->clocks[i]); | ||
91 | goto err; | ||
92 | } | ||
93 | ret = clk_prepare(is->clocks[i]); | ||
94 | if (ret < 0) { | ||
95 | clk_put(is->clocks[i]); | ||
96 | is->clocks[i] = ERR_PTR(-EINVAL); | ||
97 | goto err; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | return 0; | ||
102 | err: | ||
103 | fimc_is_put_clocks(is); | ||
104 | dev_err(&is->pdev->dev, "failed to get clock: %s\n", | ||
105 | fimc_is_clocks[i]); | ||
106 | return -ENXIO; | ||
107 | } | ||
108 | |||
109 | static int fimc_is_setup_clocks(struct fimc_is *is) | ||
110 | { | ||
111 | int ret; | ||
112 | |||
113 | ret = clk_set_parent(is->clocks[ISS_CLK_ACLK200], | ||
114 | is->clocks[ISS_CLK_ACLK200_DIV]); | ||
115 | if (ret < 0) | ||
116 | return ret; | ||
117 | |||
118 | ret = clk_set_parent(is->clocks[ISS_CLK_ACLK400MCUISP], | ||
119 | is->clocks[ISS_CLK_ACLK400MCUISP_DIV]); | ||
120 | if (ret < 0) | ||
121 | return ret; | ||
122 | |||
123 | ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV0], ACLK_AXI_FREQUENCY); | ||
124 | if (ret < 0) | ||
125 | return ret; | ||
126 | |||
127 | ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV1], ACLK_AXI_FREQUENCY); | ||
128 | if (ret < 0) | ||
129 | return ret; | ||
130 | |||
131 | ret = clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV0], | ||
132 | ATCLK_MCUISP_FREQUENCY); | ||
133 | if (ret < 0) | ||
134 | return ret; | ||
135 | |||
136 | return clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV1], | ||
137 | ATCLK_MCUISP_FREQUENCY); | ||
138 | } | ||
139 | |||
140 | int fimc_is_enable_clocks(struct fimc_is *is) | ||
141 | { | ||
142 | int i, ret; | ||
143 | |||
144 | for (i = 0; i < ISS_GATE_CLKS_MAX; i++) { | ||
145 | if (IS_ERR(is->clocks[i])) | ||
146 | continue; | ||
147 | ret = clk_enable(is->clocks[i]); | ||
148 | if (ret < 0) { | ||
149 | dev_err(&is->pdev->dev, "clock %s enable failed\n", | ||
150 | fimc_is_clocks[i]); | ||
151 | for (--i; i >= 0; i--) | ||
152 | clk_disable(is->clocks[i]); | ||
153 | return ret; | ||
154 | } | ||
155 | pr_debug("enabled clock: %s\n", fimc_is_clocks[i]); | ||
156 | } | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | void fimc_is_disable_clocks(struct fimc_is *is) | ||
161 | { | ||
162 | int i; | ||
163 | |||
164 | for (i = 0; i < ISS_GATE_CLKS_MAX; i++) { | ||
165 | if (!IS_ERR(is->clocks[i])) { | ||
166 | clk_disable(is->clocks[i]); | ||
167 | pr_debug("disabled clock: %s\n", fimc_is_clocks[i]); | ||
168 | } | ||
169 | } | ||
170 | } | ||
171 | |||
172 | static int fimc_is_parse_sensor_config(struct fimc_is_sensor *sensor, | ||
173 | struct device_node *np) | ||
174 | { | ||
175 | u32 tmp = 0; | ||
176 | int ret; | ||
177 | |||
178 | np = v4l2_of_get_next_endpoint(np, NULL); | ||
179 | if (!np) | ||
180 | return -ENXIO; | ||
181 | np = v4l2_of_get_remote_port(np); | ||
182 | if (!np) | ||
183 | return -ENXIO; | ||
184 | |||
185 | /* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */ | ||
186 | ret = of_property_read_u32(np, "reg", &tmp); | ||
187 | sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0; | ||
188 | |||
189 | return ret; | ||
190 | } | ||
191 | |||
192 | static int fimc_is_register_subdevs(struct fimc_is *is) | ||
193 | { | ||
194 | struct device_node *adapter, *child; | ||
195 | int ret; | ||
196 | |||
197 | ret = fimc_isp_subdev_create(&is->isp); | ||
198 | if (ret < 0) | ||
199 | return ret; | ||
200 | |||
201 | for_each_compatible_node(adapter, NULL, FIMC_IS_I2C_COMPATIBLE) { | ||
202 | if (!of_find_device_by_node(adapter)) { | ||
203 | of_node_put(adapter); | ||
204 | return -EPROBE_DEFER; | ||
205 | } | ||
206 | |||
207 | for_each_available_child_of_node(adapter, child) { | ||
208 | struct i2c_client *client; | ||
209 | struct v4l2_subdev *sd; | ||
210 | |||
211 | client = of_find_i2c_device_by_node(child); | ||
212 | if (!client) | ||
213 | goto e_retry; | ||
214 | |||
215 | sd = i2c_get_clientdata(client); | ||
216 | if (!sd) | ||
217 | goto e_retry; | ||
218 | |||
219 | /* FIXME: Add support for multiple sensors. */ | ||
220 | if (WARN_ON(is->sensor)) | ||
221 | continue; | ||
222 | |||
223 | is->sensor = sd_to_fimc_is_sensor(sd); | ||
224 | |||
225 | if (fimc_is_parse_sensor_config(is->sensor, child)) { | ||
226 | dev_warn(&is->pdev->dev, "DT parse error: %s\n", | ||
227 | child->full_name); | ||
228 | } | ||
229 | pr_debug("%s(): registered subdev: %p\n", | ||
230 | __func__, sd->name); | ||
231 | } | ||
232 | } | ||
233 | return 0; | ||
234 | |||
235 | e_retry: | ||
236 | of_node_put(child); | ||
237 | return -EPROBE_DEFER; | ||
238 | } | ||
239 | |||
240 | static int fimc_is_unregister_subdevs(struct fimc_is *is) | ||
241 | { | ||
242 | fimc_isp_subdev_destroy(&is->isp); | ||
243 | is->sensor = NULL; | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static int fimc_is_load_setfile(struct fimc_is *is, char *file_name) | ||
248 | { | ||
249 | const struct firmware *fw; | ||
250 | void *buf; | ||
251 | int ret; | ||
252 | |||
253 | ret = request_firmware(&fw, file_name, &is->pdev->dev); | ||
254 | if (ret < 0) { | ||
255 | dev_err(&is->pdev->dev, "firmware request failed (%d)\n", ret); | ||
256 | return ret; | ||
257 | } | ||
258 | buf = is->memory.vaddr + is->setfile.base; | ||
259 | memcpy(buf, fw->data, fw->size); | ||
260 | fimc_is_mem_barrier(); | ||
261 | is->setfile.size = fw->size; | ||
262 | |||
263 | pr_debug("mem vaddr: %p, setfile buf: %p\n", is->memory.vaddr, buf); | ||
264 | |||
265 | memcpy(is->fw.setfile_info, | ||
266 | fw->data + fw->size - FIMC_IS_SETFILE_INFO_LEN, | ||
267 | FIMC_IS_SETFILE_INFO_LEN - 1); | ||
268 | |||
269 | is->fw.setfile_info[FIMC_IS_SETFILE_INFO_LEN - 1] = '\0'; | ||
270 | is->setfile.state = 1; | ||
271 | |||
272 | pr_debug("FIMC-IS setfile loaded: base: %#x, size: %zu B\n", | ||
273 | is->setfile.base, fw->size); | ||
274 | |||
275 | release_firmware(fw); | ||
276 | return ret; | ||
277 | } | ||
278 | |||
279 | int fimc_is_cpu_set_power(struct fimc_is *is, int on) | ||
280 | { | ||
281 | unsigned int timeout = FIMC_IS_POWER_ON_TIMEOUT; | ||
282 | |||
283 | if (on) { | ||
284 | /* Disable watchdog */ | ||
285 | mcuctl_write(0, is, REG_WDT_ISP); | ||
286 | |||
287 | /* Cortex-A5 start address setting */ | ||
288 | mcuctl_write(is->memory.paddr, is, MCUCTL_REG_BBOAR); | ||
289 | |||
290 | /* Enable and start Cortex-A5 */ | ||
291 | pmuisp_write(0x18000, is, REG_PMU_ISP_ARM_OPTION); | ||
292 | pmuisp_write(0x1, is, REG_PMU_ISP_ARM_CONFIGURATION); | ||
293 | } else { | ||
294 | /* A5 power off */ | ||
295 | pmuisp_write(0x10000, is, REG_PMU_ISP_ARM_OPTION); | ||
296 | pmuisp_write(0x0, is, REG_PMU_ISP_ARM_CONFIGURATION); | ||
297 | |||
298 | while (pmuisp_read(is, REG_PMU_ISP_ARM_STATUS) & 1) { | ||
299 | if (timeout == 0) | ||
300 | return -ETIME; | ||
301 | timeout--; | ||
302 | udelay(1); | ||
303 | } | ||
304 | } | ||
305 | |||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | /* Wait until @bit of @is->state is set to @state in the interrupt handler. */ | ||
310 | int fimc_is_wait_event(struct fimc_is *is, unsigned long bit, | ||
311 | unsigned int state, unsigned int timeout) | ||
312 | { | ||
313 | |||
314 | int ret = wait_event_timeout(is->irq_queue, | ||
315 | !state ^ test_bit(bit, &is->state), | ||
316 | timeout); | ||
317 | if (ret == 0) { | ||
318 | dev_WARN(&is->pdev->dev, "%s() timed out\n", __func__); | ||
319 | return -ETIME; | ||
320 | } | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | int fimc_is_start_firmware(struct fimc_is *is) | ||
325 | { | ||
326 | struct device *dev = &is->pdev->dev; | ||
327 | int ret; | ||
328 | |||
329 | memcpy(is->memory.vaddr, is->fw.f_w->data, is->fw.f_w->size); | ||
330 | wmb(); | ||
331 | |||
332 | ret = fimc_is_cpu_set_power(is, 1); | ||
333 | if (ret < 0) | ||
334 | return ret; | ||
335 | |||
336 | ret = fimc_is_wait_event(is, IS_ST_A5_PWR_ON, 1, | ||
337 | msecs_to_jiffies(FIMC_IS_FW_LOAD_TIMEOUT)); | ||
338 | if (ret < 0) | ||
339 | dev_err(dev, "FIMC-IS CPU power on failed\n"); | ||
340 | |||
341 | return ret; | ||
342 | } | ||
343 | |||
344 | /* Allocate working memory for the FIMC-IS CPU. */ | ||
345 | static int fimc_is_alloc_cpu_memory(struct fimc_is *is) | ||
346 | { | ||
347 | struct device *dev = &is->pdev->dev; | ||
348 | |||
349 | is->memory.vaddr = dma_alloc_coherent(dev, FIMC_IS_CPU_MEM_SIZE, | ||
350 | &is->memory.paddr, GFP_KERNEL); | ||
351 | if (is->memory.vaddr == NULL) | ||
352 | return -ENOMEM; | ||
353 | |||
354 | is->memory.size = FIMC_IS_CPU_MEM_SIZE; | ||
355 | memset(is->memory.vaddr, 0, is->memory.size); | ||
356 | |||
357 | dev_info(dev, "FIMC-IS CPU memory base: %#x\n", (u32)is->memory.paddr); | ||
358 | |||
359 | if (((u32)is->memory.paddr) & FIMC_IS_FW_ADDR_MASK) { | ||
360 | dev_err(dev, "invalid firmware memory alignment: %#x\n", | ||
361 | (u32)is->memory.paddr); | ||
362 | dma_free_coherent(dev, is->memory.size, is->memory.vaddr, | ||
363 | is->memory.paddr); | ||
364 | return -EIO; | ||
365 | } | ||
366 | |||
367 | is->is_p_region = (struct is_region *)(is->memory.vaddr + | ||
368 | FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE); | ||
369 | |||
370 | is->is_dma_p_region = is->memory.paddr + | ||
371 | FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE; | ||
372 | |||
373 | is->is_shared_region = (struct is_share_region *)(is->memory.vaddr + | ||
374 | FIMC_IS_SHARED_REGION_OFFSET); | ||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | static void fimc_is_free_cpu_memory(struct fimc_is *is) | ||
379 | { | ||
380 | struct device *dev = &is->pdev->dev; | ||
381 | |||
382 | dma_free_coherent(dev, is->memory.size, is->memory.vaddr, | ||
383 | is->memory.paddr); | ||
384 | } | ||
385 | |||
386 | static void fimc_is_load_firmware(const struct firmware *fw, void *context) | ||
387 | { | ||
388 | struct fimc_is *is = context; | ||
389 | struct device *dev = &is->pdev->dev; | ||
390 | void *buf; | ||
391 | int ret; | ||
392 | |||
393 | if (fw == NULL) { | ||
394 | dev_err(dev, "firmware request failed\n"); | ||
395 | return; | ||
396 | } | ||
397 | mutex_lock(&is->lock); | ||
398 | |||
399 | if (fw->size < FIMC_IS_FW_SIZE_MIN || fw->size > FIMC_IS_FW_SIZE_MAX) { | ||
400 | dev_err(dev, "wrong firmware size: %d\n", fw->size); | ||
401 | goto done; | ||
402 | } | ||
403 | |||
404 | is->fw.size = fw->size; | ||
405 | |||
406 | ret = fimc_is_alloc_cpu_memory(is); | ||
407 | if (ret < 0) { | ||
408 | dev_err(dev, "failed to allocate FIMC-IS CPU memory\n"); | ||
409 | goto done; | ||
410 | } | ||
411 | |||
412 | memcpy(is->memory.vaddr, fw->data, fw->size); | ||
413 | wmb(); | ||
414 | |||
415 | /* Read firmware description. */ | ||
416 | buf = (void *)(is->memory.vaddr + fw->size - FIMC_IS_FW_DESC_LEN); | ||
417 | memcpy(&is->fw.info, buf, FIMC_IS_FW_INFO_LEN); | ||
418 | is->fw.info[FIMC_IS_FW_INFO_LEN] = 0; | ||
419 | |||
420 | buf = (void *)(is->memory.vaddr + fw->size - FIMC_IS_FW_VER_LEN); | ||
421 | memcpy(&is->fw.version, buf, FIMC_IS_FW_VER_LEN); | ||
422 | is->fw.version[FIMC_IS_FW_VER_LEN - 1] = 0; | ||
423 | |||
424 | is->fw.state = 1; | ||
425 | |||
426 | dev_info(dev, "loaded firmware: %s, rev. %s\n", | ||
427 | is->fw.info, is->fw.version); | ||
428 | dev_dbg(dev, "FW size: %d, paddr: %#x\n", fw->size, is->memory.paddr); | ||
429 | |||
430 | is->is_shared_region->chip_id = 0xe4412; | ||
431 | is->is_shared_region->chip_rev_no = 1; | ||
432 | |||
433 | fimc_is_mem_barrier(); | ||
434 | |||
435 | /* | ||
436 | * FIXME: The firmware is not being released for now, as it is | ||
437 | * needed around for copying to the IS working memory every | ||
438 | * time before the Cortex-A5 is restarted. | ||
439 | */ | ||
440 | if (is->fw.f_w) | ||
441 | release_firmware(is->fw.f_w); | ||
442 | is->fw.f_w = fw; | ||
443 | done: | ||
444 | mutex_unlock(&is->lock); | ||
445 | } | ||
446 | |||
447 | static int fimc_is_request_firmware(struct fimc_is *is, const char *fw_name) | ||
448 | { | ||
449 | return request_firmware_nowait(THIS_MODULE, | ||
450 | FW_ACTION_HOTPLUG, fw_name, &is->pdev->dev, | ||
451 | GFP_KERNEL, is, fimc_is_load_firmware); | ||
452 | } | ||
453 | |||
454 | /* General IS interrupt handler */ | ||
455 | static void fimc_is_general_irq_handler(struct fimc_is *is) | ||
456 | { | ||
457 | is->i2h_cmd.cmd = mcuctl_read(is, MCUCTL_REG_ISSR(10)); | ||
458 | |||
459 | switch (is->i2h_cmd.cmd) { | ||
460 | case IHC_GET_SENSOR_NUM: | ||
461 | fimc_is_hw_get_params(is, 1); | ||
462 | fimc_is_hw_wait_intmsr0_intmsd0(is); | ||
463 | fimc_is_hw_set_sensor_num(is); | ||
464 | pr_debug("ISP FW version: %#x\n", is->i2h_cmd.args[0]); | ||
465 | break; | ||
466 | case IHC_SET_FACE_MARK: | ||
467 | case IHC_FRAME_DONE: | ||
468 | fimc_is_hw_get_params(is, 2); | ||
469 | break; | ||
470 | case IHC_SET_SHOT_MARK: | ||
471 | case IHC_AA_DONE: | ||
472 | case IH_REPLY_DONE: | ||
473 | fimc_is_hw_get_params(is, 3); | ||
474 | break; | ||
475 | case IH_REPLY_NOT_DONE: | ||
476 | fimc_is_hw_get_params(is, 4); | ||
477 | break; | ||
478 | case IHC_NOT_READY: | ||
479 | break; | ||
480 | default: | ||
481 | pr_info("unknown command: %#x\n", is->i2h_cmd.cmd); | ||
482 | } | ||
483 | |||
484 | fimc_is_fw_clear_irq1(is, FIMC_IS_INT_GENERAL); | ||
485 | |||
486 | switch (is->i2h_cmd.cmd) { | ||
487 | case IHC_GET_SENSOR_NUM: | ||
488 | fimc_is_hw_set_intgr0_gd0(is); | ||
489 | set_bit(IS_ST_A5_PWR_ON, &is->state); | ||
490 | break; | ||
491 | |||
492 | case IHC_SET_SHOT_MARK: | ||
493 | break; | ||
494 | |||
495 | case IHC_SET_FACE_MARK: | ||
496 | is->fd_header.count = is->i2h_cmd.args[0]; | ||
497 | is->fd_header.index = is->i2h_cmd.args[1]; | ||
498 | is->fd_header.offset = 0; | ||
499 | break; | ||
500 | |||
501 | case IHC_FRAME_DONE: | ||
502 | break; | ||
503 | |||
504 | case IHC_AA_DONE: | ||
505 | pr_debug("AA_DONE - %d, %d, %d\n", is->i2h_cmd.args[0], | ||
506 | is->i2h_cmd.args[1], is->i2h_cmd.args[2]); | ||
507 | break; | ||
508 | |||
509 | case IH_REPLY_DONE: | ||
510 | pr_debug("ISR_DONE: args[0]: %#x\n", is->i2h_cmd.args[0]); | ||
511 | |||
512 | switch (is->i2h_cmd.args[0]) { | ||
513 | case HIC_PREVIEW_STILL...HIC_CAPTURE_VIDEO: | ||
514 | /* Get CAC margin */ | ||
515 | set_bit(IS_ST_CHANGE_MODE, &is->state); | ||
516 | is->isp.cac_margin_x = is->i2h_cmd.args[1]; | ||
517 | is->isp.cac_margin_y = is->i2h_cmd.args[2]; | ||
518 | pr_debug("CAC margin (x,y): (%d,%d)\n", | ||
519 | is->isp.cac_margin_x, is->isp.cac_margin_y); | ||
520 | break; | ||
521 | |||
522 | case HIC_STREAM_ON: | ||
523 | clear_bit(IS_ST_STREAM_OFF, &is->state); | ||
524 | set_bit(IS_ST_STREAM_ON, &is->state); | ||
525 | break; | ||
526 | |||
527 | case HIC_STREAM_OFF: | ||
528 | clear_bit(IS_ST_STREAM_ON, &is->state); | ||
529 | set_bit(IS_ST_STREAM_OFF, &is->state); | ||
530 | break; | ||
531 | |||
532 | case HIC_SET_PARAMETER: | ||
533 | is->config[is->config_index].p_region_index1 = 0; | ||
534 | is->config[is->config_index].p_region_index2 = 0; | ||
535 | set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state); | ||
536 | pr_debug("HIC_SET_PARAMETER\n"); | ||
537 | break; | ||
538 | |||
539 | case HIC_GET_PARAMETER: | ||
540 | break; | ||
541 | |||
542 | case HIC_SET_TUNE: | ||
543 | break; | ||
544 | |||
545 | case HIC_GET_STATUS: | ||
546 | break; | ||
547 | |||
548 | case HIC_OPEN_SENSOR: | ||
549 | set_bit(IS_ST_OPEN_SENSOR, &is->state); | ||
550 | pr_debug("data lanes: %d, settle line: %d\n", | ||
551 | is->i2h_cmd.args[2], is->i2h_cmd.args[1]); | ||
552 | break; | ||
553 | |||
554 | case HIC_CLOSE_SENSOR: | ||
555 | clear_bit(IS_ST_OPEN_SENSOR, &is->state); | ||
556 | is->sensor_index = 0; | ||
557 | break; | ||
558 | |||
559 | case HIC_MSG_TEST: | ||
560 | pr_debug("config MSG level completed\n"); | ||
561 | break; | ||
562 | |||
563 | case HIC_POWER_DOWN: | ||
564 | clear_bit(IS_ST_PWR_SUBIP_ON, &is->state); | ||
565 | break; | ||
566 | |||
567 | case HIC_GET_SET_FILE_ADDR: | ||
568 | is->setfile.base = is->i2h_cmd.args[1]; | ||
569 | set_bit(IS_ST_SETFILE_LOADED, &is->state); | ||
570 | break; | ||
571 | |||
572 | case HIC_LOAD_SET_FILE: | ||
573 | set_bit(IS_ST_SETFILE_LOADED, &is->state); | ||
574 | break; | ||
575 | } | ||
576 | break; | ||
577 | |||
578 | case IH_REPLY_NOT_DONE: | ||
579 | pr_err("ISR_NDONE: %d: %#x, %s\n", is->i2h_cmd.args[0], | ||
580 | is->i2h_cmd.args[1], | ||
581 | fimc_is_strerr(is->i2h_cmd.args[1])); | ||
582 | |||
583 | if (is->i2h_cmd.args[1] & IS_ERROR_TIME_OUT_FLAG) | ||
584 | pr_err("IS_ERROR_TIME_OUT\n"); | ||
585 | |||
586 | switch (is->i2h_cmd.args[1]) { | ||
587 | case IS_ERROR_SET_PARAMETER: | ||
588 | fimc_is_mem_barrier(); | ||
589 | } | ||
590 | |||
591 | switch (is->i2h_cmd.args[0]) { | ||
592 | case HIC_SET_PARAMETER: | ||
593 | is->config[is->config_index].p_region_index1 = 0; | ||
594 | is->config[is->config_index].p_region_index2 = 0; | ||
595 | set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state); | ||
596 | break; | ||
597 | } | ||
598 | break; | ||
599 | |||
600 | case IHC_NOT_READY: | ||
601 | pr_err("IS control sequence error: Not Ready\n"); | ||
602 | break; | ||
603 | } | ||
604 | |||
605 | wake_up(&is->irq_queue); | ||
606 | } | ||
607 | |||
608 | static irqreturn_t fimc_is_irq_handler(int irq, void *priv) | ||
609 | { | ||
610 | struct fimc_is *is = priv; | ||
611 | unsigned long flags; | ||
612 | u32 status; | ||
613 | |||
614 | spin_lock_irqsave(&is->slock, flags); | ||
615 | status = mcuctl_read(is, MCUCTL_REG_INTSR1); | ||
616 | |||
617 | if (status & (1UL << FIMC_IS_INT_GENERAL)) | ||
618 | fimc_is_general_irq_handler(is); | ||
619 | |||
620 | if (status & (1UL << FIMC_IS_INT_FRAME_DONE_ISP)) | ||
621 | fimc_isp_irq_handler(is); | ||
622 | |||
623 | spin_unlock_irqrestore(&is->slock, flags); | ||
624 | return IRQ_HANDLED; | ||
625 | } | ||
626 | |||
627 | static int fimc_is_hw_open_sensor(struct fimc_is *is, | ||
628 | struct fimc_is_sensor *sensor) | ||
629 | { | ||
630 | struct sensor_open_extended *soe = (void *)&is->is_p_region->shared; | ||
631 | |||
632 | fimc_is_hw_wait_intmsr0_intmsd0(is); | ||
633 | |||
634 | soe->self_calibration_mode = 1; | ||
635 | soe->actuator_type = 0; | ||
636 | soe->mipi_lane_num = 0; | ||
637 | soe->mclk = 0; | ||
638 | soe->mipi_speed = 0; | ||
639 | soe->fast_open_sensor = 0; | ||
640 | soe->i2c_sclk = 88000000; | ||
641 | |||
642 | fimc_is_mem_barrier(); | ||
643 | |||
644 | mcuctl_write(HIC_OPEN_SENSOR, is, MCUCTL_REG_ISSR(0)); | ||
645 | mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); | ||
646 | mcuctl_write(sensor->drvdata->id, is, MCUCTL_REG_ISSR(2)); | ||
647 | mcuctl_write(sensor->i2c_bus, is, MCUCTL_REG_ISSR(3)); | ||
648 | mcuctl_write(is->is_dma_p_region, is, MCUCTL_REG_ISSR(4)); | ||
649 | |||
650 | fimc_is_hw_set_intgr0_gd0(is); | ||
651 | |||
652 | return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1, | ||
653 | FIMC_IS_SENSOR_OPEN_TIMEOUT); | ||
654 | } | ||
655 | |||
656 | |||
657 | int fimc_is_hw_initialize(struct fimc_is *is) | ||
658 | { | ||
659 | const int config_ids[] = { | ||
660 | IS_SC_PREVIEW_STILL, IS_SC_PREVIEW_VIDEO, | ||
661 | IS_SC_CAPTURE_STILL, IS_SC_CAPTURE_VIDEO | ||
662 | }; | ||
663 | struct device *dev = &is->pdev->dev; | ||
664 | u32 prev_id; | ||
665 | int i, ret; | ||
666 | |||
667 | /* Sensor initialization. */ | ||
668 | ret = fimc_is_hw_open_sensor(is, is->sensor); | ||
669 | if (ret < 0) | ||
670 | return ret; | ||
671 | |||
672 | /* Get the setfile address. */ | ||
673 | fimc_is_hw_get_setfile_addr(is); | ||
674 | |||
675 | ret = fimc_is_wait_event(is, IS_ST_SETFILE_LOADED, 1, | ||
676 | FIMC_IS_CONFIG_TIMEOUT); | ||
677 | if (ret < 0) { | ||
678 | dev_err(dev, "get setfile address timed out\n"); | ||
679 | return ret; | ||
680 | } | ||
681 | pr_debug("setfile.base: %#x\n", is->setfile.base); | ||
682 | |||
683 | /* Load the setfile. */ | ||
684 | fimc_is_load_setfile(is, FIMC_IS_SETFILE_6A3); | ||
685 | clear_bit(IS_ST_SETFILE_LOADED, &is->state); | ||
686 | fimc_is_hw_load_setfile(is); | ||
687 | ret = fimc_is_wait_event(is, IS_ST_SETFILE_LOADED, 1, | ||
688 | FIMC_IS_CONFIG_TIMEOUT); | ||
689 | if (ret < 0) { | ||
690 | dev_err(dev, "loading setfile timed out\n"); | ||
691 | return ret; | ||
692 | } | ||
693 | |||
694 | pr_debug("setfile: base: %#x, size: %d\n", | ||
695 | is->setfile.base, is->setfile.size); | ||
696 | pr_info("FIMC-IS Setfile info: %s\n", is->fw.setfile_info); | ||
697 | |||
698 | /* Check magic number. */ | ||
699 | if (is->is_p_region->shared[MAX_SHARED_COUNT - 1] != | ||
700 | FIMC_IS_MAGIC_NUMBER) { | ||
701 | dev_err(dev, "magic number error!\n"); | ||
702 | return -EIO; | ||
703 | } | ||
704 | |||
705 | pr_debug("shared region: %#x, parameter region: %#x\n", | ||
706 | is->memory.paddr + FIMC_IS_SHARED_REGION_OFFSET, | ||
707 | is->is_dma_p_region); | ||
708 | |||
709 | is->setfile.sub_index = 0; | ||
710 | |||
711 | /* Stream off. */ | ||
712 | fimc_is_hw_stream_off(is); | ||
713 | ret = fimc_is_wait_event(is, IS_ST_STREAM_OFF, 1, | ||
714 | FIMC_IS_CONFIG_TIMEOUT); | ||
715 | if (ret < 0) { | ||
716 | dev_err(dev, "stream off timeout\n"); | ||
717 | return ret; | ||
718 | } | ||
719 | |||
720 | /* Preserve previous mode. */ | ||
721 | prev_id = is->config_index; | ||
722 | |||
723 | /* Set initial parameter values. */ | ||
724 | for (i = 0; i < ARRAY_SIZE(config_ids); i++) { | ||
725 | is->config_index = config_ids[i]; | ||
726 | fimc_is_set_initial_params(is); | ||
727 | ret = fimc_is_itf_s_param(is, true); | ||
728 | if (ret < 0) { | ||
729 | is->config_index = prev_id; | ||
730 | return ret; | ||
731 | } | ||
732 | } | ||
733 | is->config_index = prev_id; | ||
734 | |||
735 | set_bit(IS_ST_INIT_DONE, &is->state); | ||
736 | dev_info(dev, "initialization sequence completed (%d)\n", | ||
737 | is->config_index); | ||
738 | return 0; | ||
739 | } | ||
740 | |||
741 | static int fimc_is_log_show(struct seq_file *s, void *data) | ||
742 | { | ||
743 | struct fimc_is *is = s->private; | ||
744 | const u8 *buf = is->memory.vaddr + FIMC_IS_DEBUG_REGION_OFFSET; | ||
745 | |||
746 | if (is->memory.vaddr == NULL) { | ||
747 | dev_err(&is->pdev->dev, "firmware memory is not initialized\n"); | ||
748 | return -EIO; | ||
749 | } | ||
750 | |||
751 | seq_printf(s, "%s\n", buf); | ||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | static int fimc_is_debugfs_open(struct inode *inode, struct file *file) | ||
756 | { | ||
757 | return single_open(file, fimc_is_log_show, inode->i_private); | ||
758 | } | ||
759 | |||
760 | static const struct file_operations fimc_is_debugfs_fops = { | ||
761 | .open = fimc_is_debugfs_open, | ||
762 | .read = seq_read, | ||
763 | .llseek = seq_lseek, | ||
764 | .release = single_release, | ||
765 | }; | ||
766 | |||
767 | static void fimc_is_debugfs_remove(struct fimc_is *is) | ||
768 | { | ||
769 | debugfs_remove_recursive(is->debugfs_entry); | ||
770 | is->debugfs_entry = NULL; | ||
771 | } | ||
772 | |||
773 | static int fimc_is_debugfs_create(struct fimc_is *is) | ||
774 | { | ||
775 | struct dentry *dentry; | ||
776 | |||
777 | is->debugfs_entry = debugfs_create_dir("fimc_is", NULL); | ||
778 | |||
779 | dentry = debugfs_create_file("fw_log", S_IRUGO, is->debugfs_entry, | ||
780 | is, &fimc_is_debugfs_fops); | ||
781 | if (!dentry) | ||
782 | fimc_is_debugfs_remove(is); | ||
783 | |||
784 | return is->debugfs_entry == NULL ? -EIO : 0; | ||
785 | } | ||
786 | |||
787 | static int fimc_is_probe(struct platform_device *pdev) | ||
788 | { | ||
789 | struct device *dev = &pdev->dev; | ||
790 | struct fimc_is *is; | ||
791 | struct resource res; | ||
792 | struct device_node *node; | ||
793 | int ret; | ||
794 | |||
795 | is = devm_kzalloc(&pdev->dev, sizeof(*is), GFP_KERNEL); | ||
796 | if (!is) | ||
797 | return -ENOMEM; | ||
798 | |||
799 | is->pdev = pdev; | ||
800 | is->isp.pdev = pdev; | ||
801 | |||
802 | init_waitqueue_head(&is->irq_queue); | ||
803 | spin_lock_init(&is->slock); | ||
804 | mutex_init(&is->lock); | ||
805 | |||
806 | ret = of_address_to_resource(dev->of_node, 0, &res); | ||
807 | if (ret < 0) | ||
808 | return ret; | ||
809 | |||
810 | is->regs = devm_ioremap_resource(dev, &res); | ||
811 | if (IS_ERR(is->regs)) | ||
812 | return PTR_ERR(is->regs); | ||
813 | |||
814 | node = of_get_child_by_name(dev->of_node, "pmu"); | ||
815 | if (!node) | ||
816 | return -ENODEV; | ||
817 | |||
818 | is->pmu_regs = of_iomap(node, 0); | ||
819 | if (!is->pmu_regs) | ||
820 | return -ENOMEM; | ||
821 | |||
822 | is->irq = irq_of_parse_and_map(dev->of_node, 0); | ||
823 | if (is->irq < 0) { | ||
824 | dev_err(dev, "no irq found\n"); | ||
825 | return is->irq; | ||
826 | } | ||
827 | |||
828 | ret = fimc_is_get_clocks(is); | ||
829 | if (ret < 0) | ||
830 | return ret; | ||
831 | |||
832 | platform_set_drvdata(pdev, is); | ||
833 | |||
834 | ret = request_irq(is->irq, fimc_is_irq_handler, 0, dev_name(dev), is); | ||
835 | if (ret < 0) { | ||
836 | dev_err(dev, "irq request failed\n"); | ||
837 | goto err_clk; | ||
838 | } | ||
839 | pm_runtime_enable(dev); | ||
840 | /* | ||
841 | * Enable only the ISP power domain, keep FIMC-IS clocks off until | ||
842 | * the whole clock tree is configured. The ISP power domain needs | ||
843 | * be active in order to acces any CMU_ISP clock registers. | ||
844 | */ | ||
845 | ret = pm_runtime_get_sync(dev); | ||
846 | if (ret < 0) | ||
847 | goto err_irq; | ||
848 | |||
849 | ret = fimc_is_setup_clocks(is); | ||
850 | pm_runtime_put_sync(dev); | ||
851 | |||
852 | if (ret < 0) | ||
853 | goto err_irq; | ||
854 | |||
855 | is->clk_init = true; | ||
856 | |||
857 | is->alloc_ctx = vb2_dma_contig_init_ctx(dev); | ||
858 | if (IS_ERR(is->alloc_ctx)) { | ||
859 | ret = PTR_ERR(is->alloc_ctx); | ||
860 | goto err_irq; | ||
861 | } | ||
862 | /* | ||
863 | * Register FIMC-IS V4L2 subdevs to this driver. The video nodes | ||
864 | * will be created within the subdev's registered() callback. | ||
865 | */ | ||
866 | ret = fimc_is_register_subdevs(is); | ||
867 | if (ret < 0) | ||
868 | goto err_vb; | ||
869 | |||
870 | ret = fimc_is_debugfs_create(is); | ||
871 | if (ret < 0) | ||
872 | goto err_sd; | ||
873 | |||
874 | ret = fimc_is_request_firmware(is, FIMC_IS_FW_FILENAME); | ||
875 | if (ret < 0) | ||
876 | goto err_dfs; | ||
877 | |||
878 | dev_dbg(dev, "FIMC-IS registered successfully\n"); | ||
879 | return 0; | ||
880 | |||
881 | err_dfs: | ||
882 | fimc_is_debugfs_remove(is); | ||
883 | err_vb: | ||
884 | vb2_dma_contig_cleanup_ctx(is->alloc_ctx); | ||
885 | err_sd: | ||
886 | fimc_is_unregister_subdevs(is); | ||
887 | err_irq: | ||
888 | free_irq(is->irq, is); | ||
889 | err_clk: | ||
890 | fimc_is_put_clocks(is); | ||
891 | return ret; | ||
892 | } | ||
893 | |||
894 | static int fimc_is_runtime_resume(struct device *dev) | ||
895 | { | ||
896 | struct fimc_is *is = dev_get_drvdata(dev); | ||
897 | |||
898 | if (!is->clk_init) | ||
899 | return 0; | ||
900 | |||
901 | return fimc_is_enable_clocks(is); | ||
902 | } | ||
903 | |||
904 | static int fimc_is_runtime_suspend(struct device *dev) | ||
905 | { | ||
906 | struct fimc_is *is = dev_get_drvdata(dev); | ||
907 | |||
908 | if (is->clk_init) | ||
909 | fimc_is_disable_clocks(is); | ||
910 | |||
911 | return 0; | ||
912 | } | ||
913 | |||
914 | #ifdef CONFIG_PM_SLEEP | ||
915 | static int fimc_is_resume(struct device *dev) | ||
916 | { | ||
917 | /* TODO: */ | ||
918 | return 0; | ||
919 | } | ||
920 | |||
921 | static int fimc_is_suspend(struct device *dev) | ||
922 | { | ||
923 | struct fimc_is *is = dev_get_drvdata(dev); | ||
924 | |||
925 | /* TODO: */ | ||
926 | if (test_bit(IS_ST_A5_PWR_ON, &is->state)) | ||
927 | return -EBUSY; | ||
928 | |||
929 | return 0; | ||
930 | } | ||
931 | #endif /* CONFIG_PM_SLEEP */ | ||
932 | |||
933 | static int fimc_is_remove(struct platform_device *pdev) | ||
934 | { | ||
935 | struct fimc_is *is = platform_get_drvdata(pdev); | ||
936 | |||
937 | pm_runtime_disable(&pdev->dev); | ||
938 | pm_runtime_set_suspended(&pdev->dev); | ||
939 | free_irq(is->irq, is); | ||
940 | fimc_is_unregister_subdevs(is); | ||
941 | vb2_dma_contig_cleanup_ctx(is->alloc_ctx); | ||
942 | fimc_is_put_clocks(is); | ||
943 | fimc_is_debugfs_remove(is); | ||
944 | release_firmware(is->fw.f_w); | ||
945 | fimc_is_free_cpu_memory(is); | ||
946 | |||
947 | return 0; | ||
948 | } | ||
949 | |||
950 | static const struct of_device_id fimc_is_of_match[] = { | ||
951 | { .compatible = "samsung,exynos4212-fimc-is" }, | ||
952 | { /* sentinel */ }, | ||
953 | }; | ||
954 | MODULE_DEVICE_TABLE(of, fimc_is_of_match); | ||
955 | |||
956 | static const struct dev_pm_ops fimc_is_pm_ops = { | ||
957 | SET_SYSTEM_SLEEP_PM_OPS(fimc_is_suspend, fimc_is_resume) | ||
958 | SET_RUNTIME_PM_OPS(fimc_is_runtime_suspend, fimc_is_runtime_resume, | ||
959 | NULL) | ||
960 | }; | ||
961 | |||
962 | static struct platform_driver fimc_is_driver = { | ||
963 | .probe = fimc_is_probe, | ||
964 | .remove = fimc_is_remove, | ||
965 | .driver = { | ||
966 | .of_match_table = fimc_is_of_match, | ||
967 | .name = FIMC_IS_DRV_NAME, | ||
968 | .owner = THIS_MODULE, | ||
969 | .pm = &fimc_is_pm_ops, | ||
970 | } | ||
971 | }; | ||
972 | |||
973 | static int fimc_is_module_init(void) | ||
974 | { | ||
975 | int ret; | ||
976 | |||
977 | ret = fimc_is_register_sensor_driver(); | ||
978 | if (ret < 0) | ||
979 | return ret; | ||
980 | |||
981 | ret = fimc_is_register_i2c_driver(); | ||
982 | if (ret < 0) | ||
983 | goto err_sens; | ||
984 | |||
985 | ret = platform_driver_register(&fimc_is_driver); | ||
986 | if (!ret) | ||
987 | return ret; | ||
988 | |||
989 | fimc_is_unregister_i2c_driver(); | ||
990 | err_sens: | ||
991 | fimc_is_unregister_sensor_driver(); | ||
992 | return ret; | ||
993 | } | ||
994 | |||
995 | static void fimc_is_module_exit(void) | ||
996 | { | ||
997 | fimc_is_unregister_sensor_driver(); | ||
998 | fimc_is_unregister_i2c_driver(); | ||
999 | platform_driver_unregister(&fimc_is_driver); | ||
1000 | } | ||
1001 | |||
1002 | module_init(fimc_is_module_init); | ||
1003 | module_exit(fimc_is_module_exit); | ||
1004 | |||
1005 | MODULE_ALIAS("platform:" FIMC_IS_DRV_NAME); | ||
1006 | MODULE_AUTHOR("Younghwan Joo <yhwan.joo@samsung.com>"); | ||
1007 | MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h new file mode 100644 index 000000000000..f5275a5b0156 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-is.h | |||
@@ -0,0 +1,345 @@ | |||
1 | /* | ||
2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Authors: Younghwan Joo <yhwan.joo@samsung.com> | ||
7 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #ifndef FIMC_IS_H_ | ||
14 | #define FIMC_IS_H_ | ||
15 | |||
16 | #include <asm/barrier.h> | ||
17 | #include <linux/clk.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/pinctrl/consumer.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/sizes.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/types.h> | ||
25 | #include <media/videobuf2-core.h> | ||
26 | #include <media/v4l2-ctrls.h> | ||
27 | |||
28 | #include "fimc-isp.h" | ||
29 | #include "fimc-is-command.h" | ||
30 | #include "fimc-is-sensor.h" | ||
31 | #include "fimc-is-param.h" | ||
32 | #include "fimc-is-regs.h" | ||
33 | |||
34 | #define FIMC_IS_DRV_NAME "exynos4-fimc-is" | ||
35 | |||
36 | #define FIMC_IS_FW_FILENAME "fimc_is_fw.bin" | ||
37 | #define FIMC_IS_SETFILE_6A3 "setfile.bin" | ||
38 | |||
39 | #define FIMC_IS_FW_LOAD_TIMEOUT 1000 /* ms */ | ||
40 | #define FIMC_IS_POWER_ON_TIMEOUT 1000 /* us */ | ||
41 | |||
42 | #define FIMC_IS_SENSOR_NUM 2 | ||
43 | |||
44 | /* Memory definitions */ | ||
45 | #define FIMC_IS_CPU_MEM_SIZE (0xa00000) | ||
46 | #define FIMC_IS_CPU_BASE_MASK ((1 << 26) - 1) | ||
47 | #define FIMC_IS_REGION_SIZE 0x5000 | ||
48 | |||
49 | #define FIMC_IS_DEBUG_REGION_OFFSET 0x0084b000 | ||
50 | #define FIMC_IS_SHARED_REGION_OFFSET 0x008c0000 | ||
51 | #define FIMC_IS_FW_INFO_LEN 31 | ||
52 | #define FIMC_IS_FW_VER_LEN 7 | ||
53 | #define FIMC_IS_FW_DESC_LEN (FIMC_IS_FW_INFO_LEN + \ | ||
54 | FIMC_IS_FW_VER_LEN) | ||
55 | #define FIMC_IS_SETFILE_INFO_LEN 39 | ||
56 | |||
57 | #define FIMC_IS_EXTRA_MEM_SIZE (FIMC_IS_EXTRA_FW_SIZE + \ | ||
58 | FIMC_IS_EXTRA_SETFILE_SIZE + 0x1000) | ||
59 | #define FIMC_IS_EXTRA_FW_SIZE 0x180000 | ||
60 | #define FIMC_IS_EXTRA_SETFILE_SIZE 0x4b000 | ||
61 | |||
62 | /* TODO: revisit */ | ||
63 | #define FIMC_IS_FW_ADDR_MASK ((1 << 26) - 1) | ||
64 | #define FIMC_IS_FW_SIZE_MAX (SZ_4M) | ||
65 | #define FIMC_IS_FW_SIZE_MIN (SZ_32K) | ||
66 | |||
67 | #define ATCLK_MCUISP_FREQUENCY 100000000UL | ||
68 | #define ACLK_AXI_FREQUENCY 100000000UL | ||
69 | |||
70 | enum { | ||
71 | ISS_CLK_PPMUISPX, | ||
72 | ISS_CLK_PPMUISPMX, | ||
73 | ISS_CLK_LITE0, | ||
74 | ISS_CLK_LITE1, | ||
75 | ISS_CLK_MPLL, | ||
76 | ISS_CLK_SYSREG, | ||
77 | ISS_CLK_ISP, | ||
78 | ISS_CLK_DRC, | ||
79 | ISS_CLK_FD, | ||
80 | ISS_CLK_MCUISP, | ||
81 | ISS_CLK_UART, | ||
82 | ISS_GATE_CLKS_MAX, | ||
83 | ISS_CLK_ISP_DIV0 = ISS_GATE_CLKS_MAX, | ||
84 | ISS_CLK_ISP_DIV1, | ||
85 | ISS_CLK_MCUISP_DIV0, | ||
86 | ISS_CLK_MCUISP_DIV1, | ||
87 | ISS_CLK_ACLK200, | ||
88 | ISS_CLK_ACLK200_DIV, | ||
89 | ISS_CLK_ACLK400MCUISP, | ||
90 | ISS_CLK_ACLK400MCUISP_DIV, | ||
91 | ISS_CLKS_MAX | ||
92 | }; | ||
93 | |||
94 | /* The driver's internal state flags */ | ||
95 | enum { | ||
96 | IS_ST_IDLE, | ||
97 | IS_ST_PWR_ON, | ||
98 | IS_ST_A5_PWR_ON, | ||
99 | IS_ST_FW_LOADED, | ||
100 | IS_ST_OPEN_SENSOR, | ||
101 | IS_ST_SETFILE_LOADED, | ||
102 | IS_ST_INIT_DONE, | ||
103 | IS_ST_STREAM_ON, | ||
104 | IS_ST_STREAM_OFF, | ||
105 | IS_ST_CHANGE_MODE, | ||
106 | IS_ST_BLOCK_CMD_CLEARED, | ||
107 | IS_ST_SET_ZOOM, | ||
108 | IS_ST_PWR_SUBIP_ON, | ||
109 | IS_ST_END, | ||
110 | }; | ||
111 | |||
112 | enum af_state { | ||
113 | FIMC_IS_AF_IDLE = 0, | ||
114 | FIMC_IS_AF_SETCONFIG = 1, | ||
115 | FIMC_IS_AF_RUNNING = 2, | ||
116 | FIMC_IS_AF_LOCK = 3, | ||
117 | FIMC_IS_AF_ABORT = 4, | ||
118 | FIMC_IS_AF_FAILED = 5, | ||
119 | }; | ||
120 | |||
121 | enum af_lock_state { | ||
122 | FIMC_IS_AF_UNLOCKED = 0, | ||
123 | FIMC_IS_AF_LOCKED = 2 | ||
124 | }; | ||
125 | |||
126 | enum ae_lock_state { | ||
127 | FIMC_IS_AE_UNLOCKED = 0, | ||
128 | FIMC_IS_AE_LOCKED = 1 | ||
129 | }; | ||
130 | |||
131 | enum awb_lock_state { | ||
132 | FIMC_IS_AWB_UNLOCKED = 0, | ||
133 | FIMC_IS_AWB_LOCKED = 1 | ||
134 | }; | ||
135 | |||
136 | enum { | ||
137 | IS_METERING_CONFIG_CMD, | ||
138 | IS_METERING_CONFIG_WIN_POS_X, | ||
139 | IS_METERING_CONFIG_WIN_POS_Y, | ||
140 | IS_METERING_CONFIG_WIN_WIDTH, | ||
141 | IS_METERING_CONFIG_WIN_HEIGHT, | ||
142 | IS_METERING_CONFIG_MAX | ||
143 | }; | ||
144 | |||
145 | struct is_setfile { | ||
146 | const struct firmware *info; | ||
147 | int state; | ||
148 | u32 sub_index; | ||
149 | u32 base; | ||
150 | size_t size; | ||
151 | }; | ||
152 | |||
153 | struct is_fd_result_header { | ||
154 | u32 offset; | ||
155 | u32 count; | ||
156 | u32 index; | ||
157 | u32 curr_index; | ||
158 | u32 width; | ||
159 | u32 height; | ||
160 | }; | ||
161 | |||
162 | struct is_af_info { | ||
163 | u16 mode; | ||
164 | u32 af_state; | ||
165 | u32 af_lock_state; | ||
166 | u32 ae_lock_state; | ||
167 | u32 awb_lock_state; | ||
168 | u16 pos_x; | ||
169 | u16 pos_y; | ||
170 | u16 prev_pos_x; | ||
171 | u16 prev_pos_y; | ||
172 | u16 use_af; | ||
173 | }; | ||
174 | |||
175 | struct fimc_is_firmware { | ||
176 | const struct firmware *f_w; | ||
177 | |||
178 | dma_addr_t paddr; | ||
179 | void *vaddr; | ||
180 | unsigned int size; | ||
181 | |||
182 | char info[FIMC_IS_FW_INFO_LEN + 1]; | ||
183 | char version[FIMC_IS_FW_VER_LEN + 1]; | ||
184 | char setfile_info[FIMC_IS_SETFILE_INFO_LEN + 1]; | ||
185 | u8 state; | ||
186 | }; | ||
187 | |||
188 | struct fimc_is_memory { | ||
189 | /* physical base address */ | ||
190 | dma_addr_t paddr; | ||
191 | /* virtual base address */ | ||
192 | void *vaddr; | ||
193 | /* total length */ | ||
194 | unsigned int size; | ||
195 | }; | ||
196 | |||
197 | #define FIMC_IS_I2H_MAX_ARGS 12 | ||
198 | |||
199 | struct i2h_cmd { | ||
200 | u32 cmd; | ||
201 | u32 sensor_id; | ||
202 | u16 num_args; | ||
203 | u32 args[FIMC_IS_I2H_MAX_ARGS]; | ||
204 | }; | ||
205 | |||
206 | struct h2i_cmd { | ||
207 | u16 cmd_type; | ||
208 | u32 entry_id; | ||
209 | }; | ||
210 | |||
211 | #define FIMC_IS_DEBUG_MSG 0x3f | ||
212 | #define FIMC_IS_DEBUG_LEVEL 3 | ||
213 | |||
214 | struct fimc_is_setfile { | ||
215 | const struct firmware *info; | ||
216 | unsigned int state; | ||
217 | unsigned int size; | ||
218 | u32 sub_index; | ||
219 | u32 base; | ||
220 | }; | ||
221 | |||
222 | struct chain_config { | ||
223 | struct global_param global; | ||
224 | struct sensor_param sensor; | ||
225 | struct isp_param isp; | ||
226 | struct drc_param drc; | ||
227 | struct fd_param fd; | ||
228 | |||
229 | unsigned long p_region_index1; | ||
230 | unsigned long p_region_index2; | ||
231 | }; | ||
232 | |||
233 | /** | ||
234 | * struct fimc_is - fimc-is data structure | ||
235 | * @pdev: pointer to FIMC-IS platform device | ||
236 | * @pctrl: pointer to pinctrl structure for this device | ||
237 | * @v4l2_dev: pointer to top the level v4l2_device | ||
238 | * @alloc_ctx: videobuf2 memory allocator context | ||
239 | * @lock: mutex serializing video device and the subdev operations | ||
240 | * @slock: spinlock protecting this data structure and the hw registers | ||
241 | * @clocks: FIMC-LITE gate clock | ||
242 | * @regs: MCUCTL mmapped registers region | ||
243 | * @pmu_regs: PMU ISP mmapped registers region | ||
244 | * @irq_queue: interrupt handling waitqueue | ||
245 | * @lpm: low power mode flag | ||
246 | * @state: internal driver's state flags | ||
247 | */ | ||
248 | struct fimc_is { | ||
249 | struct platform_device *pdev; | ||
250 | struct pinctrl *pctrl; | ||
251 | struct v4l2_device *v4l2_dev; | ||
252 | |||
253 | struct fimc_is_firmware fw; | ||
254 | struct fimc_is_memory memory; | ||
255 | struct firmware *f_w; | ||
256 | |||
257 | struct fimc_isp isp; | ||
258 | struct fimc_is_sensor *sensor; | ||
259 | struct fimc_is_setfile setfile; | ||
260 | |||
261 | struct vb2_alloc_ctx *alloc_ctx; | ||
262 | struct v4l2_ctrl_handler ctrl_handler; | ||
263 | |||
264 | struct mutex lock; | ||
265 | spinlock_t slock; | ||
266 | |||
267 | struct clk *clocks[ISS_CLKS_MAX]; | ||
268 | bool clk_init; | ||
269 | void __iomem *regs; | ||
270 | void __iomem *pmu_regs; | ||
271 | int irq; | ||
272 | wait_queue_head_t irq_queue; | ||
273 | u8 lpm; | ||
274 | |||
275 | unsigned long state; | ||
276 | unsigned int sensor_index; | ||
277 | |||
278 | struct i2h_cmd i2h_cmd; | ||
279 | struct h2i_cmd h2i_cmd; | ||
280 | struct is_fd_result_header fd_header; | ||
281 | |||
282 | struct chain_config config[IS_SC_MAX]; | ||
283 | unsigned config_index; | ||
284 | |||
285 | struct is_region *is_p_region; | ||
286 | dma_addr_t is_dma_p_region; | ||
287 | struct is_share_region *is_shared_region; | ||
288 | struct is_af_info af; | ||
289 | |||
290 | struct dentry *debugfs_entry; | ||
291 | }; | ||
292 | |||
293 | static inline struct fimc_is *fimc_isp_to_is(struct fimc_isp *isp) | ||
294 | { | ||
295 | return container_of(isp, struct fimc_is, isp); | ||
296 | } | ||
297 | |||
298 | static inline void fimc_is_mem_barrier(void) | ||
299 | { | ||
300 | mb(); | ||
301 | } | ||
302 | |||
303 | static inline void fimc_is_set_param_bit(struct fimc_is *is, int num) | ||
304 | { | ||
305 | struct chain_config *cfg = &is->config[is->config_index]; | ||
306 | |||
307 | if (num >= 32) | ||
308 | set_bit(num - 32, &cfg->p_region_index2); | ||
309 | else | ||
310 | set_bit(num, &cfg->p_region_index1); | ||
311 | } | ||
312 | |||
313 | static inline void fimc_is_set_param_ctrl_cmd(struct fimc_is *is, int cmd) | ||
314 | { | ||
315 | is->is_p_region->parameter.isp.control.cmd = cmd; | ||
316 | } | ||
317 | |||
318 | static inline void mcuctl_write(u32 v, struct fimc_is *is, unsigned int offset) | ||
319 | { | ||
320 | writel(v, is->regs + offset); | ||
321 | } | ||
322 | |||
323 | static inline u32 mcuctl_read(struct fimc_is *is, unsigned int offset) | ||
324 | { | ||
325 | return readl(is->regs + offset); | ||
326 | } | ||
327 | |||
328 | static inline void pmuisp_write(u32 v, struct fimc_is *is, unsigned int offset) | ||
329 | { | ||
330 | writel(v, is->pmu_regs + offset); | ||
331 | } | ||
332 | |||
333 | static inline u32 pmuisp_read(struct fimc_is *is, unsigned int offset) | ||
334 | { | ||
335 | return readl(is->pmu_regs + offset); | ||
336 | } | ||
337 | |||
338 | int fimc_is_wait_event(struct fimc_is *is, unsigned long bit, | ||
339 | unsigned int state, unsigned int timeout); | ||
340 | int fimc_is_cpu_set_power(struct fimc_is *is, int on); | ||
341 | int fimc_is_start_firmware(struct fimc_is *is); | ||
342 | int fimc_is_hw_initialize(struct fimc_is *is); | ||
343 | void fimc_is_log_dump(const char *level, const void *buf, size_t len); | ||
344 | |||
345 | #endif /* FIMC_IS_H_ */ | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c new file mode 100644 index 000000000000..d63947f7b302 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-isp.c | |||
@@ -0,0 +1,703 @@ | |||
1 | /* | ||
2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
7 | * Younghwan Joo <yhwan.joo@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ | ||
14 | |||
15 | #include <linux/device.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/list.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/printk.h> | ||
22 | #include <linux/pm_runtime.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/types.h> | ||
25 | #include <media/v4l2-device.h> | ||
26 | |||
27 | #include "media-dev.h" | ||
28 | #include "fimc-is-command.h" | ||
29 | #include "fimc-is-param.h" | ||
30 | #include "fimc-is-regs.h" | ||
31 | #include "fimc-is.h" | ||
32 | |||
33 | static int debug; | ||
34 | module_param_named(debug_isp, debug, int, S_IRUGO | S_IWUSR); | ||
35 | |||
36 | static const struct fimc_fmt fimc_isp_formats[FIMC_ISP_NUM_FORMATS] = { | ||
37 | { | ||
38 | .name = "RAW8 (GRBG)", | ||
39 | .fourcc = V4L2_PIX_FMT_SGRBG8, | ||
40 | .depth = { 8 }, | ||
41 | .color = FIMC_FMT_RAW8, | ||
42 | .memplanes = 1, | ||
43 | .mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8, | ||
44 | }, { | ||
45 | .name = "RAW10 (GRBG)", | ||
46 | .fourcc = V4L2_PIX_FMT_SGRBG10, | ||
47 | .depth = { 10 }, | ||
48 | .color = FIMC_FMT_RAW10, | ||
49 | .memplanes = 1, | ||
50 | .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10, | ||
51 | }, { | ||
52 | .name = "RAW12 (GRBG)", | ||
53 | .fourcc = V4L2_PIX_FMT_SGRBG12, | ||
54 | .depth = { 12 }, | ||
55 | .color = FIMC_FMT_RAW12, | ||
56 | .memplanes = 1, | ||
57 | .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12, | ||
58 | }, | ||
59 | }; | ||
60 | |||
61 | /** | ||
62 | * fimc_isp_find_format - lookup color format by fourcc or media bus code | ||
63 | * @pixelformat: fourcc to match, ignored if null | ||
64 | * @mbus_code: media bus code to match, ignored if null | ||
65 | * @index: index to the fimc_isp_formats array, ignored if negative | ||
66 | */ | ||
67 | const struct fimc_fmt *fimc_isp_find_format(const u32 *pixelformat, | ||
68 | const u32 *mbus_code, int index) | ||
69 | { | ||
70 | const struct fimc_fmt *fmt, *def_fmt = NULL; | ||
71 | unsigned int i; | ||
72 | int id = 0; | ||
73 | |||
74 | if (index >= (int)ARRAY_SIZE(fimc_isp_formats)) | ||
75 | return NULL; | ||
76 | |||
77 | for (i = 0; i < ARRAY_SIZE(fimc_isp_formats); ++i) { | ||
78 | fmt = &fimc_isp_formats[i]; | ||
79 | if (pixelformat && fmt->fourcc == *pixelformat) | ||
80 | return fmt; | ||
81 | if (mbus_code && fmt->mbus_code == *mbus_code) | ||
82 | return fmt; | ||
83 | if (index == id) | ||
84 | def_fmt = fmt; | ||
85 | id++; | ||
86 | } | ||
87 | return def_fmt; | ||
88 | } | ||
89 | |||
90 | void fimc_isp_irq_handler(struct fimc_is *is) | ||
91 | { | ||
92 | is->i2h_cmd.args[0] = mcuctl_read(is, MCUCTL_REG_ISSR(20)); | ||
93 | is->i2h_cmd.args[1] = mcuctl_read(is, MCUCTL_REG_ISSR(21)); | ||
94 | |||
95 | fimc_is_fw_clear_irq1(is, FIMC_IS_INT_FRAME_DONE_ISP); | ||
96 | |||
97 | /* TODO: Complete ISP DMA interrupt handler */ | ||
98 | wake_up(&is->irq_queue); | ||
99 | } | ||
100 | |||
101 | /* Capture subdev media entity operations */ | ||
102 | static int fimc_is_link_setup(struct media_entity *entity, | ||
103 | const struct media_pad *local, | ||
104 | const struct media_pad *remote, u32 flags) | ||
105 | { | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static const struct media_entity_operations fimc_is_subdev_media_ops = { | ||
110 | .link_setup = fimc_is_link_setup, | ||
111 | }; | ||
112 | |||
113 | static int fimc_is_subdev_enum_mbus_code(struct v4l2_subdev *sd, | ||
114 | struct v4l2_subdev_fh *fh, | ||
115 | struct v4l2_subdev_mbus_code_enum *code) | ||
116 | { | ||
117 | const struct fimc_fmt *fmt; | ||
118 | |||
119 | fmt = fimc_isp_find_format(NULL, NULL, code->index); | ||
120 | if (!fmt) | ||
121 | return -EINVAL; | ||
122 | code->code = fmt->mbus_code; | ||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd, | ||
127 | struct v4l2_subdev_fh *fh, | ||
128 | struct v4l2_subdev_format *fmt) | ||
129 | { | ||
130 | struct fimc_isp *isp = v4l2_get_subdevdata(sd); | ||
131 | struct fimc_is *is = fimc_isp_to_is(isp); | ||
132 | struct v4l2_mbus_framefmt *mf = &fmt->format; | ||
133 | struct v4l2_mbus_framefmt cur_fmt; | ||
134 | |||
135 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
136 | mf = v4l2_subdev_get_try_format(fh, fmt->pad); | ||
137 | fmt->format = *mf; | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | mf->colorspace = V4L2_COLORSPACE_JPEG; | ||
142 | |||
143 | mutex_lock(&isp->subdev_lock); | ||
144 | __is_get_frame_size(is, &cur_fmt); | ||
145 | |||
146 | if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { | ||
147 | /* full camera input frame size */ | ||
148 | mf->width = cur_fmt.width + FIMC_ISP_CAC_MARGIN_WIDTH; | ||
149 | mf->height = cur_fmt.height + FIMC_ISP_CAC_MARGIN_HEIGHT; | ||
150 | mf->code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
151 | } else { | ||
152 | /* crop size */ | ||
153 | mf->width = cur_fmt.width; | ||
154 | mf->height = cur_fmt.height; | ||
155 | mf->code = V4L2_MBUS_FMT_YUV10_1X30; | ||
156 | } | ||
157 | |||
158 | mutex_unlock(&isp->subdev_lock); | ||
159 | |||
160 | v4l2_dbg(1, debug, sd, "%s: pad%d: fmt: 0x%x, %dx%d\n", | ||
161 | __func__, fmt->pad, mf->code, mf->width, mf->height); | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static void __isp_subdev_try_format(struct fimc_isp *isp, | ||
167 | struct v4l2_subdev_format *fmt) | ||
168 | { | ||
169 | struct v4l2_mbus_framefmt *mf = &fmt->format; | ||
170 | |||
171 | if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { | ||
172 | v4l_bound_align_image(&mf->width, FIMC_ISP_SINK_WIDTH_MIN, | ||
173 | FIMC_ISP_SINK_WIDTH_MAX, 0, | ||
174 | &mf->height, FIMC_ISP_SINK_HEIGHT_MIN, | ||
175 | FIMC_ISP_SINK_HEIGHT_MAX, 0, 0); | ||
176 | isp->subdev_fmt = *mf; | ||
177 | } else { | ||
178 | /* Allow changing format only on sink pad */ | ||
179 | mf->width = isp->subdev_fmt.width - FIMC_ISP_CAC_MARGIN_WIDTH; | ||
180 | mf->height = isp->subdev_fmt.height - FIMC_ISP_CAC_MARGIN_HEIGHT; | ||
181 | mf->code = isp->subdev_fmt.code; | ||
182 | } | ||
183 | } | ||
184 | |||
185 | static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, | ||
186 | struct v4l2_subdev_fh *fh, | ||
187 | struct v4l2_subdev_format *fmt) | ||
188 | { | ||
189 | struct fimc_isp *isp = v4l2_get_subdevdata(sd); | ||
190 | struct fimc_is *is = fimc_isp_to_is(isp); | ||
191 | struct v4l2_mbus_framefmt *mf = &fmt->format; | ||
192 | int ret = 0; | ||
193 | |||
194 | v4l2_dbg(1, debug, sd, "%s: pad%d: code: 0x%x, %dx%d\n", | ||
195 | __func__, fmt->pad, mf->code, mf->width, mf->height); | ||
196 | |||
197 | mf->colorspace = V4L2_COLORSPACE_JPEG; | ||
198 | |||
199 | mutex_lock(&isp->subdev_lock); | ||
200 | __isp_subdev_try_format(isp, fmt); | ||
201 | |||
202 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
203 | mf = v4l2_subdev_get_try_format(fh, fmt->pad); | ||
204 | *mf = fmt->format; | ||
205 | mutex_unlock(&isp->subdev_lock); | ||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | if (sd->entity.stream_count == 0) | ||
210 | __is_set_frame_size(is, mf); | ||
211 | else | ||
212 | ret = -EBUSY; | ||
213 | mutex_unlock(&isp->subdev_lock); | ||
214 | |||
215 | return ret; | ||
216 | } | ||
217 | |||
218 | static int fimc_isp_subdev_s_stream(struct v4l2_subdev *sd, int on) | ||
219 | { | ||
220 | struct fimc_isp *isp = v4l2_get_subdevdata(sd); | ||
221 | struct fimc_is *is = fimc_isp_to_is(isp); | ||
222 | int ret; | ||
223 | |||
224 | v4l2_dbg(1, debug, sd, "%s: on: %d\n", __func__, on); | ||
225 | |||
226 | if (!test_bit(IS_ST_INIT_DONE, &is->state)) | ||
227 | return -EBUSY; | ||
228 | |||
229 | fimc_is_mem_barrier(); | ||
230 | |||
231 | if (on) { | ||
232 | if (__get_pending_param_count(is)) { | ||
233 | ret = fimc_is_itf_s_param(is, true); | ||
234 | if (ret < 0) | ||
235 | return ret; | ||
236 | } | ||
237 | |||
238 | v4l2_dbg(1, debug, sd, "changing mode to %d\n", | ||
239 | is->config_index); | ||
240 | ret = fimc_is_itf_mode_change(is); | ||
241 | if (ret) | ||
242 | return -EINVAL; | ||
243 | |||
244 | clear_bit(IS_ST_STREAM_ON, &is->state); | ||
245 | fimc_is_hw_stream_on(is); | ||
246 | ret = fimc_is_wait_event(is, IS_ST_STREAM_ON, 1, | ||
247 | FIMC_IS_CONFIG_TIMEOUT); | ||
248 | if (ret < 0) { | ||
249 | v4l2_err(sd, "stream on timeout\n"); | ||
250 | return ret; | ||
251 | } | ||
252 | } else { | ||
253 | clear_bit(IS_ST_STREAM_OFF, &is->state); | ||
254 | fimc_is_hw_stream_off(is); | ||
255 | ret = fimc_is_wait_event(is, IS_ST_STREAM_OFF, 1, | ||
256 | FIMC_IS_CONFIG_TIMEOUT); | ||
257 | if (ret < 0) { | ||
258 | v4l2_err(sd, "stream off timeout\n"); | ||
259 | return ret; | ||
260 | } | ||
261 | is->setfile.sub_index = 0; | ||
262 | } | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static int fimc_isp_subdev_s_power(struct v4l2_subdev *sd, int on) | ||
268 | { | ||
269 | struct fimc_isp *isp = v4l2_get_subdevdata(sd); | ||
270 | struct fimc_is *is = fimc_isp_to_is(isp); | ||
271 | int ret = 0; | ||
272 | |||
273 | pr_debug("on: %d\n", on); | ||
274 | |||
275 | if (on) { | ||
276 | ret = pm_runtime_get_sync(&is->pdev->dev); | ||
277 | if (ret < 0) | ||
278 | return ret; | ||
279 | set_bit(IS_ST_PWR_ON, &is->state); | ||
280 | |||
281 | ret = fimc_is_start_firmware(is); | ||
282 | if (ret < 0) { | ||
283 | v4l2_err(sd, "firmware booting failed\n"); | ||
284 | pm_runtime_put(&is->pdev->dev); | ||
285 | return ret; | ||
286 | } | ||
287 | set_bit(IS_ST_PWR_SUBIP_ON, &is->state); | ||
288 | |||
289 | ret = fimc_is_hw_initialize(is); | ||
290 | } else { | ||
291 | /* Close sensor */ | ||
292 | if (!test_bit(IS_ST_PWR_ON, &is->state)) { | ||
293 | fimc_is_hw_close_sensor(is, 0); | ||
294 | |||
295 | ret = fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 0, | ||
296 | FIMC_IS_CONFIG_TIMEOUT); | ||
297 | if (ret < 0) { | ||
298 | v4l2_err(sd, "sensor close timeout\n"); | ||
299 | return ret; | ||
300 | } | ||
301 | } | ||
302 | |||
303 | /* SUB IP power off */ | ||
304 | if (test_bit(IS_ST_PWR_SUBIP_ON, &is->state)) { | ||
305 | fimc_is_hw_subip_power_off(is); | ||
306 | ret = fimc_is_wait_event(is, IS_ST_PWR_SUBIP_ON, 0, | ||
307 | FIMC_IS_CONFIG_TIMEOUT); | ||
308 | if (ret < 0) { | ||
309 | v4l2_err(sd, "sub-IP power off timeout\n"); | ||
310 | return ret; | ||
311 | } | ||
312 | } | ||
313 | |||
314 | fimc_is_cpu_set_power(is, 0); | ||
315 | pm_runtime_put_sync(&is->pdev->dev); | ||
316 | |||
317 | clear_bit(IS_ST_PWR_ON, &is->state); | ||
318 | clear_bit(IS_ST_INIT_DONE, &is->state); | ||
319 | is->state = 0; | ||
320 | is->config[is->config_index].p_region_index1 = 0; | ||
321 | is->config[is->config_index].p_region_index2 = 0; | ||
322 | set_bit(IS_ST_IDLE, &is->state); | ||
323 | wmb(); | ||
324 | } | ||
325 | |||
326 | return ret; | ||
327 | } | ||
328 | |||
329 | static int fimc_isp_subdev_open(struct v4l2_subdev *sd, | ||
330 | struct v4l2_subdev_fh *fh) | ||
331 | { | ||
332 | struct v4l2_mbus_framefmt fmt; | ||
333 | struct v4l2_mbus_framefmt *format; | ||
334 | |||
335 | format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SINK); | ||
336 | |||
337 | fmt.colorspace = V4L2_COLORSPACE_SRGB; | ||
338 | fmt.code = fimc_isp_formats[0].mbus_code; | ||
339 | fmt.width = DEFAULT_PREVIEW_STILL_WIDTH + FIMC_ISP_CAC_MARGIN_WIDTH; | ||
340 | fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT + FIMC_ISP_CAC_MARGIN_HEIGHT; | ||
341 | fmt.field = V4L2_FIELD_NONE; | ||
342 | *format = fmt; | ||
343 | |||
344 | format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SRC_FIFO); | ||
345 | fmt.width = DEFAULT_PREVIEW_STILL_WIDTH; | ||
346 | fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT; | ||
347 | *format = fmt; | ||
348 | |||
349 | format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SRC_DMA); | ||
350 | *format = fmt; | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static const struct v4l2_subdev_internal_ops fimc_is_subdev_internal_ops = { | ||
356 | .open = fimc_isp_subdev_open, | ||
357 | }; | ||
358 | |||
359 | static const struct v4l2_subdev_pad_ops fimc_is_subdev_pad_ops = { | ||
360 | .enum_mbus_code = fimc_is_subdev_enum_mbus_code, | ||
361 | .get_fmt = fimc_isp_subdev_get_fmt, | ||
362 | .set_fmt = fimc_isp_subdev_set_fmt, | ||
363 | }; | ||
364 | |||
365 | static const struct v4l2_subdev_video_ops fimc_is_subdev_video_ops = { | ||
366 | .s_stream = fimc_isp_subdev_s_stream, | ||
367 | }; | ||
368 | |||
369 | static const struct v4l2_subdev_core_ops fimc_is_core_ops = { | ||
370 | .s_power = fimc_isp_subdev_s_power, | ||
371 | }; | ||
372 | |||
373 | static struct v4l2_subdev_ops fimc_is_subdev_ops = { | ||
374 | .core = &fimc_is_core_ops, | ||
375 | .video = &fimc_is_subdev_video_ops, | ||
376 | .pad = &fimc_is_subdev_pad_ops, | ||
377 | }; | ||
378 | |||
379 | static int __ctrl_set_white_balance(struct fimc_is *is, int value) | ||
380 | { | ||
381 | switch (value) { | ||
382 | case V4L2_WHITE_BALANCE_AUTO: | ||
383 | __is_set_isp_awb(is, ISP_AWB_COMMAND_AUTO, 0); | ||
384 | break; | ||
385 | case V4L2_WHITE_BALANCE_DAYLIGHT: | ||
386 | __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, | ||
387 | ISP_AWB_ILLUMINATION_DAYLIGHT); | ||
388 | break; | ||
389 | case V4L2_WHITE_BALANCE_CLOUDY: | ||
390 | __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, | ||
391 | ISP_AWB_ILLUMINATION_CLOUDY); | ||
392 | break; | ||
393 | case V4L2_WHITE_BALANCE_INCANDESCENT: | ||
394 | __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, | ||
395 | ISP_AWB_ILLUMINATION_TUNGSTEN); | ||
396 | break; | ||
397 | case V4L2_WHITE_BALANCE_FLUORESCENT: | ||
398 | __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, | ||
399 | ISP_AWB_ILLUMINATION_FLUORESCENT); | ||
400 | break; | ||
401 | default: | ||
402 | return -EINVAL; | ||
403 | } | ||
404 | |||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static int __ctrl_set_aewb_lock(struct fimc_is *is, | ||
409 | struct v4l2_ctrl *ctrl) | ||
410 | { | ||
411 | bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; | ||
412 | bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; | ||
413 | struct isp_param *isp = &is->is_p_region->parameter.isp; | ||
414 | int cmd, ret; | ||
415 | |||
416 | cmd = ae_lock ? ISP_AA_COMMAND_STOP : ISP_AA_COMMAND_START; | ||
417 | isp->aa.cmd = cmd; | ||
418 | isp->aa.target = ISP_AA_TARGET_AE; | ||
419 | fimc_is_set_param_bit(is, PARAM_ISP_AA); | ||
420 | is->af.ae_lock_state = ae_lock; | ||
421 | wmb(); | ||
422 | |||
423 | ret = fimc_is_itf_s_param(is, false); | ||
424 | if (ret < 0) | ||
425 | return ret; | ||
426 | |||
427 | cmd = awb_lock ? ISP_AA_COMMAND_STOP : ISP_AA_COMMAND_START; | ||
428 | isp->aa.cmd = cmd; | ||
429 | isp->aa.target = ISP_AA_TARGET_AE; | ||
430 | fimc_is_set_param_bit(is, PARAM_ISP_AA); | ||
431 | is->af.awb_lock_state = awb_lock; | ||
432 | wmb(); | ||
433 | |||
434 | return fimc_is_itf_s_param(is, false); | ||
435 | } | ||
436 | |||
437 | /* Supported manual ISO values */ | ||
438 | static const s64 iso_qmenu[] = { | ||
439 | 50, 100, 200, 400, 800, | ||
440 | }; | ||
441 | |||
442 | static int __ctrl_set_iso(struct fimc_is *is, int value) | ||
443 | { | ||
444 | unsigned int idx, iso; | ||
445 | |||
446 | if (value == V4L2_ISO_SENSITIVITY_AUTO) { | ||
447 | __is_set_isp_iso(is, ISP_ISO_COMMAND_AUTO, 0); | ||
448 | return 0; | ||
449 | } | ||
450 | idx = is->isp.ctrls.iso->val; | ||
451 | if (idx >= ARRAY_SIZE(iso_qmenu)) | ||
452 | return -EINVAL; | ||
453 | |||
454 | iso = iso_qmenu[idx]; | ||
455 | __is_set_isp_iso(is, ISP_ISO_COMMAND_MANUAL, iso); | ||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | static int __ctrl_set_metering(struct fimc_is *is, unsigned int value) | ||
460 | { | ||
461 | unsigned int val; | ||
462 | |||
463 | switch (value) { | ||
464 | case V4L2_EXPOSURE_METERING_AVERAGE: | ||
465 | val = ISP_METERING_COMMAND_AVERAGE; | ||
466 | break; | ||
467 | case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: | ||
468 | val = ISP_METERING_COMMAND_CENTER; | ||
469 | break; | ||
470 | case V4L2_EXPOSURE_METERING_SPOT: | ||
471 | val = ISP_METERING_COMMAND_SPOT; | ||
472 | break; | ||
473 | case V4L2_EXPOSURE_METERING_MATRIX: | ||
474 | val = ISP_METERING_COMMAND_MATRIX; | ||
475 | break; | ||
476 | default: | ||
477 | return -EINVAL; | ||
478 | }; | ||
479 | |||
480 | __is_set_isp_metering(is, IS_METERING_CONFIG_CMD, val); | ||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | static int __ctrl_set_afc(struct fimc_is *is, int value) | ||
485 | { | ||
486 | switch (value) { | ||
487 | case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: | ||
488 | __is_set_isp_afc(is, ISP_AFC_COMMAND_DISABLE, 0); | ||
489 | break; | ||
490 | case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: | ||
491 | __is_set_isp_afc(is, ISP_AFC_COMMAND_MANUAL, 50); | ||
492 | break; | ||
493 | case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: | ||
494 | __is_set_isp_afc(is, ISP_AFC_COMMAND_MANUAL, 60); | ||
495 | break; | ||
496 | case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: | ||
497 | __is_set_isp_afc(is, ISP_AFC_COMMAND_AUTO, 0); | ||
498 | break; | ||
499 | default: | ||
500 | return -EINVAL; | ||
501 | } | ||
502 | |||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static int __ctrl_set_image_effect(struct fimc_is *is, int value) | ||
507 | { | ||
508 | static const u8 effects[][2] = { | ||
509 | { V4L2_COLORFX_NONE, ISP_IMAGE_EFFECT_DISABLE }, | ||
510 | { V4L2_COLORFX_BW, ISP_IMAGE_EFFECT_MONOCHROME }, | ||
511 | { V4L2_COLORFX_SEPIA, ISP_IMAGE_EFFECT_SEPIA }, | ||
512 | { V4L2_COLORFX_NEGATIVE, ISP_IMAGE_EFFECT_NEGATIVE_MONO }, | ||
513 | { 16 /* TODO */, ISP_IMAGE_EFFECT_NEGATIVE_COLOR }, | ||
514 | }; | ||
515 | int i; | ||
516 | |||
517 | for (i = 0; i < ARRAY_SIZE(effects); i++) { | ||
518 | if (effects[i][0] != value) | ||
519 | continue; | ||
520 | |||
521 | __is_set_isp_effect(is, effects[i][1]); | ||
522 | return 0; | ||
523 | } | ||
524 | |||
525 | return -EINVAL; | ||
526 | } | ||
527 | |||
528 | static int fimc_is_s_ctrl(struct v4l2_ctrl *ctrl) | ||
529 | { | ||
530 | struct fimc_isp *isp = ctrl_to_fimc_isp(ctrl); | ||
531 | struct fimc_is *is = fimc_isp_to_is(isp); | ||
532 | bool set_param = true; | ||
533 | int ret = 0; | ||
534 | |||
535 | switch (ctrl->id) { | ||
536 | case V4L2_CID_CONTRAST: | ||
537 | __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_CONTRAST, | ||
538 | ctrl->val); | ||
539 | break; | ||
540 | |||
541 | case V4L2_CID_SATURATION: | ||
542 | __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SATURATION, | ||
543 | ctrl->val); | ||
544 | break; | ||
545 | |||
546 | case V4L2_CID_SHARPNESS: | ||
547 | __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SHARPNESS, | ||
548 | ctrl->val); | ||
549 | break; | ||
550 | |||
551 | case V4L2_CID_EXPOSURE_ABSOLUTE: | ||
552 | __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_EXPOSURE, | ||
553 | ctrl->val); | ||
554 | break; | ||
555 | |||
556 | case V4L2_CID_BRIGHTNESS: | ||
557 | __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS, | ||
558 | ctrl->val); | ||
559 | break; | ||
560 | |||
561 | case V4L2_CID_HUE: | ||
562 | __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_HUE, | ||
563 | ctrl->val); | ||
564 | break; | ||
565 | |||
566 | case V4L2_CID_EXPOSURE_METERING: | ||
567 | ret = __ctrl_set_metering(is, ctrl->val); | ||
568 | break; | ||
569 | |||
570 | case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: | ||
571 | ret = __ctrl_set_white_balance(is, ctrl->val); | ||
572 | break; | ||
573 | |||
574 | case V4L2_CID_3A_LOCK: | ||
575 | ret = __ctrl_set_aewb_lock(is, ctrl); | ||
576 | set_param = false; | ||
577 | break; | ||
578 | |||
579 | case V4L2_CID_ISO_SENSITIVITY_AUTO: | ||
580 | ret = __ctrl_set_iso(is, ctrl->val); | ||
581 | break; | ||
582 | |||
583 | case V4L2_CID_POWER_LINE_FREQUENCY: | ||
584 | ret = __ctrl_set_afc(is, ctrl->val); | ||
585 | break; | ||
586 | |||
587 | case V4L2_CID_COLORFX: | ||
588 | __ctrl_set_image_effect(is, ctrl->val); | ||
589 | break; | ||
590 | |||
591 | default: | ||
592 | ret = -EINVAL; | ||
593 | break; | ||
594 | } | ||
595 | |||
596 | if (ret < 0) { | ||
597 | v4l2_err(&isp->subdev, "Failed to set control: %s (%d)\n", | ||
598 | ctrl->name, ctrl->val); | ||
599 | return ret; | ||
600 | } | ||
601 | |||
602 | if (set_param && test_bit(IS_ST_STREAM_ON, &is->state)) | ||
603 | return fimc_is_itf_s_param(is, true); | ||
604 | |||
605 | return 0; | ||
606 | } | ||
607 | |||
608 | static const struct v4l2_ctrl_ops fimc_isp_ctrl_ops = { | ||
609 | .s_ctrl = fimc_is_s_ctrl, | ||
610 | }; | ||
611 | |||
612 | int fimc_isp_subdev_create(struct fimc_isp *isp) | ||
613 | { | ||
614 | const struct v4l2_ctrl_ops *ops = &fimc_isp_ctrl_ops; | ||
615 | struct v4l2_ctrl_handler *handler = &isp->ctrls.handler; | ||
616 | struct v4l2_subdev *sd = &isp->subdev; | ||
617 | struct fimc_isp_ctrls *ctrls = &isp->ctrls; | ||
618 | int ret; | ||
619 | |||
620 | mutex_init(&isp->subdev_lock); | ||
621 | |||
622 | v4l2_subdev_init(sd, &fimc_is_subdev_ops); | ||
623 | sd->grp_id = GRP_ID_FIMC_IS; | ||
624 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
625 | snprintf(sd->name, sizeof(sd->name), "FIMC-IS-ISP"); | ||
626 | |||
627 | isp->subdev_pads[FIMC_ISP_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
628 | isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_FIFO].flags = MEDIA_PAD_FL_SOURCE; | ||
629 | isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_DMA].flags = MEDIA_PAD_FL_SOURCE; | ||
630 | ret = media_entity_init(&sd->entity, FIMC_ISP_SD_PADS_NUM, | ||
631 | isp->subdev_pads, 0); | ||
632 | if (ret) | ||
633 | return ret; | ||
634 | |||
635 | v4l2_ctrl_handler_init(handler, 20); | ||
636 | |||
637 | ctrls->saturation = v4l2_ctrl_new_std(handler, ops, V4L2_CID_SATURATION, | ||
638 | -2, 2, 1, 0); | ||
639 | ctrls->brightness = v4l2_ctrl_new_std(handler, ops, V4L2_CID_BRIGHTNESS, | ||
640 | -4, 4, 1, 0); | ||
641 | ctrls->contrast = v4l2_ctrl_new_std(handler, ops, V4L2_CID_CONTRAST, | ||
642 | -2, 2, 1, 0); | ||
643 | ctrls->sharpness = v4l2_ctrl_new_std(handler, ops, V4L2_CID_SHARPNESS, | ||
644 | -2, 2, 1, 0); | ||
645 | ctrls->hue = v4l2_ctrl_new_std(handler, ops, V4L2_CID_HUE, | ||
646 | -2, 2, 1, 0); | ||
647 | |||
648 | ctrls->auto_wb = v4l2_ctrl_new_std_menu(handler, ops, | ||
649 | V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, | ||
650 | 8, ~0x14e, V4L2_WHITE_BALANCE_AUTO); | ||
651 | |||
652 | ctrls->exposure = v4l2_ctrl_new_std(handler, ops, | ||
653 | V4L2_CID_EXPOSURE_ABSOLUTE, | ||
654 | -4, 4, 1, 0); | ||
655 | |||
656 | ctrls->exp_metering = v4l2_ctrl_new_std_menu(handler, ops, | ||
657 | V4L2_CID_EXPOSURE_METERING, 3, | ||
658 | ~0xf, V4L2_EXPOSURE_METERING_AVERAGE); | ||
659 | |||
660 | v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_POWER_LINE_FREQUENCY, | ||
661 | V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, | ||
662 | V4L2_CID_POWER_LINE_FREQUENCY_AUTO); | ||
663 | /* ISO sensitivity */ | ||
664 | ctrls->auto_iso = v4l2_ctrl_new_std_menu(handler, ops, | ||
665 | V4L2_CID_ISO_SENSITIVITY_AUTO, 1, 0, | ||
666 | V4L2_ISO_SENSITIVITY_AUTO); | ||
667 | |||
668 | ctrls->iso = v4l2_ctrl_new_int_menu(handler, ops, | ||
669 | V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, | ||
670 | ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); | ||
671 | |||
672 | ctrls->aewb_lock = v4l2_ctrl_new_std(handler, ops, | ||
673 | V4L2_CID_3A_LOCK, 0, 0x3, 0, 0); | ||
674 | |||
675 | /* TODO: Add support for NEGATIVE_COLOR option */ | ||
676 | ctrls->colorfx = v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_COLORFX, | ||
677 | V4L2_COLORFX_SET_CBCR + 1, ~0x1000f, V4L2_COLORFX_NONE); | ||
678 | |||
679 | if (handler->error) { | ||
680 | media_entity_cleanup(&sd->entity); | ||
681 | return handler->error; | ||
682 | } | ||
683 | |||
684 | v4l2_ctrl_auto_cluster(2, &ctrls->auto_iso, | ||
685 | V4L2_ISO_SENSITIVITY_MANUAL, false); | ||
686 | |||
687 | sd->ctrl_handler = handler; | ||
688 | sd->internal_ops = &fimc_is_subdev_internal_ops; | ||
689 | sd->entity.ops = &fimc_is_subdev_media_ops; | ||
690 | v4l2_set_subdevdata(sd, isp); | ||
691 | |||
692 | return 0; | ||
693 | } | ||
694 | |||
695 | void fimc_isp_subdev_destroy(struct fimc_isp *isp) | ||
696 | { | ||
697 | struct v4l2_subdev *sd = &isp->subdev; | ||
698 | |||
699 | v4l2_device_unregister_subdev(sd); | ||
700 | media_entity_cleanup(&sd->entity); | ||
701 | v4l2_ctrl_handler_free(&isp->ctrls.handler); | ||
702 | v4l2_set_subdevdata(sd, NULL); | ||
703 | } | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h new file mode 100644 index 000000000000..800aba7ab4a7 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-isp.h | |||
@@ -0,0 +1,181 @@ | |||
1 | /* | ||
2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
7 | * Younghwan Joo <yhwan.joo@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #ifndef FIMC_ISP_H_ | ||
14 | #define FIMC_ISP_H_ | ||
15 | |||
16 | #include <linux/io.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/sched.h> | ||
19 | #include <linux/spinlock.h> | ||
20 | #include <linux/types.h> | ||
21 | #include <linux/videodev2.h> | ||
22 | |||
23 | #include <media/media-entity.h> | ||
24 | #include <media/videobuf2-core.h> | ||
25 | #include <media/v4l2-device.h> | ||
26 | #include <media/v4l2-mediabus.h> | ||
27 | #include <media/s5p_fimc.h> | ||
28 | |||
29 | /* FIXME: revisit these constraints */ | ||
30 | #define FIMC_ISP_SINK_WIDTH_MIN (16 + 8) | ||
31 | #define FIMC_ISP_SINK_HEIGHT_MIN (12 + 8) | ||
32 | #define FIMC_ISP_SOURCE_WIDTH_MIN 8 | ||
33 | #define FIMC_ISP_SOURC_HEIGHT_MIN 8 | ||
34 | #define FIMC_ISP_CAC_MARGIN_WIDTH 16 | ||
35 | #define FIMC_ISP_CAC_MARGIN_HEIGHT 12 | ||
36 | |||
37 | #define FIMC_ISP_SINK_WIDTH_MAX (4000 - 16) | ||
38 | #define FIMC_ISP_SINK_HEIGHT_MAX (4000 + 12) | ||
39 | #define FIMC_ISP_SOURCE_WIDTH_MAX 4000 | ||
40 | #define FIMC_ISP_SOURC_HEIGHT_MAX 4000 | ||
41 | |||
42 | #define FIMC_ISP_NUM_FORMATS 3 | ||
43 | #define FIMC_ISP_REQ_BUFS_MIN 2 | ||
44 | |||
45 | #define FIMC_ISP_SD_PAD_SINK 0 | ||
46 | #define FIMC_ISP_SD_PAD_SRC_FIFO 1 | ||
47 | #define FIMC_ISP_SD_PAD_SRC_DMA 2 | ||
48 | #define FIMC_ISP_SD_PADS_NUM 3 | ||
49 | #define FIMC_ISP_MAX_PLANES 1 | ||
50 | |||
51 | /** | ||
52 | * struct fimc_isp_frame - source/target frame properties | ||
53 | * @width: full image width | ||
54 | * @height: full image height | ||
55 | * @rect: crop/composition rectangle | ||
56 | */ | ||
57 | struct fimc_isp_frame { | ||
58 | u16 width; | ||
59 | u16 height; | ||
60 | struct v4l2_rect rect; | ||
61 | }; | ||
62 | |||
63 | struct fimc_isp_ctrls { | ||
64 | struct v4l2_ctrl_handler handler; | ||
65 | |||
66 | /* Auto white balance */ | ||
67 | struct v4l2_ctrl *auto_wb; | ||
68 | /* Auto ISO control cluster */ | ||
69 | struct { | ||
70 | struct v4l2_ctrl *auto_iso; | ||
71 | struct v4l2_ctrl *iso; | ||
72 | }; | ||
73 | /* Adjust - contrast */ | ||
74 | struct v4l2_ctrl *contrast; | ||
75 | /* Adjust - saturation */ | ||
76 | struct v4l2_ctrl *saturation; | ||
77 | /* Adjust - sharpness */ | ||
78 | struct v4l2_ctrl *sharpness; | ||
79 | /* Adjust - brightness */ | ||
80 | struct v4l2_ctrl *brightness; | ||
81 | /* Adjust - hue */ | ||
82 | struct v4l2_ctrl *hue; | ||
83 | |||
84 | /* Auto/manual exposure */ | ||
85 | struct v4l2_ctrl *auto_exp; | ||
86 | /* Manual exposure value */ | ||
87 | struct v4l2_ctrl *exposure; | ||
88 | /* AE/AWB lock/unlock */ | ||
89 | struct v4l2_ctrl *aewb_lock; | ||
90 | /* Exposure metering mode */ | ||
91 | struct v4l2_ctrl *exp_metering; | ||
92 | /* AFC */ | ||
93 | struct v4l2_ctrl *afc; | ||
94 | /* ISP image effect */ | ||
95 | struct v4l2_ctrl *colorfx; | ||
96 | }; | ||
97 | |||
98 | /** | ||
99 | * struct fimc_is_video - fimc-is video device structure | ||
100 | * @vdev: video_device structure | ||
101 | * @type: video device type (CAPTURE/OUTPUT) | ||
102 | * @pad: video device media (sink) pad | ||
103 | * @pending_buf_q: pending buffers queue head | ||
104 | * @active_buf_q: a queue head of buffers scheduled in hardware | ||
105 | * @vb_queue: vb2 buffer queue | ||
106 | * @active_buf_count: number of video buffers scheduled in hardware | ||
107 | * @frame_count: counter of frames dequeued to user space | ||
108 | * @reqbufs_count: number of buffers requested with REQBUFS ioctl | ||
109 | * @format: current pixel format | ||
110 | */ | ||
111 | struct fimc_is_video { | ||
112 | struct video_device vdev; | ||
113 | enum v4l2_buf_type type; | ||
114 | struct media_pad pad; | ||
115 | struct list_head pending_buf_q; | ||
116 | struct list_head active_buf_q; | ||
117 | struct vb2_queue vb_queue; | ||
118 | unsigned int frame_count; | ||
119 | unsigned int reqbufs_count; | ||
120 | int streaming; | ||
121 | unsigned long payload[FIMC_ISP_MAX_PLANES]; | ||
122 | const struct fimc_fmt *format; | ||
123 | }; | ||
124 | |||
125 | /** | ||
126 | * struct fimc_isp - FIMC-IS ISP data structure | ||
127 | * @pdev: pointer to FIMC-IS platform device | ||
128 | * @alloc_ctx: videobuf2 memory allocator context | ||
129 | * @subdev: ISP v4l2_subdev | ||
130 | * @subdev_pads: the ISP subdev media pads | ||
131 | * @ctrl_handler: v4l2 controls handler | ||
132 | * @test_pattern: test pattern controls | ||
133 | * @pipeline: video capture pipeline data structure | ||
134 | * @video_lock: mutex serializing video device and the subdev operations | ||
135 | * @fmt: pointer to color format description structure | ||
136 | * @payload: image size in bytes (w x h x bpp) | ||
137 | * @inp_frame: camera input frame structure | ||
138 | * @out_frame: DMA output frame structure | ||
139 | * @source_subdev_grp_id: group id of remote source subdev | ||
140 | * @cac_margin_x: horizontal CAC margin in pixels | ||
141 | * @cac_margin_y: vertical CAC margin in pixels | ||
142 | * @state: driver state flags | ||
143 | * @video_capture: the ISP block video capture device | ||
144 | */ | ||
145 | struct fimc_isp { | ||
146 | struct platform_device *pdev; | ||
147 | struct vb2_alloc_ctx *alloc_ctx; | ||
148 | struct v4l2_subdev subdev; | ||
149 | struct media_pad subdev_pads[FIMC_ISP_SD_PADS_NUM]; | ||
150 | struct v4l2_mbus_framefmt subdev_fmt; | ||
151 | struct v4l2_ctrl *test_pattern; | ||
152 | struct fimc_isp_ctrls ctrls; | ||
153 | |||
154 | struct mutex video_lock; | ||
155 | struct mutex subdev_lock; | ||
156 | |||
157 | struct fimc_isp_frame inp_frame; | ||
158 | struct fimc_isp_frame out_frame; | ||
159 | unsigned int source_subdev_grp_id; | ||
160 | |||
161 | unsigned int cac_margin_x; | ||
162 | unsigned int cac_margin_y; | ||
163 | |||
164 | unsigned long state; | ||
165 | |||
166 | struct fimc_is_video video_capture; | ||
167 | }; | ||
168 | |||
169 | #define ctrl_to_fimc_isp(_ctrl) \ | ||
170 | container_of(ctrl->handler, struct fimc_isp, ctrls.handler) | ||
171 | |||
172 | struct fimc_is; | ||
173 | |||
174 | int fimc_isp_subdev_create(struct fimc_isp *isp); | ||
175 | void fimc_isp_subdev_destroy(struct fimc_isp *isp); | ||
176 | void fimc_isp_irq_handler(struct fimc_is *is); | ||
177 | int fimc_is_create_controls(struct fimc_isp *isp); | ||
178 | int fimc_is_delete_controls(struct fimc_isp *isp); | ||
179 | const struct fimc_fmt *fimc_isp_find_format(const u32 *pixelformat, | ||
180 | const u32 *mbus_code, int index); | ||
181 | #endif /* FIMC_ISP_H_ */ | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c new file mode 100644 index 000000000000..8cc0d39a2fea --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c | |||
@@ -0,0 +1,302 @@ | |||
1 | /* | ||
2 | * Register interface file for EXYNOS FIMC-LITE (camera interface) driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Samsung Electronics Co., Ltd. | ||
5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/io.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <media/s5p_fimc.h> | ||
15 | |||
16 | #include "fimc-lite-reg.h" | ||
17 | #include "fimc-lite.h" | ||
18 | #include "fimc-core.h" | ||
19 | |||
20 | #define FLITE_RESET_TIMEOUT 50 /* in ms */ | ||
21 | |||
22 | void flite_hw_reset(struct fimc_lite *dev) | ||
23 | { | ||
24 | unsigned long end = jiffies + msecs_to_jiffies(FLITE_RESET_TIMEOUT); | ||
25 | u32 cfg; | ||
26 | |||
27 | cfg = readl(dev->regs + FLITE_REG_CIGCTRL); | ||
28 | cfg |= FLITE_REG_CIGCTRL_SWRST_REQ; | ||
29 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | ||
30 | |||
31 | while (time_is_after_jiffies(end)) { | ||
32 | cfg = readl(dev->regs + FLITE_REG_CIGCTRL); | ||
33 | if (cfg & FLITE_REG_CIGCTRL_SWRST_RDY) | ||
34 | break; | ||
35 | usleep_range(1000, 5000); | ||
36 | } | ||
37 | |||
38 | cfg |= FLITE_REG_CIGCTRL_SWRST; | ||
39 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | ||
40 | } | ||
41 | |||
42 | void flite_hw_clear_pending_irq(struct fimc_lite *dev) | ||
43 | { | ||
44 | u32 cfg = readl(dev->regs + FLITE_REG_CISTATUS); | ||
45 | cfg &= ~FLITE_REG_CISTATUS_IRQ_CAM; | ||
46 | writel(cfg, dev->regs + FLITE_REG_CISTATUS); | ||
47 | } | ||
48 | |||
49 | u32 flite_hw_get_interrupt_source(struct fimc_lite *dev) | ||
50 | { | ||
51 | u32 intsrc = readl(dev->regs + FLITE_REG_CISTATUS); | ||
52 | return intsrc & FLITE_REG_CISTATUS_IRQ_MASK; | ||
53 | } | ||
54 | |||
55 | void flite_hw_clear_last_capture_end(struct fimc_lite *dev) | ||
56 | { | ||
57 | |||
58 | u32 cfg = readl(dev->regs + FLITE_REG_CISTATUS2); | ||
59 | cfg &= ~FLITE_REG_CISTATUS2_LASTCAPEND; | ||
60 | writel(cfg, dev->regs + FLITE_REG_CISTATUS2); | ||
61 | } | ||
62 | |||
63 | void flite_hw_set_interrupt_mask(struct fimc_lite *dev) | ||
64 | { | ||
65 | u32 cfg, intsrc; | ||
66 | |||
67 | /* Select interrupts to be enabled for each output mode */ | ||
68 | if (atomic_read(&dev->out_path) == FIMC_IO_DMA) { | ||
69 | intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN | | ||
70 | FLITE_REG_CIGCTRL_IRQ_LASTEN | | ||
71 | FLITE_REG_CIGCTRL_IRQ_STARTEN; | ||
72 | } else { | ||
73 | /* An output to the FIMC-IS */ | ||
74 | intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN | | ||
75 | FLITE_REG_CIGCTRL_IRQ_LASTEN; | ||
76 | } | ||
77 | |||
78 | cfg = readl(dev->regs + FLITE_REG_CIGCTRL); | ||
79 | cfg |= FLITE_REG_CIGCTRL_IRQ_DISABLE_MASK; | ||
80 | cfg &= ~intsrc; | ||
81 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | ||
82 | } | ||
83 | |||
84 | void flite_hw_capture_start(struct fimc_lite *dev) | ||
85 | { | ||
86 | u32 cfg = readl(dev->regs + FLITE_REG_CIIMGCPT); | ||
87 | cfg |= FLITE_REG_CIIMGCPT_IMGCPTEN; | ||
88 | writel(cfg, dev->regs + FLITE_REG_CIIMGCPT); | ||
89 | } | ||
90 | |||
91 | void flite_hw_capture_stop(struct fimc_lite *dev) | ||
92 | { | ||
93 | u32 cfg = readl(dev->regs + FLITE_REG_CIIMGCPT); | ||
94 | cfg &= ~FLITE_REG_CIIMGCPT_IMGCPTEN; | ||
95 | writel(cfg, dev->regs + FLITE_REG_CIIMGCPT); | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | * Test pattern (color bars) enable/disable. External sensor | ||
100 | * pixel clock must be active for the test pattern to work. | ||
101 | */ | ||
102 | void flite_hw_set_test_pattern(struct fimc_lite *dev, bool on) | ||
103 | { | ||
104 | u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL); | ||
105 | if (on) | ||
106 | cfg |= FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR; | ||
107 | else | ||
108 | cfg &= ~FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR; | ||
109 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | ||
110 | } | ||
111 | |||
112 | static const u32 src_pixfmt_map[8][3] = { | ||
113 | { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCBYCR, | ||
114 | FLITE_REG_CIGCTRL_YUV422_1P }, | ||
115 | { V4L2_MBUS_FMT_YVYU8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCRYCB, | ||
116 | FLITE_REG_CIGCTRL_YUV422_1P }, | ||
117 | { V4L2_MBUS_FMT_UYVY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CBYCRY, | ||
118 | FLITE_REG_CIGCTRL_YUV422_1P }, | ||
119 | { V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CRYCBY, | ||
120 | FLITE_REG_CIGCTRL_YUV422_1P }, | ||
121 | { V4L2_MBUS_FMT_SGRBG8_1X8, 0, FLITE_REG_CIGCTRL_RAW8 }, | ||
122 | { V4L2_MBUS_FMT_SGRBG10_1X10, 0, FLITE_REG_CIGCTRL_RAW10 }, | ||
123 | { V4L2_MBUS_FMT_SGRBG12_1X12, 0, FLITE_REG_CIGCTRL_RAW12 }, | ||
124 | { V4L2_MBUS_FMT_JPEG_1X8, 0, FLITE_REG_CIGCTRL_USER(1) }, | ||
125 | }; | ||
126 | |||
127 | /* Set camera input pixel format and resolution */ | ||
128 | void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f) | ||
129 | { | ||
130 | enum v4l2_mbus_pixelcode pixelcode = f->fmt->mbus_code; | ||
131 | int i = ARRAY_SIZE(src_pixfmt_map); | ||
132 | u32 cfg; | ||
133 | |||
134 | while (--i >= 0) { | ||
135 | if (src_pixfmt_map[i][0] == pixelcode) | ||
136 | break; | ||
137 | } | ||
138 | |||
139 | if (i == 0 && src_pixfmt_map[i][0] != pixelcode) { | ||
140 | v4l2_err(&dev->vfd, | ||
141 | "Unsupported pixel code, falling back to %#08x\n", | ||
142 | src_pixfmt_map[i][0]); | ||
143 | } | ||
144 | |||
145 | cfg = readl(dev->regs + FLITE_REG_CIGCTRL); | ||
146 | cfg &= ~FLITE_REG_CIGCTRL_FMT_MASK; | ||
147 | cfg |= src_pixfmt_map[i][2]; | ||
148 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | ||
149 | |||
150 | cfg = readl(dev->regs + FLITE_REG_CISRCSIZE); | ||
151 | cfg &= ~(FLITE_REG_CISRCSIZE_ORDER422_MASK | | ||
152 | FLITE_REG_CISRCSIZE_SIZE_CAM_MASK); | ||
153 | cfg |= (f->f_width << 16) | f->f_height; | ||
154 | cfg |= src_pixfmt_map[i][1]; | ||
155 | writel(cfg, dev->regs + FLITE_REG_CISRCSIZE); | ||
156 | } | ||
157 | |||
158 | /* Set the camera host input window offsets (cropping) */ | ||
159 | void flite_hw_set_window_offset(struct fimc_lite *dev, struct flite_frame *f) | ||
160 | { | ||
161 | u32 hoff2, voff2; | ||
162 | u32 cfg; | ||
163 | |||
164 | cfg = readl(dev->regs + FLITE_REG_CIWDOFST); | ||
165 | cfg &= ~FLITE_REG_CIWDOFST_OFST_MASK; | ||
166 | cfg |= (f->rect.left << 16) | f->rect.top; | ||
167 | cfg |= FLITE_REG_CIWDOFST_WINOFSEN; | ||
168 | writel(cfg, dev->regs + FLITE_REG_CIWDOFST); | ||
169 | |||
170 | hoff2 = f->f_width - f->rect.width - f->rect.left; | ||
171 | voff2 = f->f_height - f->rect.height - f->rect.top; | ||
172 | |||
173 | cfg = (hoff2 << 16) | voff2; | ||
174 | writel(cfg, dev->regs + FLITE_REG_CIWDOFST2); | ||
175 | } | ||
176 | |||
177 | /* Select camera port (A, B) */ | ||
178 | static void flite_hw_set_camera_port(struct fimc_lite *dev, int id) | ||
179 | { | ||
180 | u32 cfg = readl(dev->regs + FLITE_REG_CIGENERAL); | ||
181 | if (id == 0) | ||
182 | cfg &= ~FLITE_REG_CIGENERAL_CAM_B; | ||
183 | else | ||
184 | cfg |= FLITE_REG_CIGENERAL_CAM_B; | ||
185 | writel(cfg, dev->regs + FLITE_REG_CIGENERAL); | ||
186 | } | ||
187 | |||
188 | /* Select serial or parallel bus, camera port (A,B) and set signals polarity */ | ||
189 | void flite_hw_set_camera_bus(struct fimc_lite *dev, | ||
190 | struct fimc_source_info *si) | ||
191 | { | ||
192 | u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL); | ||
193 | unsigned int flags = si->flags; | ||
194 | |||
195 | if (si->sensor_bus_type != FIMC_BUS_TYPE_MIPI_CSI2) { | ||
196 | cfg &= ~(FLITE_REG_CIGCTRL_SELCAM_MIPI | | ||
197 | FLITE_REG_CIGCTRL_INVPOLPCLK | | ||
198 | FLITE_REG_CIGCTRL_INVPOLVSYNC | | ||
199 | FLITE_REG_CIGCTRL_INVPOLHREF); | ||
200 | |||
201 | if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) | ||
202 | cfg |= FLITE_REG_CIGCTRL_INVPOLPCLK; | ||
203 | |||
204 | if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) | ||
205 | cfg |= FLITE_REG_CIGCTRL_INVPOLVSYNC; | ||
206 | |||
207 | if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) | ||
208 | cfg |= FLITE_REG_CIGCTRL_INVPOLHREF; | ||
209 | } else { | ||
210 | cfg |= FLITE_REG_CIGCTRL_SELCAM_MIPI; | ||
211 | } | ||
212 | |||
213 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | ||
214 | |||
215 | flite_hw_set_camera_port(dev, si->mux_id); | ||
216 | } | ||
217 | |||
218 | static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f) | ||
219 | { | ||
220 | static const u32 pixcode[4][2] = { | ||
221 | { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CIODMAFMT_YCBYCR }, | ||
222 | { V4L2_MBUS_FMT_YVYU8_2X8, FLITE_REG_CIODMAFMT_YCRYCB }, | ||
223 | { V4L2_MBUS_FMT_UYVY8_2X8, FLITE_REG_CIODMAFMT_CBYCRY }, | ||
224 | { V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CIODMAFMT_CRYCBY }, | ||
225 | }; | ||
226 | u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT); | ||
227 | int i = ARRAY_SIZE(pixcode); | ||
228 | |||
229 | while (--i >= 0) | ||
230 | if (pixcode[i][0] == f->fmt->mbus_code) | ||
231 | break; | ||
232 | cfg &= ~FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK; | ||
233 | writel(cfg | pixcode[i][1], dev->regs + FLITE_REG_CIODMAFMT); | ||
234 | } | ||
235 | |||
236 | void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f) | ||
237 | { | ||
238 | u32 cfg; | ||
239 | |||
240 | /* Maximum output pixel size */ | ||
241 | cfg = readl(dev->regs + FLITE_REG_CIOCAN); | ||
242 | cfg &= ~FLITE_REG_CIOCAN_MASK; | ||
243 | cfg = (f->f_height << 16) | f->f_width; | ||
244 | writel(cfg, dev->regs + FLITE_REG_CIOCAN); | ||
245 | |||
246 | /* DMA offsets */ | ||
247 | cfg = readl(dev->regs + FLITE_REG_CIOOFF); | ||
248 | cfg &= ~FLITE_REG_CIOOFF_MASK; | ||
249 | cfg |= (f->rect.top << 16) | f->rect.left; | ||
250 | writel(cfg, dev->regs + FLITE_REG_CIOOFF); | ||
251 | } | ||
252 | |||
253 | /* Enable/disable output DMA, set output pixel size and offsets (composition) */ | ||
254 | void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f, | ||
255 | bool enable) | ||
256 | { | ||
257 | u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL); | ||
258 | |||
259 | if (!enable) { | ||
260 | cfg |= FLITE_REG_CIGCTRL_ODMA_DISABLE; | ||
261 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | ||
262 | return; | ||
263 | } | ||
264 | |||
265 | cfg &= ~FLITE_REG_CIGCTRL_ODMA_DISABLE; | ||
266 | writel(cfg, dev->regs + FLITE_REG_CIGCTRL); | ||
267 | |||
268 | flite_hw_set_out_order(dev, f); | ||
269 | flite_hw_set_dma_window(dev, f); | ||
270 | } | ||
271 | |||
272 | void flite_hw_dump_regs(struct fimc_lite *dev, const char *label) | ||
273 | { | ||
274 | struct { | ||
275 | u32 offset; | ||
276 | const char * const name; | ||
277 | } registers[] = { | ||
278 | { 0x00, "CISRCSIZE" }, | ||
279 | { 0x04, "CIGCTRL" }, | ||
280 | { 0x08, "CIIMGCPT" }, | ||
281 | { 0x0c, "CICPTSEQ" }, | ||
282 | { 0x10, "CIWDOFST" }, | ||
283 | { 0x14, "CIWDOFST2" }, | ||
284 | { 0x18, "CIODMAFMT" }, | ||
285 | { 0x20, "CIOCAN" }, | ||
286 | { 0x24, "CIOOFF" }, | ||
287 | { 0x30, "CIOSA" }, | ||
288 | { 0x40, "CISTATUS" }, | ||
289 | { 0x44, "CISTATUS2" }, | ||
290 | { 0xf0, "CITHOLD" }, | ||
291 | { 0xfc, "CIGENERAL" }, | ||
292 | }; | ||
293 | u32 i; | ||
294 | |||
295 | v4l2_info(&dev->subdev, "--- %s ---\n", label); | ||
296 | |||
297 | for (i = 0; i < ARRAY_SIZE(registers); i++) { | ||
298 | u32 cfg = readl(dev->regs + registers[i].offset); | ||
299 | v4l2_info(&dev->subdev, "%9s: 0x%08x\n", | ||
300 | registers[i].name, cfg); | ||
301 | } | ||
302 | } | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.h b/drivers/media/platform/exynos4-is/fimc-lite-reg.h new file mode 100644 index 000000000000..390383941c19 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.h | |||
@@ -0,0 +1,150 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Samsung Electronics Co., Ltd. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef FIMC_LITE_REG_H_ | ||
10 | #define FIMC_LITE_REG_H_ | ||
11 | |||
12 | #include "fimc-lite.h" | ||
13 | |||
14 | /* Camera Source size */ | ||
15 | #define FLITE_REG_CISRCSIZE 0x00 | ||
16 | #define FLITE_REG_CISRCSIZE_ORDER422_IN_YCBYCR (0 << 14) | ||
17 | #define FLITE_REG_CISRCSIZE_ORDER422_IN_YCRYCB (1 << 14) | ||
18 | #define FLITE_REG_CISRCSIZE_ORDER422_IN_CBYCRY (2 << 14) | ||
19 | #define FLITE_REG_CISRCSIZE_ORDER422_IN_CRYCBY (3 << 14) | ||
20 | #define FLITE_REG_CISRCSIZE_ORDER422_MASK (0x3 << 14) | ||
21 | #define FLITE_REG_CISRCSIZE_SIZE_CAM_MASK (0x3fff << 16 | 0x3fff) | ||
22 | |||
23 | /* Global control */ | ||
24 | #define FLITE_REG_CIGCTRL 0x04 | ||
25 | #define FLITE_REG_CIGCTRL_YUV422_1P (0x1e << 24) | ||
26 | #define FLITE_REG_CIGCTRL_RAW8 (0x2a << 24) | ||
27 | #define FLITE_REG_CIGCTRL_RAW10 (0x2b << 24) | ||
28 | #define FLITE_REG_CIGCTRL_RAW12 (0x2c << 24) | ||
29 | #define FLITE_REG_CIGCTRL_RAW14 (0x2d << 24) | ||
30 | /* User defined formats. x = 0...15 */ | ||
31 | #define FLITE_REG_CIGCTRL_USER(x) ((0x30 + x - 1) << 24) | ||
32 | #define FLITE_REG_CIGCTRL_FMT_MASK (0x3f << 24) | ||
33 | #define FLITE_REG_CIGCTRL_SHADOWMASK_DISABLE (1 << 21) | ||
34 | #define FLITE_REG_CIGCTRL_ODMA_DISABLE (1 << 20) | ||
35 | #define FLITE_REG_CIGCTRL_SWRST_REQ (1 << 19) | ||
36 | #define FLITE_REG_CIGCTRL_SWRST_RDY (1 << 18) | ||
37 | #define FLITE_REG_CIGCTRL_SWRST (1 << 17) | ||
38 | #define FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR (1 << 15) | ||
39 | #define FLITE_REG_CIGCTRL_INVPOLPCLK (1 << 14) | ||
40 | #define FLITE_REG_CIGCTRL_INVPOLVSYNC (1 << 13) | ||
41 | #define FLITE_REG_CIGCTRL_INVPOLHREF (1 << 12) | ||
42 | /* Interrupts mask bits (1 disables an interrupt) */ | ||
43 | #define FLITE_REG_CIGCTRL_IRQ_LASTEN (1 << 8) | ||
44 | #define FLITE_REG_CIGCTRL_IRQ_ENDEN (1 << 7) | ||
45 | #define FLITE_REG_CIGCTRL_IRQ_STARTEN (1 << 6) | ||
46 | #define FLITE_REG_CIGCTRL_IRQ_OVFEN (1 << 5) | ||
47 | #define FLITE_REG_CIGCTRL_IRQ_DISABLE_MASK (0xf << 5) | ||
48 | #define FLITE_REG_CIGCTRL_SELCAM_MIPI (1 << 3) | ||
49 | |||
50 | /* Image Capture Enable */ | ||
51 | #define FLITE_REG_CIIMGCPT 0x08 | ||
52 | #define FLITE_REG_CIIMGCPT_IMGCPTEN (1 << 31) | ||
53 | #define FLITE_REG_CIIMGCPT_CPT_FREN (1 << 25) | ||
54 | #define FLITE_REG_CIIMGCPT_CPT_MOD_FRCNT (1 << 18) | ||
55 | #define FLITE_REG_CIIMGCPT_CPT_MOD_FREN (0 << 18) | ||
56 | |||
57 | /* Capture Sequence */ | ||
58 | #define FLITE_REG_CICPTSEQ 0x0c | ||
59 | |||
60 | /* Camera Window Offset */ | ||
61 | #define FLITE_REG_CIWDOFST 0x10 | ||
62 | #define FLITE_REG_CIWDOFST_WINOFSEN (1 << 31) | ||
63 | #define FLITE_REG_CIWDOFST_CLROVIY (1 << 31) | ||
64 | #define FLITE_REG_CIWDOFST_CLROVFICB (1 << 15) | ||
65 | #define FLITE_REG_CIWDOFST_CLROVFICR (1 << 14) | ||
66 | #define FLITE_REG_CIWDOFST_OFST_MASK ((0x1fff << 16) | 0x1fff) | ||
67 | |||
68 | /* Camera Window Offset2 */ | ||
69 | #define FLITE_REG_CIWDOFST2 0x14 | ||
70 | |||
71 | /* Camera Output DMA Format */ | ||
72 | #define FLITE_REG_CIODMAFMT 0x18 | ||
73 | #define FLITE_REG_CIODMAFMT_RAW_CON (1 << 15) | ||
74 | #define FLITE_REG_CIODMAFMT_PACK12 (1 << 14) | ||
75 | #define FLITE_REG_CIODMAFMT_YCBYCR (0 << 4) | ||
76 | #define FLITE_REG_CIODMAFMT_YCRYCB (1 << 4) | ||
77 | #define FLITE_REG_CIODMAFMT_CBYCRY (2 << 4) | ||
78 | #define FLITE_REG_CIODMAFMT_CRYCBY (3 << 4) | ||
79 | #define FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK (0x3 << 4) | ||
80 | |||
81 | /* Camera Output Canvas */ | ||
82 | #define FLITE_REG_CIOCAN 0x20 | ||
83 | #define FLITE_REG_CIOCAN_MASK ((0x3fff << 16) | 0x3fff) | ||
84 | |||
85 | /* Camera Output DMA Offset */ | ||
86 | #define FLITE_REG_CIOOFF 0x24 | ||
87 | #define FLITE_REG_CIOOFF_MASK ((0x3fff << 16) | 0x3fff) | ||
88 | |||
89 | /* Camera Output DMA Start Address */ | ||
90 | #define FLITE_REG_CIOSA 0x30 | ||
91 | |||
92 | /* Camera Status */ | ||
93 | #define FLITE_REG_CISTATUS 0x40 | ||
94 | #define FLITE_REG_CISTATUS_MIPI_VVALID (1 << 22) | ||
95 | #define FLITE_REG_CISTATUS_MIPI_HVALID (1 << 21) | ||
96 | #define FLITE_REG_CISTATUS_MIPI_DVALID (1 << 20) | ||
97 | #define FLITE_REG_CISTATUS_ITU_VSYNC (1 << 14) | ||
98 | #define FLITE_REG_CISTATUS_ITU_HREFF (1 << 13) | ||
99 | #define FLITE_REG_CISTATUS_OVFIY (1 << 10) | ||
100 | #define FLITE_REG_CISTATUS_OVFICB (1 << 9) | ||
101 | #define FLITE_REG_CISTATUS_OVFICR (1 << 8) | ||
102 | #define FLITE_REG_CISTATUS_IRQ_SRC_OVERFLOW (1 << 7) | ||
103 | #define FLITE_REG_CISTATUS_IRQ_SRC_LASTCAPEND (1 << 6) | ||
104 | #define FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART (1 << 5) | ||
105 | #define FLITE_REG_CISTATUS_IRQ_SRC_FRMEND (1 << 4) | ||
106 | #define FLITE_REG_CISTATUS_IRQ_CAM (1 << 0) | ||
107 | #define FLITE_REG_CISTATUS_IRQ_MASK (0xf << 4) | ||
108 | |||
109 | /* Camera Status2 */ | ||
110 | #define FLITE_REG_CISTATUS2 0x44 | ||
111 | #define FLITE_REG_CISTATUS2_LASTCAPEND (1 << 1) | ||
112 | #define FLITE_REG_CISTATUS2_FRMEND (1 << 0) | ||
113 | |||
114 | /* Qos Threshold */ | ||
115 | #define FLITE_REG_CITHOLD 0xf0 | ||
116 | #define FLITE_REG_CITHOLD_W_QOS_EN (1 << 30) | ||
117 | |||
118 | /* Camera General Purpose */ | ||
119 | #define FLITE_REG_CIGENERAL 0xfc | ||
120 | /* b0: 1 - camera B, 0 - camera A */ | ||
121 | #define FLITE_REG_CIGENERAL_CAM_B (1 << 0) | ||
122 | |||
123 | /* ---------------------------------------------------------------------------- | ||
124 | * Function declarations | ||
125 | */ | ||
126 | void flite_hw_reset(struct fimc_lite *dev); | ||
127 | void flite_hw_clear_pending_irq(struct fimc_lite *dev); | ||
128 | u32 flite_hw_get_interrupt_source(struct fimc_lite *dev); | ||
129 | void flite_hw_clear_last_capture_end(struct fimc_lite *dev); | ||
130 | void flite_hw_set_interrupt_mask(struct fimc_lite *dev); | ||
131 | void flite_hw_capture_start(struct fimc_lite *dev); | ||
132 | void flite_hw_capture_stop(struct fimc_lite *dev); | ||
133 | void flite_hw_set_camera_bus(struct fimc_lite *dev, | ||
134 | struct fimc_source_info *s_info); | ||
135 | void flite_hw_set_camera_polarity(struct fimc_lite *dev, | ||
136 | struct fimc_source_info *cam); | ||
137 | void flite_hw_set_window_offset(struct fimc_lite *dev, struct flite_frame *f); | ||
138 | void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f); | ||
139 | |||
140 | void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f, | ||
141 | bool enable); | ||
142 | void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f); | ||
143 | void flite_hw_set_test_pattern(struct fimc_lite *dev, bool on); | ||
144 | void flite_hw_dump_regs(struct fimc_lite *dev, const char *label); | ||
145 | |||
146 | static inline void flite_hw_set_output_addr(struct fimc_lite *dev, u32 paddr) | ||
147 | { | ||
148 | writel(paddr, dev->regs + FLITE_REG_CIOSA); | ||
149 | } | ||
150 | #endif /* FIMC_LITE_REG_H */ | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c new file mode 100644 index 000000000000..14bb7bc8adbe --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-lite.c | |||
@@ -0,0 +1,1660 @@ | |||
1 | /* | ||
2 | * Samsung EXYNOS FIMC-LITE (camera host interface) driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Samsung Electronics Co., Ltd. | ||
5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ | ||
12 | |||
13 | #include <linux/bug.h> | ||
14 | #include <linux/clk.h> | ||
15 | #include <linux/device.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/list.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/of.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/pm_runtime.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/videodev2.h> | ||
27 | |||
28 | #include <media/v4l2-device.h> | ||
29 | #include <media/v4l2-ioctl.h> | ||
30 | #include <media/v4l2-mem2mem.h> | ||
31 | #include <media/videobuf2-core.h> | ||
32 | #include <media/videobuf2-dma-contig.h> | ||
33 | #include <media/s5p_fimc.h> | ||
34 | |||
35 | #include "fimc-core.h" | ||
36 | #include "fimc-lite.h" | ||
37 | #include "fimc-lite-reg.h" | ||
38 | |||
39 | static int debug; | ||
40 | module_param(debug, int, 0644); | ||
41 | |||
42 | static const struct fimc_fmt fimc_lite_formats[] = { | ||
43 | { | ||
44 | .name = "YUV 4:2:2 packed, YCbYCr", | ||
45 | .fourcc = V4L2_PIX_FMT_YUYV, | ||
46 | .depth = { 16 }, | ||
47 | .color = FIMC_FMT_YCBYCR422, | ||
48 | .memplanes = 1, | ||
49 | .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, | ||
50 | .flags = FMT_FLAGS_YUV, | ||
51 | }, { | ||
52 | .name = "YUV 4:2:2 packed, CbYCrY", | ||
53 | .fourcc = V4L2_PIX_FMT_UYVY, | ||
54 | .depth = { 16 }, | ||
55 | .color = FIMC_FMT_CBYCRY422, | ||
56 | .memplanes = 1, | ||
57 | .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, | ||
58 | .flags = FMT_FLAGS_YUV, | ||
59 | }, { | ||
60 | .name = "YUV 4:2:2 packed, CrYCbY", | ||
61 | .fourcc = V4L2_PIX_FMT_VYUY, | ||
62 | .depth = { 16 }, | ||
63 | .color = FIMC_FMT_CRYCBY422, | ||
64 | .memplanes = 1, | ||
65 | .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8, | ||
66 | .flags = FMT_FLAGS_YUV, | ||
67 | }, { | ||
68 | .name = "YUV 4:2:2 packed, YCrYCb", | ||
69 | .fourcc = V4L2_PIX_FMT_YVYU, | ||
70 | .depth = { 16 }, | ||
71 | .color = FIMC_FMT_YCRYCB422, | ||
72 | .memplanes = 1, | ||
73 | .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8, | ||
74 | .flags = FMT_FLAGS_YUV, | ||
75 | }, { | ||
76 | .name = "RAW8 (GRBG)", | ||
77 | .fourcc = V4L2_PIX_FMT_SGRBG8, | ||
78 | .depth = { 8 }, | ||
79 | .color = FIMC_FMT_RAW8, | ||
80 | .memplanes = 1, | ||
81 | .mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8, | ||
82 | .flags = FMT_FLAGS_RAW_BAYER, | ||
83 | }, { | ||
84 | .name = "RAW10 (GRBG)", | ||
85 | .fourcc = V4L2_PIX_FMT_SGRBG10, | ||
86 | .depth = { 10 }, | ||
87 | .color = FIMC_FMT_RAW10, | ||
88 | .memplanes = 1, | ||
89 | .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10, | ||
90 | .flags = FMT_FLAGS_RAW_BAYER, | ||
91 | }, { | ||
92 | .name = "RAW12 (GRBG)", | ||
93 | .fourcc = V4L2_PIX_FMT_SGRBG12, | ||
94 | .depth = { 12 }, | ||
95 | .color = FIMC_FMT_RAW12, | ||
96 | .memplanes = 1, | ||
97 | .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12, | ||
98 | .flags = FMT_FLAGS_RAW_BAYER, | ||
99 | }, | ||
100 | }; | ||
101 | |||
102 | /** | ||
103 | * fimc_lite_find_format - lookup fimc color format by fourcc or media bus code | ||
104 | * @pixelformat: fourcc to match, ignored if null | ||
105 | * @mbus_code: media bus code to match, ignored if null | ||
106 | * @mask: the color format flags to match | ||
107 | * @index: index to the fimc_lite_formats array, ignored if negative | ||
108 | */ | ||
109 | static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat, | ||
110 | const u32 *mbus_code, unsigned int mask, int index) | ||
111 | { | ||
112 | const struct fimc_fmt *fmt, *def_fmt = NULL; | ||
113 | unsigned int i; | ||
114 | int id = 0; | ||
115 | |||
116 | if (index >= (int)ARRAY_SIZE(fimc_lite_formats)) | ||
117 | return NULL; | ||
118 | |||
119 | for (i = 0; i < ARRAY_SIZE(fimc_lite_formats); ++i) { | ||
120 | fmt = &fimc_lite_formats[i]; | ||
121 | if (mask && !(fmt->flags & mask)) | ||
122 | continue; | ||
123 | if (pixelformat && fmt->fourcc == *pixelformat) | ||
124 | return fmt; | ||
125 | if (mbus_code && fmt->mbus_code == *mbus_code) | ||
126 | return fmt; | ||
127 | if (index == id) | ||
128 | def_fmt = fmt; | ||
129 | id++; | ||
130 | } | ||
131 | return def_fmt; | ||
132 | } | ||
133 | |||
134 | /* Called with the media graph mutex held or @me stream_count > 0. */ | ||
135 | static struct v4l2_subdev *__find_remote_sensor(struct media_entity *me) | ||
136 | { | ||
137 | struct media_pad *pad = &me->pads[0]; | ||
138 | struct v4l2_subdev *sd; | ||
139 | |||
140 | while (pad->flags & MEDIA_PAD_FL_SINK) { | ||
141 | /* source pad */ | ||
142 | pad = media_entity_remote_source(pad); | ||
143 | if (pad == NULL || | ||
144 | media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
145 | break; | ||
146 | |||
147 | sd = media_entity_to_v4l2_subdev(pad->entity); | ||
148 | |||
149 | if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR || | ||
150 | sd->grp_id == GRP_ID_SENSOR) | ||
151 | return sd; | ||
152 | /* sink pad */ | ||
153 | pad = &sd->entity.pads[0]; | ||
154 | } | ||
155 | return NULL; | ||
156 | } | ||
157 | |||
158 | static int fimc_lite_hw_init(struct fimc_lite *fimc, bool isp_output) | ||
159 | { | ||
160 | struct fimc_source_info *si; | ||
161 | unsigned long flags; | ||
162 | |||
163 | if (fimc->sensor == NULL) | ||
164 | return -ENXIO; | ||
165 | |||
166 | if (fimc->inp_frame.fmt == NULL || fimc->out_frame.fmt == NULL) | ||
167 | return -EINVAL; | ||
168 | |||
169 | /* Get sensor configuration data from the sensor subdev */ | ||
170 | si = v4l2_get_subdev_hostdata(fimc->sensor); | ||
171 | if (!si) | ||
172 | return -EINVAL; | ||
173 | |||
174 | spin_lock_irqsave(&fimc->slock, flags); | ||
175 | |||
176 | flite_hw_set_camera_bus(fimc, si); | ||
177 | flite_hw_set_source_format(fimc, &fimc->inp_frame); | ||
178 | flite_hw_set_window_offset(fimc, &fimc->inp_frame); | ||
179 | flite_hw_set_output_dma(fimc, &fimc->out_frame, !isp_output); | ||
180 | flite_hw_set_interrupt_mask(fimc); | ||
181 | flite_hw_set_test_pattern(fimc, fimc->test_pattern->val); | ||
182 | |||
183 | if (debug > 0) | ||
184 | flite_hw_dump_regs(fimc, __func__); | ||
185 | |||
186 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * Reinitialize the driver so it is ready to start the streaming again. | ||
192 | * Set fimc->state to indicate stream off and the hardware shut down state. | ||
193 | * If not suspending (@suspend is false), return any buffers to videobuf2. | ||
194 | * Otherwise put any owned buffers onto the pending buffers queue, so they | ||
195 | * can be re-spun when the device is being resumed. Also perform FIMC | ||
196 | * software reset and disable streaming on the whole pipeline if required. | ||
197 | */ | ||
198 | static int fimc_lite_reinit(struct fimc_lite *fimc, bool suspend) | ||
199 | { | ||
200 | struct flite_buffer *buf; | ||
201 | unsigned long flags; | ||
202 | bool streaming; | ||
203 | |||
204 | spin_lock_irqsave(&fimc->slock, flags); | ||
205 | streaming = fimc->state & (1 << ST_SENSOR_STREAM); | ||
206 | |||
207 | fimc->state &= ~(1 << ST_FLITE_RUN | 1 << ST_FLITE_OFF | | ||
208 | 1 << ST_FLITE_STREAM | 1 << ST_SENSOR_STREAM); | ||
209 | if (suspend) | ||
210 | fimc->state |= (1 << ST_FLITE_SUSPENDED); | ||
211 | else | ||
212 | fimc->state &= ~(1 << ST_FLITE_PENDING | | ||
213 | 1 << ST_FLITE_SUSPENDED); | ||
214 | |||
215 | /* Release unused buffers */ | ||
216 | while (!suspend && !list_empty(&fimc->pending_buf_q)) { | ||
217 | buf = fimc_lite_pending_queue_pop(fimc); | ||
218 | vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); | ||
219 | } | ||
220 | /* If suspending put unused buffers onto pending queue */ | ||
221 | while (!list_empty(&fimc->active_buf_q)) { | ||
222 | buf = fimc_lite_active_queue_pop(fimc); | ||
223 | if (suspend) | ||
224 | fimc_lite_pending_queue_add(fimc, buf); | ||
225 | else | ||
226 | vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); | ||
227 | } | ||
228 | |||
229 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
230 | |||
231 | flite_hw_reset(fimc); | ||
232 | |||
233 | if (!streaming) | ||
234 | return 0; | ||
235 | |||
236 | return fimc_pipeline_call(fimc, set_stream, &fimc->pipeline, 0); | ||
237 | } | ||
238 | |||
239 | static int fimc_lite_stop_capture(struct fimc_lite *fimc, bool suspend) | ||
240 | { | ||
241 | unsigned long flags; | ||
242 | |||
243 | if (!fimc_lite_active(fimc)) | ||
244 | return 0; | ||
245 | |||
246 | spin_lock_irqsave(&fimc->slock, flags); | ||
247 | set_bit(ST_FLITE_OFF, &fimc->state); | ||
248 | flite_hw_capture_stop(fimc); | ||
249 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
250 | |||
251 | wait_event_timeout(fimc->irq_queue, | ||
252 | !test_bit(ST_FLITE_OFF, &fimc->state), | ||
253 | (2*HZ/10)); /* 200 ms */ | ||
254 | |||
255 | return fimc_lite_reinit(fimc, suspend); | ||
256 | } | ||
257 | |||
258 | /* Must be called with fimc.slock spinlock held. */ | ||
259 | static void fimc_lite_config_update(struct fimc_lite *fimc) | ||
260 | { | ||
261 | flite_hw_set_window_offset(fimc, &fimc->inp_frame); | ||
262 | flite_hw_set_dma_window(fimc, &fimc->out_frame); | ||
263 | flite_hw_set_test_pattern(fimc, fimc->test_pattern->val); | ||
264 | clear_bit(ST_FLITE_CONFIG, &fimc->state); | ||
265 | } | ||
266 | |||
267 | static irqreturn_t flite_irq_handler(int irq, void *priv) | ||
268 | { | ||
269 | struct fimc_lite *fimc = priv; | ||
270 | struct flite_buffer *vbuf; | ||
271 | unsigned long flags; | ||
272 | struct timeval *tv; | ||
273 | struct timespec ts; | ||
274 | u32 intsrc; | ||
275 | |||
276 | spin_lock_irqsave(&fimc->slock, flags); | ||
277 | |||
278 | intsrc = flite_hw_get_interrupt_source(fimc); | ||
279 | flite_hw_clear_pending_irq(fimc); | ||
280 | |||
281 | if (test_and_clear_bit(ST_FLITE_OFF, &fimc->state)) { | ||
282 | wake_up(&fimc->irq_queue); | ||
283 | goto done; | ||
284 | } | ||
285 | |||
286 | if (intsrc & FLITE_REG_CISTATUS_IRQ_SRC_OVERFLOW) { | ||
287 | clear_bit(ST_FLITE_RUN, &fimc->state); | ||
288 | fimc->events.data_overflow++; | ||
289 | } | ||
290 | |||
291 | if (intsrc & FLITE_REG_CISTATUS_IRQ_SRC_LASTCAPEND) { | ||
292 | flite_hw_clear_last_capture_end(fimc); | ||
293 | clear_bit(ST_FLITE_STREAM, &fimc->state); | ||
294 | wake_up(&fimc->irq_queue); | ||
295 | } | ||
296 | |||
297 | if (atomic_read(&fimc->out_path) != FIMC_IO_DMA) | ||
298 | goto done; | ||
299 | |||
300 | if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART) && | ||
301 | test_bit(ST_FLITE_RUN, &fimc->state) && | ||
302 | !list_empty(&fimc->active_buf_q) && | ||
303 | !list_empty(&fimc->pending_buf_q)) { | ||
304 | vbuf = fimc_lite_active_queue_pop(fimc); | ||
305 | ktime_get_ts(&ts); | ||
306 | tv = &vbuf->vb.v4l2_buf.timestamp; | ||
307 | tv->tv_sec = ts.tv_sec; | ||
308 | tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; | ||
309 | vbuf->vb.v4l2_buf.sequence = fimc->frame_count++; | ||
310 | vb2_buffer_done(&vbuf->vb, VB2_BUF_STATE_DONE); | ||
311 | |||
312 | vbuf = fimc_lite_pending_queue_pop(fimc); | ||
313 | flite_hw_set_output_addr(fimc, vbuf->paddr); | ||
314 | fimc_lite_active_queue_add(fimc, vbuf); | ||
315 | } | ||
316 | |||
317 | if (test_bit(ST_FLITE_CONFIG, &fimc->state)) | ||
318 | fimc_lite_config_update(fimc); | ||
319 | |||
320 | if (list_empty(&fimc->pending_buf_q)) { | ||
321 | flite_hw_capture_stop(fimc); | ||
322 | clear_bit(ST_FLITE_STREAM, &fimc->state); | ||
323 | } | ||
324 | done: | ||
325 | set_bit(ST_FLITE_RUN, &fimc->state); | ||
326 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
327 | return IRQ_HANDLED; | ||
328 | } | ||
329 | |||
330 | static int start_streaming(struct vb2_queue *q, unsigned int count) | ||
331 | { | ||
332 | struct fimc_lite *fimc = q->drv_priv; | ||
333 | int ret; | ||
334 | |||
335 | fimc->frame_count = 0; | ||
336 | |||
337 | ret = fimc_lite_hw_init(fimc, false); | ||
338 | if (ret) { | ||
339 | fimc_lite_reinit(fimc, false); | ||
340 | return ret; | ||
341 | } | ||
342 | |||
343 | set_bit(ST_FLITE_PENDING, &fimc->state); | ||
344 | |||
345 | if (!list_empty(&fimc->active_buf_q) && | ||
346 | !test_and_set_bit(ST_FLITE_STREAM, &fimc->state)) { | ||
347 | flite_hw_capture_start(fimc); | ||
348 | |||
349 | if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state)) | ||
350 | fimc_pipeline_call(fimc, set_stream, | ||
351 | &fimc->pipeline, 1); | ||
352 | } | ||
353 | if (debug > 0) | ||
354 | flite_hw_dump_regs(fimc, __func__); | ||
355 | |||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | static int stop_streaming(struct vb2_queue *q) | ||
360 | { | ||
361 | struct fimc_lite *fimc = q->drv_priv; | ||
362 | |||
363 | if (!fimc_lite_active(fimc)) | ||
364 | return -EINVAL; | ||
365 | |||
366 | return fimc_lite_stop_capture(fimc, false); | ||
367 | } | ||
368 | |||
369 | static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, | ||
370 | unsigned int *num_buffers, unsigned int *num_planes, | ||
371 | unsigned int sizes[], void *allocators[]) | ||
372 | { | ||
373 | const struct v4l2_pix_format_mplane *pixm = NULL; | ||
374 | struct fimc_lite *fimc = vq->drv_priv; | ||
375 | struct flite_frame *frame = &fimc->out_frame; | ||
376 | const struct fimc_fmt *fmt = frame->fmt; | ||
377 | unsigned long wh; | ||
378 | int i; | ||
379 | |||
380 | if (pfmt) { | ||
381 | pixm = &pfmt->fmt.pix_mp; | ||
382 | fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, 0, -1); | ||
383 | wh = pixm->width * pixm->height; | ||
384 | } else { | ||
385 | wh = frame->f_width * frame->f_height; | ||
386 | } | ||
387 | |||
388 | if (fmt == NULL) | ||
389 | return -EINVAL; | ||
390 | |||
391 | *num_planes = fmt->memplanes; | ||
392 | |||
393 | for (i = 0; i < fmt->memplanes; i++) { | ||
394 | unsigned int size = (wh * fmt->depth[i]) / 8; | ||
395 | if (pixm) | ||
396 | sizes[i] = max(size, pixm->plane_fmt[i].sizeimage); | ||
397 | else | ||
398 | sizes[i] = size; | ||
399 | allocators[i] = fimc->alloc_ctx; | ||
400 | } | ||
401 | |||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | static int buffer_prepare(struct vb2_buffer *vb) | ||
406 | { | ||
407 | struct vb2_queue *vq = vb->vb2_queue; | ||
408 | struct fimc_lite *fimc = vq->drv_priv; | ||
409 | int i; | ||
410 | |||
411 | if (fimc->out_frame.fmt == NULL) | ||
412 | return -EINVAL; | ||
413 | |||
414 | for (i = 0; i < fimc->out_frame.fmt->memplanes; i++) { | ||
415 | unsigned long size = fimc->payload[i]; | ||
416 | |||
417 | if (vb2_plane_size(vb, i) < size) { | ||
418 | v4l2_err(&fimc->vfd, | ||
419 | "User buffer too small (%ld < %ld)\n", | ||
420 | vb2_plane_size(vb, i), size); | ||
421 | return -EINVAL; | ||
422 | } | ||
423 | vb2_set_plane_payload(vb, i, size); | ||
424 | } | ||
425 | |||
426 | return 0; | ||
427 | } | ||
428 | |||
429 | static void buffer_queue(struct vb2_buffer *vb) | ||
430 | { | ||
431 | struct flite_buffer *buf | ||
432 | = container_of(vb, struct flite_buffer, vb); | ||
433 | struct fimc_lite *fimc = vb2_get_drv_priv(vb->vb2_queue); | ||
434 | unsigned long flags; | ||
435 | |||
436 | spin_lock_irqsave(&fimc->slock, flags); | ||
437 | buf->paddr = vb2_dma_contig_plane_dma_addr(vb, 0); | ||
438 | |||
439 | if (!test_bit(ST_FLITE_SUSPENDED, &fimc->state) && | ||
440 | !test_bit(ST_FLITE_STREAM, &fimc->state) && | ||
441 | list_empty(&fimc->active_buf_q)) { | ||
442 | flite_hw_set_output_addr(fimc, buf->paddr); | ||
443 | fimc_lite_active_queue_add(fimc, buf); | ||
444 | } else { | ||
445 | fimc_lite_pending_queue_add(fimc, buf); | ||
446 | } | ||
447 | |||
448 | if (vb2_is_streaming(&fimc->vb_queue) && | ||
449 | !list_empty(&fimc->pending_buf_q) && | ||
450 | !test_and_set_bit(ST_FLITE_STREAM, &fimc->state)) { | ||
451 | flite_hw_capture_start(fimc); | ||
452 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
453 | |||
454 | if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state)) | ||
455 | fimc_pipeline_call(fimc, set_stream, | ||
456 | &fimc->pipeline, 1); | ||
457 | return; | ||
458 | } | ||
459 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
460 | } | ||
461 | |||
462 | static const struct vb2_ops fimc_lite_qops = { | ||
463 | .queue_setup = queue_setup, | ||
464 | .buf_prepare = buffer_prepare, | ||
465 | .buf_queue = buffer_queue, | ||
466 | .wait_prepare = vb2_ops_wait_prepare, | ||
467 | .wait_finish = vb2_ops_wait_finish, | ||
468 | .start_streaming = start_streaming, | ||
469 | .stop_streaming = stop_streaming, | ||
470 | }; | ||
471 | |||
472 | static void fimc_lite_clear_event_counters(struct fimc_lite *fimc) | ||
473 | { | ||
474 | unsigned long flags; | ||
475 | |||
476 | spin_lock_irqsave(&fimc->slock, flags); | ||
477 | memset(&fimc->events, 0, sizeof(fimc->events)); | ||
478 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
479 | } | ||
480 | |||
481 | static int fimc_lite_open(struct file *file) | ||
482 | { | ||
483 | struct fimc_lite *fimc = video_drvdata(file); | ||
484 | struct media_entity *me = &fimc->vfd.entity; | ||
485 | int ret; | ||
486 | |||
487 | mutex_lock(&me->parent->graph_mutex); | ||
488 | |||
489 | mutex_lock(&fimc->lock); | ||
490 | if (atomic_read(&fimc->out_path) != FIMC_IO_DMA) { | ||
491 | ret = -EBUSY; | ||
492 | goto unlock; | ||
493 | } | ||
494 | |||
495 | set_bit(ST_FLITE_IN_USE, &fimc->state); | ||
496 | ret = pm_runtime_get_sync(&fimc->pdev->dev); | ||
497 | if (ret < 0) | ||
498 | goto unlock; | ||
499 | |||
500 | ret = v4l2_fh_open(file); | ||
501 | if (ret < 0) | ||
502 | goto err_pm; | ||
503 | |||
504 | if (!v4l2_fh_is_singular_file(file) || | ||
505 | atomic_read(&fimc->out_path) != FIMC_IO_DMA) | ||
506 | goto unlock; | ||
507 | |||
508 | ret = fimc_pipeline_call(fimc, open, &fimc->pipeline, | ||
509 | me, true); | ||
510 | if (!ret) { | ||
511 | fimc_lite_clear_event_counters(fimc); | ||
512 | fimc->ref_count++; | ||
513 | goto unlock; | ||
514 | } | ||
515 | |||
516 | v4l2_fh_release(file); | ||
517 | err_pm: | ||
518 | pm_runtime_put_sync(&fimc->pdev->dev); | ||
519 | clear_bit(ST_FLITE_IN_USE, &fimc->state); | ||
520 | unlock: | ||
521 | mutex_unlock(&fimc->lock); | ||
522 | mutex_unlock(&me->parent->graph_mutex); | ||
523 | return ret; | ||
524 | } | ||
525 | |||
526 | static int fimc_lite_release(struct file *file) | ||
527 | { | ||
528 | struct fimc_lite *fimc = video_drvdata(file); | ||
529 | |||
530 | mutex_lock(&fimc->lock); | ||
531 | |||
532 | if (v4l2_fh_is_singular_file(file) && | ||
533 | atomic_read(&fimc->out_path) == FIMC_IO_DMA) { | ||
534 | if (fimc->streaming) { | ||
535 | media_entity_pipeline_stop(&fimc->vfd.entity); | ||
536 | fimc->streaming = false; | ||
537 | } | ||
538 | clear_bit(ST_FLITE_IN_USE, &fimc->state); | ||
539 | fimc_lite_stop_capture(fimc, false); | ||
540 | fimc_pipeline_call(fimc, close, &fimc->pipeline); | ||
541 | fimc->ref_count--; | ||
542 | } | ||
543 | |||
544 | vb2_fop_release(file); | ||
545 | pm_runtime_put(&fimc->pdev->dev); | ||
546 | clear_bit(ST_FLITE_SUSPENDED, &fimc->state); | ||
547 | |||
548 | mutex_unlock(&fimc->lock); | ||
549 | return 0; | ||
550 | } | ||
551 | |||
552 | static const struct v4l2_file_operations fimc_lite_fops = { | ||
553 | .owner = THIS_MODULE, | ||
554 | .open = fimc_lite_open, | ||
555 | .release = fimc_lite_release, | ||
556 | .poll = vb2_fop_poll, | ||
557 | .unlocked_ioctl = video_ioctl2, | ||
558 | .mmap = vb2_fop_mmap, | ||
559 | }; | ||
560 | |||
561 | /* | ||
562 | * Format and crop negotiation helpers | ||
563 | */ | ||
564 | |||
565 | static const struct fimc_fmt *fimc_lite_try_format(struct fimc_lite *fimc, | ||
566 | u32 *width, u32 *height, | ||
567 | u32 *code, u32 *fourcc, int pad) | ||
568 | { | ||
569 | struct flite_drvdata *dd = fimc->dd; | ||
570 | const struct fimc_fmt *fmt; | ||
571 | unsigned int flags = 0; | ||
572 | |||
573 | if (pad == FLITE_SD_PAD_SINK) { | ||
574 | v4l_bound_align_image(width, 8, dd->max_width, | ||
575 | ffs(dd->out_width_align) - 1, | ||
576 | height, 0, dd->max_height, 0, 0); | ||
577 | } else { | ||
578 | v4l_bound_align_image(width, 8, fimc->inp_frame.rect.width, | ||
579 | ffs(dd->out_width_align) - 1, | ||
580 | height, 0, fimc->inp_frame.rect.height, | ||
581 | 0, 0); | ||
582 | flags = fimc->inp_frame.fmt->flags; | ||
583 | } | ||
584 | |||
585 | fmt = fimc_lite_find_format(fourcc, code, flags, 0); | ||
586 | if (WARN_ON(!fmt)) | ||
587 | return NULL; | ||
588 | |||
589 | if (code) | ||
590 | *code = fmt->mbus_code; | ||
591 | if (fourcc) | ||
592 | *fourcc = fmt->fourcc; | ||
593 | |||
594 | v4l2_dbg(1, debug, &fimc->subdev, "code: 0x%x, %dx%d\n", | ||
595 | code ? *code : 0, *width, *height); | ||
596 | |||
597 | return fmt; | ||
598 | } | ||
599 | |||
600 | static void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r) | ||
601 | { | ||
602 | struct flite_frame *frame = &fimc->inp_frame; | ||
603 | |||
604 | v4l_bound_align_image(&r->width, 0, frame->f_width, 0, | ||
605 | &r->height, 0, frame->f_height, 0, 0); | ||
606 | |||
607 | /* Adjust left/top if cropping rectangle got out of bounds */ | ||
608 | r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width); | ||
609 | r->left = round_down(r->left, fimc->dd->win_hor_offs_align); | ||
610 | r->top = clamp_t(u32, r->top, 0, frame->f_height - r->height); | ||
611 | |||
612 | v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d\n", | ||
613 | r->left, r->top, r->width, r->height, | ||
614 | frame->f_width, frame->f_height); | ||
615 | } | ||
616 | |||
617 | static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r) | ||
618 | { | ||
619 | struct flite_frame *frame = &fimc->out_frame; | ||
620 | struct v4l2_rect *crop_rect = &fimc->inp_frame.rect; | ||
621 | |||
622 | /* Scaling is not supported so we enforce compose rectangle size | ||
623 | same as size of the sink crop rectangle. */ | ||
624 | r->width = crop_rect->width; | ||
625 | r->height = crop_rect->height; | ||
626 | |||
627 | /* Adjust left/top if the composing rectangle got out of bounds */ | ||
628 | r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width); | ||
629 | r->left = round_down(r->left, fimc->dd->out_hor_offs_align); | ||
630 | r->top = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height); | ||
631 | |||
632 | v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d\n", | ||
633 | r->left, r->top, r->width, r->height, | ||
634 | frame->f_width, frame->f_height); | ||
635 | } | ||
636 | |||
637 | /* | ||
638 | * Video node ioctl operations | ||
639 | */ | ||
640 | static int fimc_vidioc_querycap_capture(struct file *file, void *priv, | ||
641 | struct v4l2_capability *cap) | ||
642 | { | ||
643 | strlcpy(cap->driver, FIMC_LITE_DRV_NAME, sizeof(cap->driver)); | ||
644 | cap->bus_info[0] = 0; | ||
645 | cap->card[0] = 0; | ||
646 | cap->capabilities = V4L2_CAP_STREAMING; | ||
647 | return 0; | ||
648 | } | ||
649 | |||
650 | static int fimc_lite_enum_fmt_mplane(struct file *file, void *priv, | ||
651 | struct v4l2_fmtdesc *f) | ||
652 | { | ||
653 | const struct fimc_fmt *fmt; | ||
654 | |||
655 | if (f->index >= ARRAY_SIZE(fimc_lite_formats)) | ||
656 | return -EINVAL; | ||
657 | |||
658 | fmt = &fimc_lite_formats[f->index]; | ||
659 | strlcpy(f->description, fmt->name, sizeof(f->description)); | ||
660 | f->pixelformat = fmt->fourcc; | ||
661 | |||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | static int fimc_lite_g_fmt_mplane(struct file *file, void *fh, | ||
666 | struct v4l2_format *f) | ||
667 | { | ||
668 | struct fimc_lite *fimc = video_drvdata(file); | ||
669 | struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; | ||
670 | struct v4l2_plane_pix_format *plane_fmt = &pixm->plane_fmt[0]; | ||
671 | struct flite_frame *frame = &fimc->out_frame; | ||
672 | const struct fimc_fmt *fmt = frame->fmt; | ||
673 | |||
674 | plane_fmt->bytesperline = (frame->f_width * fmt->depth[0]) / 8; | ||
675 | plane_fmt->sizeimage = plane_fmt->bytesperline * frame->f_height; | ||
676 | |||
677 | pixm->num_planes = fmt->memplanes; | ||
678 | pixm->pixelformat = fmt->fourcc; | ||
679 | pixm->width = frame->f_width; | ||
680 | pixm->height = frame->f_height; | ||
681 | pixm->field = V4L2_FIELD_NONE; | ||
682 | pixm->colorspace = V4L2_COLORSPACE_JPEG; | ||
683 | return 0; | ||
684 | } | ||
685 | |||
686 | static int fimc_lite_try_fmt(struct fimc_lite *fimc, | ||
687 | struct v4l2_pix_format_mplane *pixm, | ||
688 | const struct fimc_fmt **ffmt) | ||
689 | { | ||
690 | u32 bpl = pixm->plane_fmt[0].bytesperline; | ||
691 | struct flite_drvdata *dd = fimc->dd; | ||
692 | const struct fimc_fmt *inp_fmt = fimc->inp_frame.fmt; | ||
693 | const struct fimc_fmt *fmt; | ||
694 | |||
695 | if (WARN_ON(inp_fmt == NULL)) | ||
696 | return -EINVAL; | ||
697 | /* | ||
698 | * We allow some flexibility only for YUV formats. In case of raw | ||
699 | * raw Bayer the FIMC-LITE's output format must match its camera | ||
700 | * interface input format. | ||
701 | */ | ||
702 | if (inp_fmt->flags & FMT_FLAGS_YUV) | ||
703 | fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, | ||
704 | inp_fmt->flags, 0); | ||
705 | else | ||
706 | fmt = inp_fmt; | ||
707 | |||
708 | if (WARN_ON(fmt == NULL)) | ||
709 | return -EINVAL; | ||
710 | if (ffmt) | ||
711 | *ffmt = fmt; | ||
712 | v4l_bound_align_image(&pixm->width, 8, dd->max_width, | ||
713 | ffs(dd->out_width_align) - 1, | ||
714 | &pixm->height, 0, dd->max_height, 0, 0); | ||
715 | |||
716 | if ((bpl == 0 || ((bpl * 8) / fmt->depth[0]) < pixm->width)) | ||
717 | pixm->plane_fmt[0].bytesperline = (pixm->width * | ||
718 | fmt->depth[0]) / 8; | ||
719 | |||
720 | if (pixm->plane_fmt[0].sizeimage == 0) | ||
721 | pixm->plane_fmt[0].sizeimage = (pixm->width * pixm->height * | ||
722 | fmt->depth[0]) / 8; | ||
723 | pixm->num_planes = fmt->memplanes; | ||
724 | pixm->pixelformat = fmt->fourcc; | ||
725 | pixm->colorspace = V4L2_COLORSPACE_JPEG; | ||
726 | pixm->field = V4L2_FIELD_NONE; | ||
727 | return 0; | ||
728 | } | ||
729 | |||
730 | static int fimc_lite_try_fmt_mplane(struct file *file, void *fh, | ||
731 | struct v4l2_format *f) | ||
732 | { | ||
733 | struct fimc_lite *fimc = video_drvdata(file); | ||
734 | return fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, NULL); | ||
735 | } | ||
736 | |||
737 | static int fimc_lite_s_fmt_mplane(struct file *file, void *priv, | ||
738 | struct v4l2_format *f) | ||
739 | { | ||
740 | struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; | ||
741 | struct fimc_lite *fimc = video_drvdata(file); | ||
742 | struct flite_frame *frame = &fimc->out_frame; | ||
743 | const struct fimc_fmt *fmt = NULL; | ||
744 | int ret; | ||
745 | |||
746 | if (vb2_is_busy(&fimc->vb_queue)) | ||
747 | return -EBUSY; | ||
748 | |||
749 | ret = fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, &fmt); | ||
750 | if (ret < 0) | ||
751 | return ret; | ||
752 | |||
753 | frame->fmt = fmt; | ||
754 | fimc->payload[0] = max((pixm->width * pixm->height * fmt->depth[0]) / 8, | ||
755 | pixm->plane_fmt[0].sizeimage); | ||
756 | frame->f_width = pixm->width; | ||
757 | frame->f_height = pixm->height; | ||
758 | |||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | static int fimc_pipeline_validate(struct fimc_lite *fimc) | ||
763 | { | ||
764 | struct v4l2_subdev *sd = &fimc->subdev; | ||
765 | struct v4l2_subdev_format sink_fmt, src_fmt; | ||
766 | struct media_pad *pad; | ||
767 | int ret; | ||
768 | |||
769 | while (1) { | ||
770 | /* Retrieve format at the sink pad */ | ||
771 | pad = &sd->entity.pads[0]; | ||
772 | if (!(pad->flags & MEDIA_PAD_FL_SINK)) | ||
773 | break; | ||
774 | /* Don't call FIMC subdev operation to avoid nested locking */ | ||
775 | if (sd == &fimc->subdev) { | ||
776 | struct flite_frame *ff = &fimc->out_frame; | ||
777 | sink_fmt.format.width = ff->f_width; | ||
778 | sink_fmt.format.height = ff->f_height; | ||
779 | sink_fmt.format.code = fimc->inp_frame.fmt->mbus_code; | ||
780 | } else { | ||
781 | sink_fmt.pad = pad->index; | ||
782 | sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
783 | ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, | ||
784 | &sink_fmt); | ||
785 | if (ret < 0 && ret != -ENOIOCTLCMD) | ||
786 | return -EPIPE; | ||
787 | } | ||
788 | /* Retrieve format at the source pad */ | ||
789 | pad = media_entity_remote_source(pad); | ||
790 | if (pad == NULL || | ||
791 | media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
792 | break; | ||
793 | |||
794 | sd = media_entity_to_v4l2_subdev(pad->entity); | ||
795 | src_fmt.pad = pad->index; | ||
796 | src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
797 | ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); | ||
798 | if (ret < 0 && ret != -ENOIOCTLCMD) | ||
799 | return -EPIPE; | ||
800 | |||
801 | if (src_fmt.format.width != sink_fmt.format.width || | ||
802 | src_fmt.format.height != sink_fmt.format.height || | ||
803 | src_fmt.format.code != sink_fmt.format.code) | ||
804 | return -EPIPE; | ||
805 | } | ||
806 | return 0; | ||
807 | } | ||
808 | |||
809 | static int fimc_lite_streamon(struct file *file, void *priv, | ||
810 | enum v4l2_buf_type type) | ||
811 | { | ||
812 | struct fimc_lite *fimc = video_drvdata(file); | ||
813 | struct media_entity *entity = &fimc->vfd.entity; | ||
814 | struct fimc_pipeline *p = &fimc->pipeline; | ||
815 | int ret; | ||
816 | |||
817 | if (fimc_lite_active(fimc)) | ||
818 | return -EBUSY; | ||
819 | |||
820 | ret = media_entity_pipeline_start(entity, p->m_pipeline); | ||
821 | if (ret < 0) | ||
822 | return ret; | ||
823 | |||
824 | ret = fimc_pipeline_validate(fimc); | ||
825 | if (ret < 0) | ||
826 | goto err_p_stop; | ||
827 | |||
828 | fimc->sensor = __find_remote_sensor(&fimc->subdev.entity); | ||
829 | |||
830 | ret = vb2_ioctl_streamon(file, priv, type); | ||
831 | if (!ret) { | ||
832 | fimc->streaming = true; | ||
833 | return ret; | ||
834 | } | ||
835 | |||
836 | err_p_stop: | ||
837 | media_entity_pipeline_stop(entity); | ||
838 | return 0; | ||
839 | } | ||
840 | |||
841 | static int fimc_lite_streamoff(struct file *file, void *priv, | ||
842 | enum v4l2_buf_type type) | ||
843 | { | ||
844 | struct fimc_lite *fimc = video_drvdata(file); | ||
845 | int ret; | ||
846 | |||
847 | ret = vb2_ioctl_streamoff(file, priv, type); | ||
848 | if (ret < 0) | ||
849 | return ret; | ||
850 | |||
851 | media_entity_pipeline_stop(&fimc->vfd.entity); | ||
852 | fimc->streaming = false; | ||
853 | return 0; | ||
854 | } | ||
855 | |||
856 | static int fimc_lite_reqbufs(struct file *file, void *priv, | ||
857 | struct v4l2_requestbuffers *reqbufs) | ||
858 | { | ||
859 | struct fimc_lite *fimc = video_drvdata(file); | ||
860 | int ret; | ||
861 | |||
862 | reqbufs->count = max_t(u32, FLITE_REQ_BUFS_MIN, reqbufs->count); | ||
863 | ret = vb2_ioctl_reqbufs(file, priv, reqbufs); | ||
864 | if (!ret) | ||
865 | fimc->reqbufs_count = reqbufs->count; | ||
866 | |||
867 | return ret; | ||
868 | } | ||
869 | |||
870 | /* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ | ||
871 | static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) | ||
872 | { | ||
873 | if (a->left < b->left || a->top < b->top) | ||
874 | return 0; | ||
875 | if (a->left + a->width > b->left + b->width) | ||
876 | return 0; | ||
877 | if (a->top + a->height > b->top + b->height) | ||
878 | return 0; | ||
879 | |||
880 | return 1; | ||
881 | } | ||
882 | |||
883 | static int fimc_lite_g_selection(struct file *file, void *fh, | ||
884 | struct v4l2_selection *sel) | ||
885 | { | ||
886 | struct fimc_lite *fimc = video_drvdata(file); | ||
887 | struct flite_frame *f = &fimc->out_frame; | ||
888 | |||
889 | if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | ||
890 | return -EINVAL; | ||
891 | |||
892 | switch (sel->target) { | ||
893 | case V4L2_SEL_TGT_COMPOSE_BOUNDS: | ||
894 | case V4L2_SEL_TGT_COMPOSE_DEFAULT: | ||
895 | sel->r.left = 0; | ||
896 | sel->r.top = 0; | ||
897 | sel->r.width = f->f_width; | ||
898 | sel->r.height = f->f_height; | ||
899 | return 0; | ||
900 | |||
901 | case V4L2_SEL_TGT_COMPOSE: | ||
902 | sel->r = f->rect; | ||
903 | return 0; | ||
904 | } | ||
905 | |||
906 | return -EINVAL; | ||
907 | } | ||
908 | |||
909 | static int fimc_lite_s_selection(struct file *file, void *fh, | ||
910 | struct v4l2_selection *sel) | ||
911 | { | ||
912 | struct fimc_lite *fimc = video_drvdata(file); | ||
913 | struct flite_frame *f = &fimc->out_frame; | ||
914 | struct v4l2_rect rect = sel->r; | ||
915 | unsigned long flags; | ||
916 | |||
917 | if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || | ||
918 | sel->target != V4L2_SEL_TGT_COMPOSE) | ||
919 | return -EINVAL; | ||
920 | |||
921 | fimc_lite_try_compose(fimc, &rect); | ||
922 | |||
923 | if ((sel->flags & V4L2_SEL_FLAG_LE) && | ||
924 | !enclosed_rectangle(&rect, &sel->r)) | ||
925 | return -ERANGE; | ||
926 | |||
927 | if ((sel->flags & V4L2_SEL_FLAG_GE) && | ||
928 | !enclosed_rectangle(&sel->r, &rect)) | ||
929 | return -ERANGE; | ||
930 | |||
931 | sel->r = rect; | ||
932 | spin_lock_irqsave(&fimc->slock, flags); | ||
933 | f->rect = rect; | ||
934 | set_bit(ST_FLITE_CONFIG, &fimc->state); | ||
935 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
936 | |||
937 | return 0; | ||
938 | } | ||
939 | |||
940 | static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = { | ||
941 | .vidioc_querycap = fimc_vidioc_querycap_capture, | ||
942 | .vidioc_enum_fmt_vid_cap_mplane = fimc_lite_enum_fmt_mplane, | ||
943 | .vidioc_try_fmt_vid_cap_mplane = fimc_lite_try_fmt_mplane, | ||
944 | .vidioc_s_fmt_vid_cap_mplane = fimc_lite_s_fmt_mplane, | ||
945 | .vidioc_g_fmt_vid_cap_mplane = fimc_lite_g_fmt_mplane, | ||
946 | .vidioc_g_selection = fimc_lite_g_selection, | ||
947 | .vidioc_s_selection = fimc_lite_s_selection, | ||
948 | .vidioc_reqbufs = fimc_lite_reqbufs, | ||
949 | .vidioc_querybuf = vb2_ioctl_querybuf, | ||
950 | .vidioc_prepare_buf = vb2_ioctl_prepare_buf, | ||
951 | .vidioc_create_bufs = vb2_ioctl_create_bufs, | ||
952 | .vidioc_qbuf = vb2_ioctl_qbuf, | ||
953 | .vidioc_dqbuf = vb2_ioctl_dqbuf, | ||
954 | .vidioc_streamon = fimc_lite_streamon, | ||
955 | .vidioc_streamoff = fimc_lite_streamoff, | ||
956 | }; | ||
957 | |||
958 | /* Capture subdev media entity operations */ | ||
959 | static int fimc_lite_link_setup(struct media_entity *entity, | ||
960 | const struct media_pad *local, | ||
961 | const struct media_pad *remote, u32 flags) | ||
962 | { | ||
963 | struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
964 | struct fimc_lite *fimc = v4l2_get_subdevdata(sd); | ||
965 | unsigned int remote_ent_type = media_entity_type(remote->entity); | ||
966 | int ret = 0; | ||
967 | |||
968 | if (WARN_ON(fimc == NULL)) | ||
969 | return 0; | ||
970 | |||
971 | v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x\n", | ||
972 | __func__, remote->entity->name, local->entity->name, | ||
973 | flags, fimc->source_subdev_grp_id); | ||
974 | |||
975 | mutex_lock(&fimc->lock); | ||
976 | |||
977 | switch (local->index) { | ||
978 | case FLITE_SD_PAD_SINK: | ||
979 | if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) { | ||
980 | ret = -EINVAL; | ||
981 | break; | ||
982 | } | ||
983 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
984 | if (fimc->source_subdev_grp_id == 0) | ||
985 | fimc->source_subdev_grp_id = sd->grp_id; | ||
986 | else | ||
987 | ret = -EBUSY; | ||
988 | } else { | ||
989 | fimc->source_subdev_grp_id = 0; | ||
990 | fimc->sensor = NULL; | ||
991 | } | ||
992 | break; | ||
993 | |||
994 | case FLITE_SD_PAD_SOURCE_DMA: | ||
995 | if (!(flags & MEDIA_LNK_FL_ENABLED)) | ||
996 | atomic_set(&fimc->out_path, FIMC_IO_NONE); | ||
997 | else if (remote_ent_type == MEDIA_ENT_T_DEVNODE) | ||
998 | atomic_set(&fimc->out_path, FIMC_IO_DMA); | ||
999 | else | ||
1000 | ret = -EINVAL; | ||
1001 | break; | ||
1002 | |||
1003 | case FLITE_SD_PAD_SOURCE_ISP: | ||
1004 | if (!(flags & MEDIA_LNK_FL_ENABLED)) | ||
1005 | atomic_set(&fimc->out_path, FIMC_IO_NONE); | ||
1006 | else if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) | ||
1007 | atomic_set(&fimc->out_path, FIMC_IO_ISP); | ||
1008 | else | ||
1009 | ret = -EINVAL; | ||
1010 | break; | ||
1011 | |||
1012 | default: | ||
1013 | v4l2_err(sd, "Invalid pad index\n"); | ||
1014 | ret = -EINVAL; | ||
1015 | } | ||
1016 | mb(); | ||
1017 | |||
1018 | mutex_unlock(&fimc->lock); | ||
1019 | return ret; | ||
1020 | } | ||
1021 | |||
1022 | static const struct media_entity_operations fimc_lite_subdev_media_ops = { | ||
1023 | .link_setup = fimc_lite_link_setup, | ||
1024 | }; | ||
1025 | |||
1026 | static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd, | ||
1027 | struct v4l2_subdev_fh *fh, | ||
1028 | struct v4l2_subdev_mbus_code_enum *code) | ||
1029 | { | ||
1030 | const struct fimc_fmt *fmt; | ||
1031 | |||
1032 | fmt = fimc_lite_find_format(NULL, NULL, 0, code->index); | ||
1033 | if (!fmt) | ||
1034 | return -EINVAL; | ||
1035 | code->code = fmt->mbus_code; | ||
1036 | return 0; | ||
1037 | } | ||
1038 | |||
1039 | static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, | ||
1040 | struct v4l2_subdev_fh *fh, | ||
1041 | struct v4l2_subdev_format *fmt) | ||
1042 | { | ||
1043 | struct fimc_lite *fimc = v4l2_get_subdevdata(sd); | ||
1044 | struct v4l2_mbus_framefmt *mf = &fmt->format; | ||
1045 | struct flite_frame *f = &fimc->inp_frame; | ||
1046 | |||
1047 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1048 | mf = v4l2_subdev_get_try_format(fh, fmt->pad); | ||
1049 | fmt->format = *mf; | ||
1050 | return 0; | ||
1051 | } | ||
1052 | mf->colorspace = V4L2_COLORSPACE_JPEG; | ||
1053 | |||
1054 | mutex_lock(&fimc->lock); | ||
1055 | mf->code = f->fmt->mbus_code; | ||
1056 | |||
1057 | if (fmt->pad == FLITE_SD_PAD_SINK) { | ||
1058 | /* full camera input frame size */ | ||
1059 | mf->width = f->f_width; | ||
1060 | mf->height = f->f_height; | ||
1061 | } else { | ||
1062 | /* crop size */ | ||
1063 | mf->width = f->rect.width; | ||
1064 | mf->height = f->rect.height; | ||
1065 | } | ||
1066 | mutex_unlock(&fimc->lock); | ||
1067 | return 0; | ||
1068 | } | ||
1069 | |||
1070 | static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, | ||
1071 | struct v4l2_subdev_fh *fh, | ||
1072 | struct v4l2_subdev_format *fmt) | ||
1073 | { | ||
1074 | struct fimc_lite *fimc = v4l2_get_subdevdata(sd); | ||
1075 | struct v4l2_mbus_framefmt *mf = &fmt->format; | ||
1076 | struct flite_frame *sink = &fimc->inp_frame; | ||
1077 | struct flite_frame *source = &fimc->out_frame; | ||
1078 | const struct fimc_fmt *ffmt; | ||
1079 | |||
1080 | v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d\n", | ||
1081 | fmt->pad, mf->code, mf->width, mf->height); | ||
1082 | |||
1083 | mf->colorspace = V4L2_COLORSPACE_JPEG; | ||
1084 | mutex_lock(&fimc->lock); | ||
1085 | |||
1086 | if ((atomic_read(&fimc->out_path) == FIMC_IO_ISP && | ||
1087 | sd->entity.stream_count > 0) || | ||
1088 | (atomic_read(&fimc->out_path) == FIMC_IO_DMA && | ||
1089 | vb2_is_busy(&fimc->vb_queue))) { | ||
1090 | mutex_unlock(&fimc->lock); | ||
1091 | return -EBUSY; | ||
1092 | } | ||
1093 | |||
1094 | ffmt = fimc_lite_try_format(fimc, &mf->width, &mf->height, | ||
1095 | &mf->code, NULL, fmt->pad); | ||
1096 | |||
1097 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1098 | mf = v4l2_subdev_get_try_format(fh, fmt->pad); | ||
1099 | *mf = fmt->format; | ||
1100 | mutex_unlock(&fimc->lock); | ||
1101 | return 0; | ||
1102 | } | ||
1103 | |||
1104 | if (fmt->pad == FLITE_SD_PAD_SINK) { | ||
1105 | sink->f_width = mf->width; | ||
1106 | sink->f_height = mf->height; | ||
1107 | sink->fmt = ffmt; | ||
1108 | /* Set sink crop rectangle */ | ||
1109 | sink->rect.width = mf->width; | ||
1110 | sink->rect.height = mf->height; | ||
1111 | sink->rect.left = 0; | ||
1112 | sink->rect.top = 0; | ||
1113 | /* Reset source format and crop rectangle */ | ||
1114 | source->rect = sink->rect; | ||
1115 | source->f_width = mf->width; | ||
1116 | source->f_height = mf->height; | ||
1117 | } else { | ||
1118 | /* Allow changing format only on sink pad */ | ||
1119 | mf->code = sink->fmt->mbus_code; | ||
1120 | mf->width = sink->rect.width; | ||
1121 | mf->height = sink->rect.height; | ||
1122 | } | ||
1123 | |||
1124 | mutex_unlock(&fimc->lock); | ||
1125 | return 0; | ||
1126 | } | ||
1127 | |||
1128 | static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, | ||
1129 | struct v4l2_subdev_fh *fh, | ||
1130 | struct v4l2_subdev_selection *sel) | ||
1131 | { | ||
1132 | struct fimc_lite *fimc = v4l2_get_subdevdata(sd); | ||
1133 | struct flite_frame *f = &fimc->inp_frame; | ||
1134 | |||
1135 | if ((sel->target != V4L2_SEL_TGT_CROP && | ||
1136 | sel->target != V4L2_SEL_TGT_CROP_BOUNDS) || | ||
1137 | sel->pad != FLITE_SD_PAD_SINK) | ||
1138 | return -EINVAL; | ||
1139 | |||
1140 | if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1141 | sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad); | ||
1142 | return 0; | ||
1143 | } | ||
1144 | |||
1145 | mutex_lock(&fimc->lock); | ||
1146 | if (sel->target == V4L2_SEL_TGT_CROP) { | ||
1147 | sel->r = f->rect; | ||
1148 | } else { | ||
1149 | sel->r.left = 0; | ||
1150 | sel->r.top = 0; | ||
1151 | sel->r.width = f->f_width; | ||
1152 | sel->r.height = f->f_height; | ||
1153 | } | ||
1154 | mutex_unlock(&fimc->lock); | ||
1155 | |||
1156 | v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n", | ||
1157 | __func__, f->rect.left, f->rect.top, f->rect.width, | ||
1158 | f->rect.height, f->f_width, f->f_height); | ||
1159 | |||
1160 | return 0; | ||
1161 | } | ||
1162 | |||
1163 | static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, | ||
1164 | struct v4l2_subdev_fh *fh, | ||
1165 | struct v4l2_subdev_selection *sel) | ||
1166 | { | ||
1167 | struct fimc_lite *fimc = v4l2_get_subdevdata(sd); | ||
1168 | struct flite_frame *f = &fimc->inp_frame; | ||
1169 | int ret = 0; | ||
1170 | |||
1171 | if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != FLITE_SD_PAD_SINK) | ||
1172 | return -EINVAL; | ||
1173 | |||
1174 | mutex_lock(&fimc->lock); | ||
1175 | fimc_lite_try_crop(fimc, &sel->r); | ||
1176 | |||
1177 | if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1178 | *v4l2_subdev_get_try_crop(fh, sel->pad) = sel->r; | ||
1179 | } else { | ||
1180 | unsigned long flags; | ||
1181 | spin_lock_irqsave(&fimc->slock, flags); | ||
1182 | f->rect = sel->r; | ||
1183 | /* Same crop rectangle on the source pad */ | ||
1184 | fimc->out_frame.rect = sel->r; | ||
1185 | set_bit(ST_FLITE_CONFIG, &fimc->state); | ||
1186 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
1187 | } | ||
1188 | mutex_unlock(&fimc->lock); | ||
1189 | |||
1190 | v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n", | ||
1191 | __func__, f->rect.left, f->rect.top, f->rect.width, | ||
1192 | f->rect.height, f->f_width, f->f_height); | ||
1193 | |||
1194 | return ret; | ||
1195 | } | ||
1196 | |||
1197 | static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on) | ||
1198 | { | ||
1199 | struct fimc_lite *fimc = v4l2_get_subdevdata(sd); | ||
1200 | unsigned long flags; | ||
1201 | int ret; | ||
1202 | |||
1203 | /* | ||
1204 | * Find sensor subdev linked to FIMC-LITE directly or through | ||
1205 | * MIPI-CSIS. This is required for configuration where FIMC-LITE | ||
1206 | * is used as a subdev only and feeds data internally to FIMC-IS. | ||
1207 | * The pipeline links are protected through entity.stream_count | ||
1208 | * so there is no need to take the media graph mutex here. | ||
1209 | */ | ||
1210 | fimc->sensor = __find_remote_sensor(&sd->entity); | ||
1211 | |||
1212 | if (atomic_read(&fimc->out_path) != FIMC_IO_ISP) | ||
1213 | return -ENOIOCTLCMD; | ||
1214 | |||
1215 | mutex_lock(&fimc->lock); | ||
1216 | if (on) { | ||
1217 | flite_hw_reset(fimc); | ||
1218 | ret = fimc_lite_hw_init(fimc, true); | ||
1219 | if (!ret) { | ||
1220 | spin_lock_irqsave(&fimc->slock, flags); | ||
1221 | flite_hw_capture_start(fimc); | ||
1222 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
1223 | } | ||
1224 | } else { | ||
1225 | set_bit(ST_FLITE_OFF, &fimc->state); | ||
1226 | |||
1227 | spin_lock_irqsave(&fimc->slock, flags); | ||
1228 | flite_hw_capture_stop(fimc); | ||
1229 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
1230 | |||
1231 | ret = wait_event_timeout(fimc->irq_queue, | ||
1232 | !test_bit(ST_FLITE_OFF, &fimc->state), | ||
1233 | msecs_to_jiffies(200)); | ||
1234 | if (ret == 0) | ||
1235 | v4l2_err(sd, "s_stream(0) timeout\n"); | ||
1236 | clear_bit(ST_FLITE_RUN, &fimc->state); | ||
1237 | } | ||
1238 | |||
1239 | mutex_unlock(&fimc->lock); | ||
1240 | return ret; | ||
1241 | } | ||
1242 | |||
1243 | static int fimc_lite_log_status(struct v4l2_subdev *sd) | ||
1244 | { | ||
1245 | struct fimc_lite *fimc = v4l2_get_subdevdata(sd); | ||
1246 | |||
1247 | flite_hw_dump_regs(fimc, __func__); | ||
1248 | return 0; | ||
1249 | } | ||
1250 | |||
1251 | static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) | ||
1252 | { | ||
1253 | struct fimc_lite *fimc = v4l2_get_subdevdata(sd); | ||
1254 | struct vb2_queue *q = &fimc->vb_queue; | ||
1255 | struct video_device *vfd = &fimc->vfd; | ||
1256 | int ret; | ||
1257 | |||
1258 | memset(vfd, 0, sizeof(*vfd)); | ||
1259 | |||
1260 | fimc->inp_frame.fmt = &fimc_lite_formats[0]; | ||
1261 | fimc->out_frame.fmt = &fimc_lite_formats[0]; | ||
1262 | atomic_set(&fimc->out_path, FIMC_IO_DMA); | ||
1263 | |||
1264 | snprintf(vfd->name, sizeof(vfd->name), "fimc-lite.%d.capture", | ||
1265 | fimc->index); | ||
1266 | |||
1267 | vfd->fops = &fimc_lite_fops; | ||
1268 | vfd->ioctl_ops = &fimc_lite_ioctl_ops; | ||
1269 | vfd->v4l2_dev = sd->v4l2_dev; | ||
1270 | vfd->minor = -1; | ||
1271 | vfd->release = video_device_release_empty; | ||
1272 | vfd->queue = q; | ||
1273 | fimc->reqbufs_count = 0; | ||
1274 | |||
1275 | INIT_LIST_HEAD(&fimc->pending_buf_q); | ||
1276 | INIT_LIST_HEAD(&fimc->active_buf_q); | ||
1277 | |||
1278 | memset(q, 0, sizeof(*q)); | ||
1279 | q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | ||
1280 | q->io_modes = VB2_MMAP | VB2_USERPTR; | ||
1281 | q->ops = &fimc_lite_qops; | ||
1282 | q->mem_ops = &vb2_dma_contig_memops; | ||
1283 | q->buf_struct_size = sizeof(struct flite_buffer); | ||
1284 | q->drv_priv = fimc; | ||
1285 | q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; | ||
1286 | q->lock = &fimc->lock; | ||
1287 | |||
1288 | ret = vb2_queue_init(q); | ||
1289 | if (ret < 0) | ||
1290 | return ret; | ||
1291 | |||
1292 | fimc->vd_pad.flags = MEDIA_PAD_FL_SINK; | ||
1293 | ret = media_entity_init(&vfd->entity, 1, &fimc->vd_pad, 0); | ||
1294 | if (ret < 0) | ||
1295 | return ret; | ||
1296 | |||
1297 | video_set_drvdata(vfd, fimc); | ||
1298 | fimc->pipeline_ops = v4l2_get_subdev_hostdata(sd); | ||
1299 | |||
1300 | ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); | ||
1301 | if (ret < 0) { | ||
1302 | media_entity_cleanup(&vfd->entity); | ||
1303 | fimc->pipeline_ops = NULL; | ||
1304 | return ret; | ||
1305 | } | ||
1306 | |||
1307 | v4l2_info(sd->v4l2_dev, "Registered %s as /dev/%s\n", | ||
1308 | vfd->name, video_device_node_name(vfd)); | ||
1309 | return 0; | ||
1310 | } | ||
1311 | |||
1312 | static void fimc_lite_subdev_unregistered(struct v4l2_subdev *sd) | ||
1313 | { | ||
1314 | struct fimc_lite *fimc = v4l2_get_subdevdata(sd); | ||
1315 | |||
1316 | if (fimc == NULL) | ||
1317 | return; | ||
1318 | |||
1319 | if (video_is_registered(&fimc->vfd)) { | ||
1320 | video_unregister_device(&fimc->vfd); | ||
1321 | media_entity_cleanup(&fimc->vfd.entity); | ||
1322 | fimc->pipeline_ops = NULL; | ||
1323 | } | ||
1324 | } | ||
1325 | |||
1326 | static const struct v4l2_subdev_internal_ops fimc_lite_subdev_internal_ops = { | ||
1327 | .registered = fimc_lite_subdev_registered, | ||
1328 | .unregistered = fimc_lite_subdev_unregistered, | ||
1329 | }; | ||
1330 | |||
1331 | static const struct v4l2_subdev_pad_ops fimc_lite_subdev_pad_ops = { | ||
1332 | .enum_mbus_code = fimc_lite_subdev_enum_mbus_code, | ||
1333 | .get_selection = fimc_lite_subdev_get_selection, | ||
1334 | .set_selection = fimc_lite_subdev_set_selection, | ||
1335 | .get_fmt = fimc_lite_subdev_get_fmt, | ||
1336 | .set_fmt = fimc_lite_subdev_set_fmt, | ||
1337 | }; | ||
1338 | |||
1339 | static const struct v4l2_subdev_video_ops fimc_lite_subdev_video_ops = { | ||
1340 | .s_stream = fimc_lite_subdev_s_stream, | ||
1341 | }; | ||
1342 | |||
1343 | static const struct v4l2_subdev_core_ops fimc_lite_core_ops = { | ||
1344 | .log_status = fimc_lite_log_status, | ||
1345 | }; | ||
1346 | |||
1347 | static struct v4l2_subdev_ops fimc_lite_subdev_ops = { | ||
1348 | .core = &fimc_lite_core_ops, | ||
1349 | .video = &fimc_lite_subdev_video_ops, | ||
1350 | .pad = &fimc_lite_subdev_pad_ops, | ||
1351 | }; | ||
1352 | |||
1353 | static int fimc_lite_s_ctrl(struct v4l2_ctrl *ctrl) | ||
1354 | { | ||
1355 | struct fimc_lite *fimc = container_of(ctrl->handler, struct fimc_lite, | ||
1356 | ctrl_handler); | ||
1357 | set_bit(ST_FLITE_CONFIG, &fimc->state); | ||
1358 | return 0; | ||
1359 | } | ||
1360 | |||
1361 | static const struct v4l2_ctrl_ops fimc_lite_ctrl_ops = { | ||
1362 | .s_ctrl = fimc_lite_s_ctrl, | ||
1363 | }; | ||
1364 | |||
1365 | static const struct v4l2_ctrl_config fimc_lite_ctrl = { | ||
1366 | .ops = &fimc_lite_ctrl_ops, | ||
1367 | .id = V4L2_CTRL_CLASS_USER | 0x1001, | ||
1368 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
1369 | .name = "Test Pattern 640x480", | ||
1370 | .step = 1, | ||
1371 | }; | ||
1372 | |||
1373 | static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) | ||
1374 | { | ||
1375 | struct v4l2_ctrl_handler *handler = &fimc->ctrl_handler; | ||
1376 | struct v4l2_subdev *sd = &fimc->subdev; | ||
1377 | int ret; | ||
1378 | |||
1379 | v4l2_subdev_init(sd, &fimc_lite_subdev_ops); | ||
1380 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1381 | snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index); | ||
1382 | |||
1383 | fimc->subdev_pads[FLITE_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
1384 | fimc->subdev_pads[FLITE_SD_PAD_SOURCE_DMA].flags = MEDIA_PAD_FL_SOURCE; | ||
1385 | fimc->subdev_pads[FLITE_SD_PAD_SOURCE_ISP].flags = MEDIA_PAD_FL_SOURCE; | ||
1386 | ret = media_entity_init(&sd->entity, FLITE_SD_PADS_NUM, | ||
1387 | fimc->subdev_pads, 0); | ||
1388 | if (ret) | ||
1389 | return ret; | ||
1390 | |||
1391 | v4l2_ctrl_handler_init(handler, 1); | ||
1392 | fimc->test_pattern = v4l2_ctrl_new_custom(handler, &fimc_lite_ctrl, | ||
1393 | NULL); | ||
1394 | if (handler->error) { | ||
1395 | media_entity_cleanup(&sd->entity); | ||
1396 | return handler->error; | ||
1397 | } | ||
1398 | |||
1399 | sd->ctrl_handler = handler; | ||
1400 | sd->internal_ops = &fimc_lite_subdev_internal_ops; | ||
1401 | sd->entity.ops = &fimc_lite_subdev_media_ops; | ||
1402 | sd->owner = THIS_MODULE; | ||
1403 | v4l2_set_subdevdata(sd, fimc); | ||
1404 | |||
1405 | return 0; | ||
1406 | } | ||
1407 | |||
1408 | static void fimc_lite_unregister_capture_subdev(struct fimc_lite *fimc) | ||
1409 | { | ||
1410 | struct v4l2_subdev *sd = &fimc->subdev; | ||
1411 | |||
1412 | v4l2_device_unregister_subdev(sd); | ||
1413 | media_entity_cleanup(&sd->entity); | ||
1414 | v4l2_ctrl_handler_free(&fimc->ctrl_handler); | ||
1415 | v4l2_set_subdevdata(sd, NULL); | ||
1416 | } | ||
1417 | |||
1418 | static void fimc_lite_clk_put(struct fimc_lite *fimc) | ||
1419 | { | ||
1420 | if (IS_ERR_OR_NULL(fimc->clock)) | ||
1421 | return; | ||
1422 | |||
1423 | clk_unprepare(fimc->clock); | ||
1424 | clk_put(fimc->clock); | ||
1425 | fimc->clock = NULL; | ||
1426 | } | ||
1427 | |||
1428 | static int fimc_lite_clk_get(struct fimc_lite *fimc) | ||
1429 | { | ||
1430 | int ret; | ||
1431 | |||
1432 | fimc->clock = clk_get(&fimc->pdev->dev, FLITE_CLK_NAME); | ||
1433 | if (IS_ERR(fimc->clock)) | ||
1434 | return PTR_ERR(fimc->clock); | ||
1435 | |||
1436 | ret = clk_prepare(fimc->clock); | ||
1437 | if (ret < 0) { | ||
1438 | clk_put(fimc->clock); | ||
1439 | fimc->clock = NULL; | ||
1440 | } | ||
1441 | return ret; | ||
1442 | } | ||
1443 | |||
1444 | static const struct of_device_id flite_of_match[]; | ||
1445 | |||
1446 | static int fimc_lite_probe(struct platform_device *pdev) | ||
1447 | { | ||
1448 | struct flite_drvdata *drv_data = NULL; | ||
1449 | struct device *dev = &pdev->dev; | ||
1450 | const struct of_device_id *of_id; | ||
1451 | struct fimc_lite *fimc; | ||
1452 | struct resource *res; | ||
1453 | int ret; | ||
1454 | |||
1455 | fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL); | ||
1456 | if (!fimc) | ||
1457 | return -ENOMEM; | ||
1458 | |||
1459 | if (dev->of_node) { | ||
1460 | of_id = of_match_node(flite_of_match, dev->of_node); | ||
1461 | if (of_id) | ||
1462 | drv_data = (struct flite_drvdata *)of_id->data; | ||
1463 | fimc->index = of_alias_get_id(dev->of_node, "fimc-lite"); | ||
1464 | } else { | ||
1465 | drv_data = fimc_lite_get_drvdata(pdev); | ||
1466 | fimc->index = pdev->id; | ||
1467 | } | ||
1468 | |||
1469 | if (!drv_data || fimc->index < 0 || fimc->index >= FIMC_LITE_MAX_DEVS) | ||
1470 | return -EINVAL; | ||
1471 | |||
1472 | fimc->dd = drv_data; | ||
1473 | fimc->pdev = pdev; | ||
1474 | |||
1475 | init_waitqueue_head(&fimc->irq_queue); | ||
1476 | spin_lock_init(&fimc->slock); | ||
1477 | mutex_init(&fimc->lock); | ||
1478 | |||
1479 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1480 | fimc->regs = devm_ioremap_resource(dev, res); | ||
1481 | if (IS_ERR(fimc->regs)) | ||
1482 | return PTR_ERR(fimc->regs); | ||
1483 | |||
1484 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
1485 | if (res == NULL) { | ||
1486 | dev_err(dev, "Failed to get IRQ resource\n"); | ||
1487 | return -ENXIO; | ||
1488 | } | ||
1489 | |||
1490 | ret = fimc_lite_clk_get(fimc); | ||
1491 | if (ret) | ||
1492 | return ret; | ||
1493 | |||
1494 | ret = devm_request_irq(dev, res->start, flite_irq_handler, | ||
1495 | 0, dev_name(dev), fimc); | ||
1496 | if (ret) { | ||
1497 | dev_err(dev, "Failed to install irq (%d)\n", ret); | ||
1498 | goto err_clk; | ||
1499 | } | ||
1500 | |||
1501 | /* The video node will be created within the subdev's registered() op */ | ||
1502 | ret = fimc_lite_create_capture_subdev(fimc); | ||
1503 | if (ret) | ||
1504 | goto err_clk; | ||
1505 | |||
1506 | platform_set_drvdata(pdev, fimc); | ||
1507 | pm_runtime_enable(dev); | ||
1508 | ret = pm_runtime_get_sync(dev); | ||
1509 | if (ret < 0) | ||
1510 | goto err_sd; | ||
1511 | |||
1512 | fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); | ||
1513 | if (IS_ERR(fimc->alloc_ctx)) { | ||
1514 | ret = PTR_ERR(fimc->alloc_ctx); | ||
1515 | goto err_pm; | ||
1516 | } | ||
1517 | pm_runtime_put(dev); | ||
1518 | |||
1519 | dev_dbg(dev, "FIMC-LITE.%d registered successfully\n", | ||
1520 | fimc->index); | ||
1521 | return 0; | ||
1522 | err_pm: | ||
1523 | pm_runtime_put(dev); | ||
1524 | err_sd: | ||
1525 | fimc_lite_unregister_capture_subdev(fimc); | ||
1526 | err_clk: | ||
1527 | fimc_lite_clk_put(fimc); | ||
1528 | return ret; | ||
1529 | } | ||
1530 | |||
1531 | static int fimc_lite_runtime_resume(struct device *dev) | ||
1532 | { | ||
1533 | struct fimc_lite *fimc = dev_get_drvdata(dev); | ||
1534 | |||
1535 | clk_enable(fimc->clock); | ||
1536 | return 0; | ||
1537 | } | ||
1538 | |||
1539 | static int fimc_lite_runtime_suspend(struct device *dev) | ||
1540 | { | ||
1541 | struct fimc_lite *fimc = dev_get_drvdata(dev); | ||
1542 | |||
1543 | clk_disable(fimc->clock); | ||
1544 | return 0; | ||
1545 | } | ||
1546 | |||
1547 | #ifdef CONFIG_PM_SLEEP | ||
1548 | static int fimc_lite_resume(struct device *dev) | ||
1549 | { | ||
1550 | struct fimc_lite *fimc = dev_get_drvdata(dev); | ||
1551 | struct flite_buffer *buf; | ||
1552 | unsigned long flags; | ||
1553 | int i; | ||
1554 | |||
1555 | spin_lock_irqsave(&fimc->slock, flags); | ||
1556 | if (!test_and_clear_bit(ST_LPM, &fimc->state) || | ||
1557 | !test_bit(ST_FLITE_IN_USE, &fimc->state)) { | ||
1558 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
1559 | return 0; | ||
1560 | } | ||
1561 | flite_hw_reset(fimc); | ||
1562 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
1563 | |||
1564 | if (!test_and_clear_bit(ST_FLITE_SUSPENDED, &fimc->state)) | ||
1565 | return 0; | ||
1566 | |||
1567 | INIT_LIST_HEAD(&fimc->active_buf_q); | ||
1568 | fimc_pipeline_call(fimc, open, &fimc->pipeline, | ||
1569 | &fimc->vfd.entity, false); | ||
1570 | fimc_lite_hw_init(fimc, atomic_read(&fimc->out_path) == FIMC_IO_ISP); | ||
1571 | clear_bit(ST_FLITE_SUSPENDED, &fimc->state); | ||
1572 | |||
1573 | for (i = 0; i < fimc->reqbufs_count; i++) { | ||
1574 | if (list_empty(&fimc->pending_buf_q)) | ||
1575 | break; | ||
1576 | buf = fimc_lite_pending_queue_pop(fimc); | ||
1577 | buffer_queue(&buf->vb); | ||
1578 | } | ||
1579 | return 0; | ||
1580 | } | ||
1581 | |||
1582 | static int fimc_lite_suspend(struct device *dev) | ||
1583 | { | ||
1584 | struct fimc_lite *fimc = dev_get_drvdata(dev); | ||
1585 | bool suspend = test_bit(ST_FLITE_IN_USE, &fimc->state); | ||
1586 | int ret; | ||
1587 | |||
1588 | if (test_and_set_bit(ST_LPM, &fimc->state)) | ||
1589 | return 0; | ||
1590 | |||
1591 | ret = fimc_lite_stop_capture(fimc, suspend); | ||
1592 | if (ret < 0 || !fimc_lite_active(fimc)) | ||
1593 | return ret; | ||
1594 | |||
1595 | return fimc_pipeline_call(fimc, close, &fimc->pipeline); | ||
1596 | } | ||
1597 | #endif /* CONFIG_PM_SLEEP */ | ||
1598 | |||
1599 | static int fimc_lite_remove(struct platform_device *pdev) | ||
1600 | { | ||
1601 | struct fimc_lite *fimc = platform_get_drvdata(pdev); | ||
1602 | struct device *dev = &pdev->dev; | ||
1603 | |||
1604 | pm_runtime_disable(dev); | ||
1605 | pm_runtime_set_suspended(dev); | ||
1606 | fimc_lite_unregister_capture_subdev(fimc); | ||
1607 | vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); | ||
1608 | fimc_lite_clk_put(fimc); | ||
1609 | |||
1610 | dev_info(dev, "Driver unloaded\n"); | ||
1611 | return 0; | ||
1612 | } | ||
1613 | |||
1614 | static const struct dev_pm_ops fimc_lite_pm_ops = { | ||
1615 | SET_SYSTEM_SLEEP_PM_OPS(fimc_lite_suspend, fimc_lite_resume) | ||
1616 | SET_RUNTIME_PM_OPS(fimc_lite_runtime_suspend, fimc_lite_runtime_resume, | ||
1617 | NULL) | ||
1618 | }; | ||
1619 | |||
1620 | /* EXYNOS4212, EXYNOS4412 */ | ||
1621 | static struct flite_drvdata fimc_lite_drvdata_exynos4 = { | ||
1622 | .max_width = 8192, | ||
1623 | .max_height = 8192, | ||
1624 | .out_width_align = 8, | ||
1625 | .win_hor_offs_align = 2, | ||
1626 | .out_hor_offs_align = 8, | ||
1627 | }; | ||
1628 | |||
1629 | static struct platform_device_id fimc_lite_driver_ids[] = { | ||
1630 | { | ||
1631 | .name = "exynos-fimc-lite", | ||
1632 | .driver_data = (unsigned long)&fimc_lite_drvdata_exynos4, | ||
1633 | }, | ||
1634 | { /* sentinel */ }, | ||
1635 | }; | ||
1636 | MODULE_DEVICE_TABLE(platform, fimc_lite_driver_ids); | ||
1637 | |||
1638 | static const struct of_device_id flite_of_match[] = { | ||
1639 | { | ||
1640 | .compatible = "samsung,exynos4212-fimc-lite", | ||
1641 | .data = &fimc_lite_drvdata_exynos4, | ||
1642 | }, | ||
1643 | { /* sentinel */ }, | ||
1644 | }; | ||
1645 | MODULE_DEVICE_TABLE(of, flite_of_match); | ||
1646 | |||
1647 | static struct platform_driver fimc_lite_driver = { | ||
1648 | .probe = fimc_lite_probe, | ||
1649 | .remove = fimc_lite_remove, | ||
1650 | .id_table = fimc_lite_driver_ids, | ||
1651 | .driver = { | ||
1652 | .of_match_table = flite_of_match, | ||
1653 | .name = FIMC_LITE_DRV_NAME, | ||
1654 | .owner = THIS_MODULE, | ||
1655 | .pm = &fimc_lite_pm_ops, | ||
1656 | } | ||
1657 | }; | ||
1658 | module_platform_driver(fimc_lite_driver); | ||
1659 | MODULE_LICENSE("GPL"); | ||
1660 | MODULE_ALIAS("platform:" FIMC_LITE_DRV_NAME); | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.h b/drivers/media/platform/exynos4-is/fimc-lite.h new file mode 100644 index 000000000000..47da5e049247 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-lite.h | |||
@@ -0,0 +1,214 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Samsung Electronics Co., Ltd. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef FIMC_LITE_H_ | ||
10 | #define FIMC_LITE_H_ | ||
11 | |||
12 | #include <linux/sizes.h> | ||
13 | #include <linux/io.h> | ||
14 | #include <linux/irqreturn.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/videodev2.h> | ||
20 | |||
21 | #include <media/media-entity.h> | ||
22 | #include <media/videobuf2-core.h> | ||
23 | #include <media/v4l2-ctrls.h> | ||
24 | #include <media/v4l2-device.h> | ||
25 | #include <media/v4l2-mediabus.h> | ||
26 | #include <media/s5p_fimc.h> | ||
27 | |||
28 | #define FIMC_LITE_DRV_NAME "exynos-fimc-lite" | ||
29 | #define FLITE_CLK_NAME "flite" | ||
30 | #define FIMC_LITE_MAX_DEVS 2 | ||
31 | #define FLITE_REQ_BUFS_MIN 2 | ||
32 | |||
33 | /* Bit index definitions for struct fimc_lite::state */ | ||
34 | enum { | ||
35 | ST_FLITE_LPM, | ||
36 | ST_FLITE_PENDING, | ||
37 | ST_FLITE_RUN, | ||
38 | ST_FLITE_STREAM, | ||
39 | ST_FLITE_SUSPENDED, | ||
40 | ST_FLITE_OFF, | ||
41 | ST_FLITE_IN_USE, | ||
42 | ST_FLITE_CONFIG, | ||
43 | ST_SENSOR_STREAM, | ||
44 | }; | ||
45 | |||
46 | #define FLITE_SD_PAD_SINK 0 | ||
47 | #define FLITE_SD_PAD_SOURCE_DMA 1 | ||
48 | #define FLITE_SD_PAD_SOURCE_ISP 2 | ||
49 | #define FLITE_SD_PADS_NUM 3 | ||
50 | |||
51 | struct flite_drvdata { | ||
52 | unsigned short max_width; | ||
53 | unsigned short max_height; | ||
54 | unsigned short out_width_align; | ||
55 | unsigned short win_hor_offs_align; | ||
56 | unsigned short out_hor_offs_align; | ||
57 | }; | ||
58 | |||
59 | #define fimc_lite_get_drvdata(_pdev) \ | ||
60 | ((struct flite_drvdata *) platform_get_device_id(_pdev)->driver_data) | ||
61 | |||
62 | struct fimc_lite_events { | ||
63 | unsigned int data_overflow; | ||
64 | }; | ||
65 | |||
66 | #define FLITE_MAX_PLANES 1 | ||
67 | |||
68 | /** | ||
69 | * struct flite_frame - source/target frame properties | ||
70 | * @f_width: full pixel width | ||
71 | * @f_height: full pixel height | ||
72 | * @rect: crop/composition rectangle | ||
73 | * @fmt: pointer to pixel format description data structure | ||
74 | */ | ||
75 | struct flite_frame { | ||
76 | u16 f_width; | ||
77 | u16 f_height; | ||
78 | struct v4l2_rect rect; | ||
79 | const struct fimc_fmt *fmt; | ||
80 | }; | ||
81 | |||
82 | /** | ||
83 | * struct flite_buffer - video buffer structure | ||
84 | * @vb: vb2 buffer | ||
85 | * @list: list head for the buffers queue | ||
86 | * @paddr: precalculated physical address | ||
87 | */ | ||
88 | struct flite_buffer { | ||
89 | struct vb2_buffer vb; | ||
90 | struct list_head list; | ||
91 | dma_addr_t paddr; | ||
92 | }; | ||
93 | |||
94 | /** | ||
95 | * struct fimc_lite - fimc lite structure | ||
96 | * @pdev: pointer to FIMC-LITE platform device | ||
97 | * @dd: SoC specific driver data structure | ||
98 | * @v4l2_dev: pointer to top the level v4l2_device | ||
99 | * @vfd: video device node | ||
100 | * @fh: v4l2 file handle | ||
101 | * @alloc_ctx: videobuf2 memory allocator context | ||
102 | * @subdev: FIMC-LITE subdev | ||
103 | * @vd_pad: media (sink) pad for the capture video node | ||
104 | * @subdev_pads: the subdev media pads | ||
105 | * @sensor: sensor subdev attached to FIMC-LITE directly or through MIPI-CSIS | ||
106 | * @ctrl_handler: v4l2 control handler | ||
107 | * @test_pattern: test pattern controls | ||
108 | * @index: FIMC-LITE platform device index | ||
109 | * @pipeline: video capture pipeline data structure | ||
110 | * @pipeline_ops: media pipeline ops for the video node driver | ||
111 | * @slock: spinlock protecting this data structure and the hw registers | ||
112 | * @lock: mutex serializing video device and the subdev operations | ||
113 | * @clock: FIMC-LITE gate clock | ||
114 | * @regs: memory mapped io registers | ||
115 | * @irq_queue: interrupt handler waitqueue | ||
116 | * @payload: image size in bytes (w x h x bpp) | ||
117 | * @inp_frame: camera input frame structure | ||
118 | * @out_frame: DMA output frame structure | ||
119 | * @out_path: output data path (DMA or FIFO) | ||
120 | * @source_subdev_grp_id: source subdev group id | ||
121 | * @state: driver state flags | ||
122 | * @pending_buf_q: pending buffers queue head | ||
123 | * @active_buf_q: the queue head of buffers scheduled in hardware | ||
124 | * @vb_queue: vb2 buffers queue | ||
125 | * @active_buf_count: number of video buffers scheduled in hardware | ||
126 | * @frame_count: the captured frames counter | ||
127 | * @reqbufs_count: the number of buffers requested with REQBUFS ioctl | ||
128 | * @ref_count: driver's private reference counter | ||
129 | */ | ||
130 | struct fimc_lite { | ||
131 | struct platform_device *pdev; | ||
132 | struct flite_drvdata *dd; | ||
133 | struct v4l2_device *v4l2_dev; | ||
134 | struct video_device vfd; | ||
135 | struct v4l2_fh fh; | ||
136 | struct vb2_alloc_ctx *alloc_ctx; | ||
137 | struct v4l2_subdev subdev; | ||
138 | struct media_pad vd_pad; | ||
139 | struct media_pad subdev_pads[FLITE_SD_PADS_NUM]; | ||
140 | struct v4l2_subdev *sensor; | ||
141 | struct v4l2_ctrl_handler ctrl_handler; | ||
142 | struct v4l2_ctrl *test_pattern; | ||
143 | int index; | ||
144 | struct fimc_pipeline pipeline; | ||
145 | const struct fimc_pipeline_ops *pipeline_ops; | ||
146 | |||
147 | struct mutex lock; | ||
148 | spinlock_t slock; | ||
149 | |||
150 | struct clk *clock; | ||
151 | void __iomem *regs; | ||
152 | wait_queue_head_t irq_queue; | ||
153 | |||
154 | unsigned long payload[FLITE_MAX_PLANES]; | ||
155 | struct flite_frame inp_frame; | ||
156 | struct flite_frame out_frame; | ||
157 | atomic_t out_path; | ||
158 | unsigned int source_subdev_grp_id; | ||
159 | |||
160 | unsigned long state; | ||
161 | struct list_head pending_buf_q; | ||
162 | struct list_head active_buf_q; | ||
163 | struct vb2_queue vb_queue; | ||
164 | unsigned int frame_count; | ||
165 | unsigned int reqbufs_count; | ||
166 | int ref_count; | ||
167 | |||
168 | struct fimc_lite_events events; | ||
169 | bool streaming; | ||
170 | }; | ||
171 | |||
172 | static inline bool fimc_lite_active(struct fimc_lite *fimc) | ||
173 | { | ||
174 | unsigned long flags; | ||
175 | bool ret; | ||
176 | |||
177 | spin_lock_irqsave(&fimc->slock, flags); | ||
178 | ret = fimc->state & (1 << ST_FLITE_RUN) || | ||
179 | fimc->state & (1 << ST_FLITE_PENDING); | ||
180 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
181 | return ret; | ||
182 | } | ||
183 | |||
184 | static inline void fimc_lite_active_queue_add(struct fimc_lite *dev, | ||
185 | struct flite_buffer *buf) | ||
186 | { | ||
187 | list_add_tail(&buf->list, &dev->active_buf_q); | ||
188 | } | ||
189 | |||
190 | static inline struct flite_buffer *fimc_lite_active_queue_pop( | ||
191 | struct fimc_lite *dev) | ||
192 | { | ||
193 | struct flite_buffer *buf = list_entry(dev->active_buf_q.next, | ||
194 | struct flite_buffer, list); | ||
195 | list_del(&buf->list); | ||
196 | return buf; | ||
197 | } | ||
198 | |||
199 | static inline void fimc_lite_pending_queue_add(struct fimc_lite *dev, | ||
200 | struct flite_buffer *buf) | ||
201 | { | ||
202 | list_add_tail(&buf->list, &dev->pending_buf_q); | ||
203 | } | ||
204 | |||
205 | static inline struct flite_buffer *fimc_lite_pending_queue_pop( | ||
206 | struct fimc_lite *dev) | ||
207 | { | ||
208 | struct flite_buffer *buf = list_entry(dev->pending_buf_q.next, | ||
209 | struct flite_buffer, list); | ||
210 | list_del(&buf->list); | ||
211 | return buf; | ||
212 | } | ||
213 | |||
214 | #endif /* FIMC_LITE_H_ */ | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c new file mode 100644 index 000000000000..bde1f47f7ed3 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c | |||
@@ -0,0 +1,865 @@ | |||
1 | /* | ||
2 | * Samsung S5P/EXYNOS4 SoC series FIMC (video postprocessor) driver | ||
3 | * | ||
4 | * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. | ||
5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published | ||
9 | * by the Free Software Foundation, either version 2 of the License, | ||
10 | * or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/bug.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/device.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/pm_runtime.h> | ||
22 | #include <linux/list.h> | ||
23 | #include <linux/io.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/clk.h> | ||
26 | #include <media/v4l2-ioctl.h> | ||
27 | #include <media/videobuf2-core.h> | ||
28 | #include <media/videobuf2-dma-contig.h> | ||
29 | |||
30 | #include "fimc-core.h" | ||
31 | #include "fimc-reg.h" | ||
32 | #include "media-dev.h" | ||
33 | |||
34 | static unsigned int get_m2m_fmt_flags(unsigned int stream_type) | ||
35 | { | ||
36 | if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | ||
37 | return FMT_FLAGS_M2M_IN; | ||
38 | else | ||
39 | return FMT_FLAGS_M2M_OUT; | ||
40 | } | ||
41 | |||
42 | void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state) | ||
43 | { | ||
44 | struct vb2_buffer *src_vb, *dst_vb; | ||
45 | |||
46 | if (!ctx || !ctx->m2m_ctx) | ||
47 | return; | ||
48 | |||
49 | src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); | ||
50 | dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); | ||
51 | |||
52 | if (src_vb && dst_vb) { | ||
53 | v4l2_m2m_buf_done(src_vb, vb_state); | ||
54 | v4l2_m2m_buf_done(dst_vb, vb_state); | ||
55 | v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev, | ||
56 | ctx->m2m_ctx); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | /* Complete the transaction which has been scheduled for execution. */ | ||
61 | static int fimc_m2m_shutdown(struct fimc_ctx *ctx) | ||
62 | { | ||
63 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
64 | int ret; | ||
65 | |||
66 | if (!fimc_m2m_pending(fimc)) | ||
67 | return 0; | ||
68 | |||
69 | fimc_ctx_state_set(FIMC_CTX_SHUT, ctx); | ||
70 | |||
71 | ret = wait_event_timeout(fimc->irq_queue, | ||
72 | !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), | ||
73 | FIMC_SHUTDOWN_TIMEOUT); | ||
74 | |||
75 | return ret == 0 ? -ETIMEDOUT : ret; | ||
76 | } | ||
77 | |||
78 | static int start_streaming(struct vb2_queue *q, unsigned int count) | ||
79 | { | ||
80 | struct fimc_ctx *ctx = q->drv_priv; | ||
81 | int ret; | ||
82 | |||
83 | ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev); | ||
84 | return ret > 0 ? 0 : ret; | ||
85 | } | ||
86 | |||
87 | static int stop_streaming(struct vb2_queue *q) | ||
88 | { | ||
89 | struct fimc_ctx *ctx = q->drv_priv; | ||
90 | int ret; | ||
91 | |||
92 | ret = fimc_m2m_shutdown(ctx); | ||
93 | if (ret == -ETIMEDOUT) | ||
94 | fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); | ||
95 | |||
96 | pm_runtime_put(&ctx->fimc_dev->pdev->dev); | ||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static void fimc_device_run(void *priv) | ||
101 | { | ||
102 | struct vb2_buffer *src_vb, *dst_vb; | ||
103 | struct fimc_ctx *ctx = priv; | ||
104 | struct fimc_frame *sf, *df; | ||
105 | struct fimc_dev *fimc; | ||
106 | unsigned long flags; | ||
107 | int ret; | ||
108 | |||
109 | if (WARN(!ctx, "Null context\n")) | ||
110 | return; | ||
111 | |||
112 | fimc = ctx->fimc_dev; | ||
113 | spin_lock_irqsave(&fimc->slock, flags); | ||
114 | |||
115 | set_bit(ST_M2M_PEND, &fimc->state); | ||
116 | sf = &ctx->s_frame; | ||
117 | df = &ctx->d_frame; | ||
118 | |||
119 | if (ctx->state & FIMC_PARAMS) { | ||
120 | /* Prepare the DMA offsets for scaler */ | ||
121 | fimc_prepare_dma_offset(ctx, sf); | ||
122 | fimc_prepare_dma_offset(ctx, df); | ||
123 | } | ||
124 | |||
125 | src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); | ||
126 | ret = fimc_prepare_addr(ctx, src_vb, sf, &sf->paddr); | ||
127 | if (ret) | ||
128 | goto dma_unlock; | ||
129 | |||
130 | dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); | ||
131 | ret = fimc_prepare_addr(ctx, dst_vb, df, &df->paddr); | ||
132 | if (ret) | ||
133 | goto dma_unlock; | ||
134 | |||
135 | dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; | ||
136 | |||
137 | /* Reconfigure hardware if the context has changed. */ | ||
138 | if (fimc->m2m.ctx != ctx) { | ||
139 | ctx->state |= FIMC_PARAMS; | ||
140 | fimc->m2m.ctx = ctx; | ||
141 | } | ||
142 | |||
143 | if (ctx->state & FIMC_PARAMS) { | ||
144 | fimc_set_yuv_order(ctx); | ||
145 | fimc_hw_set_input_path(ctx); | ||
146 | fimc_hw_set_in_dma(ctx); | ||
147 | ret = fimc_set_scaler_info(ctx); | ||
148 | if (ret) | ||
149 | goto dma_unlock; | ||
150 | fimc_hw_set_prescaler(ctx); | ||
151 | fimc_hw_set_mainscaler(ctx); | ||
152 | fimc_hw_set_target_format(ctx); | ||
153 | fimc_hw_set_rotation(ctx); | ||
154 | fimc_hw_set_effect(ctx); | ||
155 | fimc_hw_set_out_dma(ctx); | ||
156 | if (fimc->drv_data->alpha_color) | ||
157 | fimc_hw_set_rgb_alpha(ctx); | ||
158 | fimc_hw_set_output_path(ctx); | ||
159 | } | ||
160 | fimc_hw_set_input_addr(fimc, &sf->paddr); | ||
161 | fimc_hw_set_output_addr(fimc, &df->paddr, -1); | ||
162 | |||
163 | fimc_activate_capture(ctx); | ||
164 | ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP); | ||
165 | fimc_hw_activate_input_dma(fimc, true); | ||
166 | |||
167 | dma_unlock: | ||
168 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
169 | } | ||
170 | |||
171 | static void fimc_job_abort(void *priv) | ||
172 | { | ||
173 | fimc_m2m_shutdown(priv); | ||
174 | } | ||
175 | |||
176 | static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, | ||
177 | unsigned int *num_buffers, unsigned int *num_planes, | ||
178 | unsigned int sizes[], void *allocators[]) | ||
179 | { | ||
180 | struct fimc_ctx *ctx = vb2_get_drv_priv(vq); | ||
181 | struct fimc_frame *f; | ||
182 | int i; | ||
183 | |||
184 | f = ctx_get_frame(ctx, vq->type); | ||
185 | if (IS_ERR(f)) | ||
186 | return PTR_ERR(f); | ||
187 | /* | ||
188 | * Return number of non-contigous planes (plane buffers) | ||
189 | * depending on the configured color format. | ||
190 | */ | ||
191 | if (!f->fmt) | ||
192 | return -EINVAL; | ||
193 | |||
194 | *num_planes = f->fmt->memplanes; | ||
195 | for (i = 0; i < f->fmt->memplanes; i++) { | ||
196 | sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8; | ||
197 | allocators[i] = ctx->fimc_dev->alloc_ctx; | ||
198 | } | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int fimc_buf_prepare(struct vb2_buffer *vb) | ||
203 | { | ||
204 | struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); | ||
205 | struct fimc_frame *frame; | ||
206 | int i; | ||
207 | |||
208 | frame = ctx_get_frame(ctx, vb->vb2_queue->type); | ||
209 | if (IS_ERR(frame)) | ||
210 | return PTR_ERR(frame); | ||
211 | |||
212 | for (i = 0; i < frame->fmt->memplanes; i++) | ||
213 | vb2_set_plane_payload(vb, i, frame->payload[i]); | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static void fimc_buf_queue(struct vb2_buffer *vb) | ||
219 | { | ||
220 | struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); | ||
221 | |||
222 | dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); | ||
223 | |||
224 | if (ctx->m2m_ctx) | ||
225 | v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); | ||
226 | } | ||
227 | |||
228 | static void fimc_lock(struct vb2_queue *vq) | ||
229 | { | ||
230 | struct fimc_ctx *ctx = vb2_get_drv_priv(vq); | ||
231 | mutex_lock(&ctx->fimc_dev->lock); | ||
232 | } | ||
233 | |||
234 | static void fimc_unlock(struct vb2_queue *vq) | ||
235 | { | ||
236 | struct fimc_ctx *ctx = vb2_get_drv_priv(vq); | ||
237 | mutex_unlock(&ctx->fimc_dev->lock); | ||
238 | } | ||
239 | |||
240 | static struct vb2_ops fimc_qops = { | ||
241 | .queue_setup = fimc_queue_setup, | ||
242 | .buf_prepare = fimc_buf_prepare, | ||
243 | .buf_queue = fimc_buf_queue, | ||
244 | .wait_prepare = fimc_unlock, | ||
245 | .wait_finish = fimc_lock, | ||
246 | .stop_streaming = stop_streaming, | ||
247 | .start_streaming = start_streaming, | ||
248 | }; | ||
249 | |||
250 | /* | ||
251 | * V4L2 ioctl handlers | ||
252 | */ | ||
253 | static int fimc_m2m_querycap(struct file *file, void *fh, | ||
254 | struct v4l2_capability *cap) | ||
255 | { | ||
256 | struct fimc_dev *fimc = video_drvdata(file); | ||
257 | unsigned int caps; | ||
258 | |||
259 | /* | ||
260 | * This is only a mem-to-mem video device. The capture and output | ||
261 | * device capability flags are left only for backward compatibility | ||
262 | * and are scheduled for removal. | ||
263 | */ | ||
264 | caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE | | ||
265 | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; | ||
266 | |||
267 | __fimc_vidioc_querycap(&fimc->pdev->dev, cap, caps); | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv, | ||
272 | struct v4l2_fmtdesc *f) | ||
273 | { | ||
274 | struct fimc_fmt *fmt; | ||
275 | |||
276 | fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type), | ||
277 | f->index); | ||
278 | if (!fmt) | ||
279 | return -EINVAL; | ||
280 | |||
281 | strncpy(f->description, fmt->name, sizeof(f->description) - 1); | ||
282 | f->pixelformat = fmt->fourcc; | ||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, | ||
287 | struct v4l2_format *f) | ||
288 | { | ||
289 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
290 | struct fimc_frame *frame = ctx_get_frame(ctx, f->type); | ||
291 | |||
292 | if (IS_ERR(frame)) | ||
293 | return PTR_ERR(frame); | ||
294 | |||
295 | __fimc_get_format(frame, f); | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) | ||
300 | { | ||
301 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
302 | const struct fimc_variant *variant = fimc->variant; | ||
303 | struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; | ||
304 | struct fimc_fmt *fmt; | ||
305 | u32 max_w, mod_x, mod_y; | ||
306 | |||
307 | if (!IS_M2M(f->type)) | ||
308 | return -EINVAL; | ||
309 | |||
310 | fmt = fimc_find_format(&pix->pixelformat, NULL, | ||
311 | get_m2m_fmt_flags(f->type), 0); | ||
312 | if (WARN(fmt == NULL, "Pixel format lookup failed")) | ||
313 | return -EINVAL; | ||
314 | |||
315 | if (pix->field == V4L2_FIELD_ANY) | ||
316 | pix->field = V4L2_FIELD_NONE; | ||
317 | else if (pix->field != V4L2_FIELD_NONE) | ||
318 | return -EINVAL; | ||
319 | |||
320 | if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { | ||
321 | max_w = variant->pix_limit->scaler_dis_w; | ||
322 | mod_x = ffs(variant->min_inp_pixsize) - 1; | ||
323 | } else { | ||
324 | max_w = variant->pix_limit->out_rot_dis_w; | ||
325 | mod_x = ffs(variant->min_out_pixsize) - 1; | ||
326 | } | ||
327 | |||
328 | if (tiled_fmt(fmt)) { | ||
329 | mod_x = 6; /* 64 x 32 pixels tile */ | ||
330 | mod_y = 5; | ||
331 | } else { | ||
332 | if (variant->min_vsize_align == 1) | ||
333 | mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1; | ||
334 | else | ||
335 | mod_y = ffs(variant->min_vsize_align) - 1; | ||
336 | } | ||
337 | |||
338 | v4l_bound_align_image(&pix->width, 16, max_w, mod_x, | ||
339 | &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0); | ||
340 | |||
341 | fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp); | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh, | ||
346 | struct v4l2_format *f) | ||
347 | { | ||
348 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
349 | return fimc_try_fmt_mplane(ctx, f); | ||
350 | } | ||
351 | |||
352 | static void __set_frame_format(struct fimc_frame *frame, struct fimc_fmt *fmt, | ||
353 | struct v4l2_pix_format_mplane *pixm) | ||
354 | { | ||
355 | int i; | ||
356 | |||
357 | for (i = 0; i < fmt->colplanes; i++) { | ||
358 | frame->bytesperline[i] = pixm->plane_fmt[i].bytesperline; | ||
359 | frame->payload[i] = pixm->plane_fmt[i].sizeimage; | ||
360 | } | ||
361 | |||
362 | frame->f_width = pixm->width; | ||
363 | frame->f_height = pixm->height; | ||
364 | frame->o_width = pixm->width; | ||
365 | frame->o_height = pixm->height; | ||
366 | frame->width = pixm->width; | ||
367 | frame->height = pixm->height; | ||
368 | frame->offs_h = 0; | ||
369 | frame->offs_v = 0; | ||
370 | frame->fmt = fmt; | ||
371 | } | ||
372 | |||
373 | static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, | ||
374 | struct v4l2_format *f) | ||
375 | { | ||
376 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
377 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
378 | struct fimc_fmt *fmt; | ||
379 | struct vb2_queue *vq; | ||
380 | struct fimc_frame *frame; | ||
381 | int ret; | ||
382 | |||
383 | ret = fimc_try_fmt_mplane(ctx, f); | ||
384 | if (ret) | ||
385 | return ret; | ||
386 | |||
387 | vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); | ||
388 | |||
389 | if (vb2_is_busy(vq)) { | ||
390 | v4l2_err(&fimc->m2m.vfd, "queue (%d) busy\n", f->type); | ||
391 | return -EBUSY; | ||
392 | } | ||
393 | |||
394 | if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | ||
395 | frame = &ctx->s_frame; | ||
396 | else | ||
397 | frame = &ctx->d_frame; | ||
398 | |||
399 | fmt = fimc_find_format(&f->fmt.pix_mp.pixelformat, NULL, | ||
400 | get_m2m_fmt_flags(f->type), 0); | ||
401 | if (!fmt) | ||
402 | return -EINVAL; | ||
403 | |||
404 | __set_frame_format(frame, fmt, &f->fmt.pix_mp); | ||
405 | |||
406 | /* Update RGB Alpha control state and value range */ | ||
407 | fimc_alpha_ctrl_update(ctx); | ||
408 | |||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | static int fimc_m2m_reqbufs(struct file *file, void *fh, | ||
413 | struct v4l2_requestbuffers *reqbufs) | ||
414 | { | ||
415 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
416 | return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); | ||
417 | } | ||
418 | |||
419 | static int fimc_m2m_querybuf(struct file *file, void *fh, | ||
420 | struct v4l2_buffer *buf) | ||
421 | { | ||
422 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
423 | return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); | ||
424 | } | ||
425 | |||
426 | static int fimc_m2m_qbuf(struct file *file, void *fh, | ||
427 | struct v4l2_buffer *buf) | ||
428 | { | ||
429 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
430 | return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); | ||
431 | } | ||
432 | |||
433 | static int fimc_m2m_dqbuf(struct file *file, void *fh, | ||
434 | struct v4l2_buffer *buf) | ||
435 | { | ||
436 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
437 | return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); | ||
438 | } | ||
439 | |||
440 | static int fimc_m2m_expbuf(struct file *file, void *fh, | ||
441 | struct v4l2_exportbuffer *eb) | ||
442 | { | ||
443 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
444 | return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); | ||
445 | } | ||
446 | |||
447 | |||
448 | static int fimc_m2m_streamon(struct file *file, void *fh, | ||
449 | enum v4l2_buf_type type) | ||
450 | { | ||
451 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
452 | return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); | ||
453 | } | ||
454 | |||
455 | static int fimc_m2m_streamoff(struct file *file, void *fh, | ||
456 | enum v4l2_buf_type type) | ||
457 | { | ||
458 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
459 | return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); | ||
460 | } | ||
461 | |||
462 | static int fimc_m2m_cropcap(struct file *file, void *fh, | ||
463 | struct v4l2_cropcap *cr) | ||
464 | { | ||
465 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
466 | struct fimc_frame *frame; | ||
467 | |||
468 | frame = ctx_get_frame(ctx, cr->type); | ||
469 | if (IS_ERR(frame)) | ||
470 | return PTR_ERR(frame); | ||
471 | |||
472 | cr->bounds.left = 0; | ||
473 | cr->bounds.top = 0; | ||
474 | cr->bounds.width = frame->o_width; | ||
475 | cr->bounds.height = frame->o_height; | ||
476 | cr->defrect = cr->bounds; | ||
477 | |||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) | ||
482 | { | ||
483 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
484 | struct fimc_frame *frame; | ||
485 | |||
486 | frame = ctx_get_frame(ctx, cr->type); | ||
487 | if (IS_ERR(frame)) | ||
488 | return PTR_ERR(frame); | ||
489 | |||
490 | cr->c.left = frame->offs_h; | ||
491 | cr->c.top = frame->offs_v; | ||
492 | cr->c.width = frame->width; | ||
493 | cr->c.height = frame->height; | ||
494 | |||
495 | return 0; | ||
496 | } | ||
497 | |||
498 | static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) | ||
499 | { | ||
500 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
501 | struct fimc_frame *f; | ||
502 | u32 min_size, halign, depth = 0; | ||
503 | int i; | ||
504 | |||
505 | if (cr->c.top < 0 || cr->c.left < 0) { | ||
506 | v4l2_err(&fimc->m2m.vfd, | ||
507 | "doesn't support negative values for top & left\n"); | ||
508 | return -EINVAL; | ||
509 | } | ||
510 | if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | ||
511 | f = &ctx->d_frame; | ||
512 | else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | ||
513 | f = &ctx->s_frame; | ||
514 | else | ||
515 | return -EINVAL; | ||
516 | |||
517 | min_size = (f == &ctx->s_frame) ? | ||
518 | fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize; | ||
519 | |||
520 | /* Get pixel alignment constraints. */ | ||
521 | if (fimc->variant->min_vsize_align == 1) | ||
522 | halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1; | ||
523 | else | ||
524 | halign = ffs(fimc->variant->min_vsize_align) - 1; | ||
525 | |||
526 | for (i = 0; i < f->fmt->colplanes; i++) | ||
527 | depth += f->fmt->depth[i]; | ||
528 | |||
529 | v4l_bound_align_image(&cr->c.width, min_size, f->o_width, | ||
530 | ffs(min_size) - 1, | ||
531 | &cr->c.height, min_size, f->o_height, | ||
532 | halign, 64/(ALIGN(depth, 8))); | ||
533 | |||
534 | /* adjust left/top if cropping rectangle is out of bounds */ | ||
535 | if (cr->c.left + cr->c.width > f->o_width) | ||
536 | cr->c.left = f->o_width - cr->c.width; | ||
537 | if (cr->c.top + cr->c.height > f->o_height) | ||
538 | cr->c.top = f->o_height - cr->c.height; | ||
539 | |||
540 | cr->c.left = round_down(cr->c.left, min_size); | ||
541 | cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align); | ||
542 | |||
543 | dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", | ||
544 | cr->c.left, cr->c.top, cr->c.width, cr->c.height, | ||
545 | f->f_width, f->f_height); | ||
546 | |||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | static int fimc_m2m_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop) | ||
551 | { | ||
552 | struct fimc_ctx *ctx = fh_to_ctx(fh); | ||
553 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
554 | struct v4l2_crop cr = *crop; | ||
555 | struct fimc_frame *f; | ||
556 | int ret; | ||
557 | |||
558 | ret = fimc_m2m_try_crop(ctx, &cr); | ||
559 | if (ret) | ||
560 | return ret; | ||
561 | |||
562 | f = (cr.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? | ||
563 | &ctx->s_frame : &ctx->d_frame; | ||
564 | |||
565 | /* Check to see if scaling ratio is within supported range */ | ||
566 | if (cr.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { | ||
567 | ret = fimc_check_scaler_ratio(ctx, cr.c.width, | ||
568 | cr.c.height, ctx->d_frame.width, | ||
569 | ctx->d_frame.height, ctx->rotation); | ||
570 | } else { | ||
571 | ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, | ||
572 | ctx->s_frame.height, cr.c.width, | ||
573 | cr.c.height, ctx->rotation); | ||
574 | } | ||
575 | if (ret) { | ||
576 | v4l2_err(&fimc->m2m.vfd, "Out of scaler range\n"); | ||
577 | return -EINVAL; | ||
578 | } | ||
579 | |||
580 | f->offs_h = cr.c.left; | ||
581 | f->offs_v = cr.c.top; | ||
582 | f->width = cr.c.width; | ||
583 | f->height = cr.c.height; | ||
584 | |||
585 | fimc_ctx_state_set(FIMC_PARAMS, ctx); | ||
586 | |||
587 | return 0; | ||
588 | } | ||
589 | |||
590 | static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { | ||
591 | .vidioc_querycap = fimc_m2m_querycap, | ||
592 | .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane, | ||
593 | .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane, | ||
594 | .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane, | ||
595 | .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane, | ||
596 | .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane, | ||
597 | .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane, | ||
598 | .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane, | ||
599 | .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane, | ||
600 | .vidioc_reqbufs = fimc_m2m_reqbufs, | ||
601 | .vidioc_querybuf = fimc_m2m_querybuf, | ||
602 | .vidioc_qbuf = fimc_m2m_qbuf, | ||
603 | .vidioc_dqbuf = fimc_m2m_dqbuf, | ||
604 | .vidioc_expbuf = fimc_m2m_expbuf, | ||
605 | .vidioc_streamon = fimc_m2m_streamon, | ||
606 | .vidioc_streamoff = fimc_m2m_streamoff, | ||
607 | .vidioc_g_crop = fimc_m2m_g_crop, | ||
608 | .vidioc_s_crop = fimc_m2m_s_crop, | ||
609 | .vidioc_cropcap = fimc_m2m_cropcap | ||
610 | |||
611 | }; | ||
612 | |||
613 | static int queue_init(void *priv, struct vb2_queue *src_vq, | ||
614 | struct vb2_queue *dst_vq) | ||
615 | { | ||
616 | struct fimc_ctx *ctx = priv; | ||
617 | int ret; | ||
618 | |||
619 | src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | ||
620 | src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; | ||
621 | src_vq->drv_priv = ctx; | ||
622 | src_vq->ops = &fimc_qops; | ||
623 | src_vq->mem_ops = &vb2_dma_contig_memops; | ||
624 | src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); | ||
625 | src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; | ||
626 | |||
627 | ret = vb2_queue_init(src_vq); | ||
628 | if (ret) | ||
629 | return ret; | ||
630 | |||
631 | dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | ||
632 | dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; | ||
633 | dst_vq->drv_priv = ctx; | ||
634 | dst_vq->ops = &fimc_qops; | ||
635 | dst_vq->mem_ops = &vb2_dma_contig_memops; | ||
636 | dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); | ||
637 | dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; | ||
638 | |||
639 | return vb2_queue_init(dst_vq); | ||
640 | } | ||
641 | |||
642 | static int fimc_m2m_set_default_format(struct fimc_ctx *ctx) | ||
643 | { | ||
644 | struct v4l2_pix_format_mplane pixm = { | ||
645 | .pixelformat = V4L2_PIX_FMT_RGB32, | ||
646 | .width = 800, | ||
647 | .height = 600, | ||
648 | .plane_fmt[0] = { | ||
649 | .bytesperline = 800 * 4, | ||
650 | .sizeimage = 800 * 4 * 600, | ||
651 | }, | ||
652 | }; | ||
653 | struct fimc_fmt *fmt; | ||
654 | |||
655 | fmt = fimc_find_format(&pixm.pixelformat, NULL, FMT_FLAGS_M2M, 0); | ||
656 | if (!fmt) | ||
657 | return -EINVAL; | ||
658 | |||
659 | __set_frame_format(&ctx->s_frame, fmt, &pixm); | ||
660 | __set_frame_format(&ctx->d_frame, fmt, &pixm); | ||
661 | |||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | static int fimc_m2m_open(struct file *file) | ||
666 | { | ||
667 | struct fimc_dev *fimc = video_drvdata(file); | ||
668 | struct fimc_ctx *ctx; | ||
669 | int ret = -EBUSY; | ||
670 | |||
671 | pr_debug("pid: %d, state: %#lx\n", task_pid_nr(current), fimc->state); | ||
672 | |||
673 | if (mutex_lock_interruptible(&fimc->lock)) | ||
674 | return -ERESTARTSYS; | ||
675 | /* | ||
676 | * Don't allow simultaneous open() of the mem-to-mem and the | ||
677 | * capture video node that belong to same FIMC IP instance. | ||
678 | */ | ||
679 | if (test_bit(ST_CAPT_BUSY, &fimc->state)) | ||
680 | goto unlock; | ||
681 | |||
682 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | ||
683 | if (!ctx) { | ||
684 | ret = -ENOMEM; | ||
685 | goto unlock; | ||
686 | } | ||
687 | v4l2_fh_init(&ctx->fh, &fimc->m2m.vfd); | ||
688 | ctx->fimc_dev = fimc; | ||
689 | |||
690 | /* Default color format */ | ||
691 | ctx->s_frame.fmt = fimc_get_format(0); | ||
692 | ctx->d_frame.fmt = fimc_get_format(0); | ||
693 | |||
694 | ret = fimc_ctrls_create(ctx); | ||
695 | if (ret) | ||
696 | goto error_fh; | ||
697 | |||
698 | /* Use separate control handler per file handle */ | ||
699 | ctx->fh.ctrl_handler = &ctx->ctrls.handler; | ||
700 | file->private_data = &ctx->fh; | ||
701 | v4l2_fh_add(&ctx->fh); | ||
702 | |||
703 | /* Setup the device context for memory-to-memory mode */ | ||
704 | ctx->state = FIMC_CTX_M2M; | ||
705 | ctx->flags = 0; | ||
706 | ctx->in_path = FIMC_IO_DMA; | ||
707 | ctx->out_path = FIMC_IO_DMA; | ||
708 | ctx->scaler.enabled = 1; | ||
709 | |||
710 | ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); | ||
711 | if (IS_ERR(ctx->m2m_ctx)) { | ||
712 | ret = PTR_ERR(ctx->m2m_ctx); | ||
713 | goto error_c; | ||
714 | } | ||
715 | |||
716 | if (fimc->m2m.refcnt++ == 0) | ||
717 | set_bit(ST_M2M_RUN, &fimc->state); | ||
718 | |||
719 | ret = fimc_m2m_set_default_format(ctx); | ||
720 | if (ret < 0) | ||
721 | goto error_m2m_ctx; | ||
722 | |||
723 | mutex_unlock(&fimc->lock); | ||
724 | return 0; | ||
725 | |||
726 | error_m2m_ctx: | ||
727 | v4l2_m2m_ctx_release(ctx->m2m_ctx); | ||
728 | error_c: | ||
729 | fimc_ctrls_delete(ctx); | ||
730 | error_fh: | ||
731 | v4l2_fh_del(&ctx->fh); | ||
732 | v4l2_fh_exit(&ctx->fh); | ||
733 | kfree(ctx); | ||
734 | unlock: | ||
735 | mutex_unlock(&fimc->lock); | ||
736 | return ret; | ||
737 | } | ||
738 | |||
739 | static int fimc_m2m_release(struct file *file) | ||
740 | { | ||
741 | struct fimc_ctx *ctx = fh_to_ctx(file->private_data); | ||
742 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
743 | |||
744 | dbg("pid: %d, state: 0x%lx, refcnt= %d", | ||
745 | task_pid_nr(current), fimc->state, fimc->m2m.refcnt); | ||
746 | |||
747 | mutex_lock(&fimc->lock); | ||
748 | |||
749 | v4l2_m2m_ctx_release(ctx->m2m_ctx); | ||
750 | fimc_ctrls_delete(ctx); | ||
751 | v4l2_fh_del(&ctx->fh); | ||
752 | v4l2_fh_exit(&ctx->fh); | ||
753 | |||
754 | if (--fimc->m2m.refcnt <= 0) | ||
755 | clear_bit(ST_M2M_RUN, &fimc->state); | ||
756 | kfree(ctx); | ||
757 | |||
758 | mutex_unlock(&fimc->lock); | ||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | static unsigned int fimc_m2m_poll(struct file *file, | ||
763 | struct poll_table_struct *wait) | ||
764 | { | ||
765 | struct fimc_ctx *ctx = fh_to_ctx(file->private_data); | ||
766 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
767 | int ret; | ||
768 | |||
769 | if (mutex_lock_interruptible(&fimc->lock)) | ||
770 | return -ERESTARTSYS; | ||
771 | |||
772 | ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); | ||
773 | mutex_unlock(&fimc->lock); | ||
774 | |||
775 | return ret; | ||
776 | } | ||
777 | |||
778 | |||
779 | static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma) | ||
780 | { | ||
781 | struct fimc_ctx *ctx = fh_to_ctx(file->private_data); | ||
782 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
783 | int ret; | ||
784 | |||
785 | if (mutex_lock_interruptible(&fimc->lock)) | ||
786 | return -ERESTARTSYS; | ||
787 | |||
788 | ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); | ||
789 | mutex_unlock(&fimc->lock); | ||
790 | |||
791 | return ret; | ||
792 | } | ||
793 | |||
794 | static const struct v4l2_file_operations fimc_m2m_fops = { | ||
795 | .owner = THIS_MODULE, | ||
796 | .open = fimc_m2m_open, | ||
797 | .release = fimc_m2m_release, | ||
798 | .poll = fimc_m2m_poll, | ||
799 | .unlocked_ioctl = video_ioctl2, | ||
800 | .mmap = fimc_m2m_mmap, | ||
801 | }; | ||
802 | |||
803 | static struct v4l2_m2m_ops m2m_ops = { | ||
804 | .device_run = fimc_device_run, | ||
805 | .job_abort = fimc_job_abort, | ||
806 | }; | ||
807 | |||
808 | int fimc_register_m2m_device(struct fimc_dev *fimc, | ||
809 | struct v4l2_device *v4l2_dev) | ||
810 | { | ||
811 | struct video_device *vfd = &fimc->m2m.vfd; | ||
812 | int ret; | ||
813 | |||
814 | fimc->v4l2_dev = v4l2_dev; | ||
815 | |||
816 | memset(vfd, 0, sizeof(*vfd)); | ||
817 | vfd->fops = &fimc_m2m_fops; | ||
818 | vfd->ioctl_ops = &fimc_m2m_ioctl_ops; | ||
819 | vfd->v4l2_dev = v4l2_dev; | ||
820 | vfd->minor = -1; | ||
821 | vfd->release = video_device_release_empty; | ||
822 | vfd->lock = &fimc->lock; | ||
823 | vfd->vfl_dir = VFL_DIR_M2M; | ||
824 | |||
825 | snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id); | ||
826 | video_set_drvdata(vfd, fimc); | ||
827 | |||
828 | fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops); | ||
829 | if (IS_ERR(fimc->m2m.m2m_dev)) { | ||
830 | v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n"); | ||
831 | return PTR_ERR(fimc->m2m.m2m_dev); | ||
832 | } | ||
833 | |||
834 | ret = media_entity_init(&vfd->entity, 0, NULL, 0); | ||
835 | if (ret) | ||
836 | goto err_me; | ||
837 | |||
838 | ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); | ||
839 | if (ret) | ||
840 | goto err_vd; | ||
841 | |||
842 | v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n", | ||
843 | vfd->name, video_device_node_name(vfd)); | ||
844 | return 0; | ||
845 | |||
846 | err_vd: | ||
847 | media_entity_cleanup(&vfd->entity); | ||
848 | err_me: | ||
849 | v4l2_m2m_release(fimc->m2m.m2m_dev); | ||
850 | return ret; | ||
851 | } | ||
852 | |||
853 | void fimc_unregister_m2m_device(struct fimc_dev *fimc) | ||
854 | { | ||
855 | if (!fimc) | ||
856 | return; | ||
857 | |||
858 | if (fimc->m2m.m2m_dev) | ||
859 | v4l2_m2m_release(fimc->m2m.m2m_dev); | ||
860 | |||
861 | if (video_is_registered(&fimc->m2m.vfd)) { | ||
862 | video_unregister_device(&fimc->m2m.vfd); | ||
863 | media_entity_cleanup(&fimc->m2m.vfd.entity); | ||
864 | } | ||
865 | } | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-reg.c b/drivers/media/platform/exynos4-is/fimc-reg.c new file mode 100644 index 000000000000..f079f36099de --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-reg.c | |||
@@ -0,0 +1,841 @@ | |||
1 | /* | ||
2 | * Register interface file for Samsung Camera Interface (FIMC) driver | ||
3 | * | ||
4 | * Copyright (C) 2010 - 2013 Samsung Electronics Co., Ltd. | ||
5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/delay.h> | ||
13 | #include <linux/io.h> | ||
14 | #include <linux/regmap.h> | ||
15 | |||
16 | #include <media/s5p_fimc.h> | ||
17 | #include "media-dev.h" | ||
18 | |||
19 | #include "fimc-reg.h" | ||
20 | #include "fimc-core.h" | ||
21 | |||
22 | void fimc_hw_reset(struct fimc_dev *dev) | ||
23 | { | ||
24 | u32 cfg; | ||
25 | |||
26 | cfg = readl(dev->regs + FIMC_REG_CISRCFMT); | ||
27 | cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; | ||
28 | writel(cfg, dev->regs + FIMC_REG_CISRCFMT); | ||
29 | |||
30 | /* Software reset. */ | ||
31 | cfg = readl(dev->regs + FIMC_REG_CIGCTRL); | ||
32 | cfg |= (FIMC_REG_CIGCTRL_SWRST | FIMC_REG_CIGCTRL_IRQ_LEVEL); | ||
33 | writel(cfg, dev->regs + FIMC_REG_CIGCTRL); | ||
34 | udelay(10); | ||
35 | |||
36 | cfg = readl(dev->regs + FIMC_REG_CIGCTRL); | ||
37 | cfg &= ~FIMC_REG_CIGCTRL_SWRST; | ||
38 | writel(cfg, dev->regs + FIMC_REG_CIGCTRL); | ||
39 | |||
40 | if (dev->drv_data->out_buf_count > 4) | ||
41 | fimc_hw_set_dma_seq(dev, 0xF); | ||
42 | } | ||
43 | |||
44 | static u32 fimc_hw_get_in_flip(struct fimc_ctx *ctx) | ||
45 | { | ||
46 | u32 flip = FIMC_REG_MSCTRL_FLIP_NORMAL; | ||
47 | |||
48 | if (ctx->hflip) | ||
49 | flip = FIMC_REG_MSCTRL_FLIP_Y_MIRROR; | ||
50 | if (ctx->vflip) | ||
51 | flip = FIMC_REG_MSCTRL_FLIP_X_MIRROR; | ||
52 | |||
53 | if (ctx->rotation <= 90) | ||
54 | return flip; | ||
55 | |||
56 | return (flip ^ FIMC_REG_MSCTRL_FLIP_180) & FIMC_REG_MSCTRL_FLIP_180; | ||
57 | } | ||
58 | |||
59 | static u32 fimc_hw_get_target_flip(struct fimc_ctx *ctx) | ||
60 | { | ||
61 | u32 flip = FIMC_REG_CITRGFMT_FLIP_NORMAL; | ||
62 | |||
63 | if (ctx->hflip) | ||
64 | flip |= FIMC_REG_CITRGFMT_FLIP_Y_MIRROR; | ||
65 | if (ctx->vflip) | ||
66 | flip |= FIMC_REG_CITRGFMT_FLIP_X_MIRROR; | ||
67 | |||
68 | if (ctx->rotation <= 90) | ||
69 | return flip; | ||
70 | |||
71 | return (flip ^ FIMC_REG_CITRGFMT_FLIP_180) & FIMC_REG_CITRGFMT_FLIP_180; | ||
72 | } | ||
73 | |||
74 | void fimc_hw_set_rotation(struct fimc_ctx *ctx) | ||
75 | { | ||
76 | u32 cfg, flip; | ||
77 | struct fimc_dev *dev = ctx->fimc_dev; | ||
78 | |||
79 | cfg = readl(dev->regs + FIMC_REG_CITRGFMT); | ||
80 | cfg &= ~(FIMC_REG_CITRGFMT_INROT90 | FIMC_REG_CITRGFMT_OUTROT90 | | ||
81 | FIMC_REG_CITRGFMT_FLIP_180); | ||
82 | |||
83 | /* | ||
84 | * The input and output rotator cannot work simultaneously. | ||
85 | * Use the output rotator in output DMA mode or the input rotator | ||
86 | * in direct fifo output mode. | ||
87 | */ | ||
88 | if (ctx->rotation == 90 || ctx->rotation == 270) { | ||
89 | if (ctx->out_path == FIMC_IO_LCDFIFO) | ||
90 | cfg |= FIMC_REG_CITRGFMT_INROT90; | ||
91 | else | ||
92 | cfg |= FIMC_REG_CITRGFMT_OUTROT90; | ||
93 | } | ||
94 | |||
95 | if (ctx->out_path == FIMC_IO_DMA) { | ||
96 | cfg |= fimc_hw_get_target_flip(ctx); | ||
97 | writel(cfg, dev->regs + FIMC_REG_CITRGFMT); | ||
98 | } else { | ||
99 | /* LCD FIFO path */ | ||
100 | flip = readl(dev->regs + FIMC_REG_MSCTRL); | ||
101 | flip &= ~FIMC_REG_MSCTRL_FLIP_MASK; | ||
102 | flip |= fimc_hw_get_in_flip(ctx); | ||
103 | writel(flip, dev->regs + FIMC_REG_MSCTRL); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | void fimc_hw_set_target_format(struct fimc_ctx *ctx) | ||
108 | { | ||
109 | u32 cfg; | ||
110 | struct fimc_dev *dev = ctx->fimc_dev; | ||
111 | struct fimc_frame *frame = &ctx->d_frame; | ||
112 | |||
113 | dbg("w= %d, h= %d color: %d", frame->width, | ||
114 | frame->height, frame->fmt->color); | ||
115 | |||
116 | cfg = readl(dev->regs + FIMC_REG_CITRGFMT); | ||
117 | cfg &= ~(FIMC_REG_CITRGFMT_FMT_MASK | FIMC_REG_CITRGFMT_HSIZE_MASK | | ||
118 | FIMC_REG_CITRGFMT_VSIZE_MASK); | ||
119 | |||
120 | switch (frame->fmt->color) { | ||
121 | case FIMC_FMT_RGB444...FIMC_FMT_RGB888: | ||
122 | cfg |= FIMC_REG_CITRGFMT_RGB; | ||
123 | break; | ||
124 | case FIMC_FMT_YCBCR420: | ||
125 | cfg |= FIMC_REG_CITRGFMT_YCBCR420; | ||
126 | break; | ||
127 | case FIMC_FMT_YCBYCR422...FIMC_FMT_CRYCBY422: | ||
128 | if (frame->fmt->colplanes == 1) | ||
129 | cfg |= FIMC_REG_CITRGFMT_YCBCR422_1P; | ||
130 | else | ||
131 | cfg |= FIMC_REG_CITRGFMT_YCBCR422; | ||
132 | break; | ||
133 | default: | ||
134 | break; | ||
135 | } | ||
136 | |||
137 | if (ctx->rotation == 90 || ctx->rotation == 270) | ||
138 | cfg |= (frame->height << 16) | frame->width; | ||
139 | else | ||
140 | cfg |= (frame->width << 16) | frame->height; | ||
141 | |||
142 | writel(cfg, dev->regs + FIMC_REG_CITRGFMT); | ||
143 | |||
144 | cfg = readl(dev->regs + FIMC_REG_CITAREA); | ||
145 | cfg &= ~FIMC_REG_CITAREA_MASK; | ||
146 | cfg |= (frame->width * frame->height); | ||
147 | writel(cfg, dev->regs + FIMC_REG_CITAREA); | ||
148 | } | ||
149 | |||
150 | static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx) | ||
151 | { | ||
152 | struct fimc_dev *dev = ctx->fimc_dev; | ||
153 | struct fimc_frame *frame = &ctx->d_frame; | ||
154 | u32 cfg; | ||
155 | |||
156 | cfg = (frame->f_height << 16) | frame->f_width; | ||
157 | writel(cfg, dev->regs + FIMC_REG_ORGOSIZE); | ||
158 | |||
159 | /* Select color space conversion equation (HD/SD size).*/ | ||
160 | cfg = readl(dev->regs + FIMC_REG_CIGCTRL); | ||
161 | if (frame->f_width >= 1280) /* HD */ | ||
162 | cfg |= FIMC_REG_CIGCTRL_CSC_ITU601_709; | ||
163 | else /* SD */ | ||
164 | cfg &= ~FIMC_REG_CIGCTRL_CSC_ITU601_709; | ||
165 | writel(cfg, dev->regs + FIMC_REG_CIGCTRL); | ||
166 | |||
167 | } | ||
168 | |||
169 | void fimc_hw_set_out_dma(struct fimc_ctx *ctx) | ||
170 | { | ||
171 | struct fimc_dev *dev = ctx->fimc_dev; | ||
172 | struct fimc_frame *frame = &ctx->d_frame; | ||
173 | struct fimc_dma_offset *offset = &frame->dma_offset; | ||
174 | struct fimc_fmt *fmt = frame->fmt; | ||
175 | u32 cfg; | ||
176 | |||
177 | /* Set the input dma offsets. */ | ||
178 | cfg = (offset->y_v << 16) | offset->y_h; | ||
179 | writel(cfg, dev->regs + FIMC_REG_CIOYOFF); | ||
180 | |||
181 | cfg = (offset->cb_v << 16) | offset->cb_h; | ||
182 | writel(cfg, dev->regs + FIMC_REG_CIOCBOFF); | ||
183 | |||
184 | cfg = (offset->cr_v << 16) | offset->cr_h; | ||
185 | writel(cfg, dev->regs + FIMC_REG_CIOCROFF); | ||
186 | |||
187 | fimc_hw_set_out_dma_size(ctx); | ||
188 | |||
189 | /* Configure chroma components order. */ | ||
190 | cfg = readl(dev->regs + FIMC_REG_CIOCTRL); | ||
191 | |||
192 | cfg &= ~(FIMC_REG_CIOCTRL_ORDER2P_MASK | | ||
193 | FIMC_REG_CIOCTRL_ORDER422_MASK | | ||
194 | FIMC_REG_CIOCTRL_YCBCR_PLANE_MASK | | ||
195 | FIMC_REG_CIOCTRL_RGB16FMT_MASK); | ||
196 | |||
197 | if (fmt->colplanes == 1) | ||
198 | cfg |= ctx->out_order_1p; | ||
199 | else if (fmt->colplanes == 2) | ||
200 | cfg |= ctx->out_order_2p | FIMC_REG_CIOCTRL_YCBCR_2PLANE; | ||
201 | else if (fmt->colplanes == 3) | ||
202 | cfg |= FIMC_REG_CIOCTRL_YCBCR_3PLANE; | ||
203 | |||
204 | if (fmt->color == FIMC_FMT_RGB565) | ||
205 | cfg |= FIMC_REG_CIOCTRL_RGB565; | ||
206 | else if (fmt->color == FIMC_FMT_RGB555) | ||
207 | cfg |= FIMC_REG_CIOCTRL_ARGB1555; | ||
208 | else if (fmt->color == FIMC_FMT_RGB444) | ||
209 | cfg |= FIMC_REG_CIOCTRL_ARGB4444; | ||
210 | |||
211 | writel(cfg, dev->regs + FIMC_REG_CIOCTRL); | ||
212 | } | ||
213 | |||
214 | static void fimc_hw_en_autoload(struct fimc_dev *dev, int enable) | ||
215 | { | ||
216 | u32 cfg = readl(dev->regs + FIMC_REG_ORGISIZE); | ||
217 | if (enable) | ||
218 | cfg |= FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN; | ||
219 | else | ||
220 | cfg &= ~FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN; | ||
221 | writel(cfg, dev->regs + FIMC_REG_ORGISIZE); | ||
222 | } | ||
223 | |||
224 | void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable) | ||
225 | { | ||
226 | u32 cfg = readl(dev->regs + FIMC_REG_CIOCTRL); | ||
227 | if (enable) | ||
228 | cfg |= FIMC_REG_CIOCTRL_LASTIRQ_ENABLE; | ||
229 | else | ||
230 | cfg &= ~FIMC_REG_CIOCTRL_LASTIRQ_ENABLE; | ||
231 | writel(cfg, dev->regs + FIMC_REG_CIOCTRL); | ||
232 | } | ||
233 | |||
234 | void fimc_hw_set_prescaler(struct fimc_ctx *ctx) | ||
235 | { | ||
236 | struct fimc_dev *dev = ctx->fimc_dev; | ||
237 | struct fimc_scaler *sc = &ctx->scaler; | ||
238 | u32 cfg, shfactor; | ||
239 | |||
240 | shfactor = 10 - (sc->hfactor + sc->vfactor); | ||
241 | cfg = shfactor << 28; | ||
242 | |||
243 | cfg |= (sc->pre_hratio << 16) | sc->pre_vratio; | ||
244 | writel(cfg, dev->regs + FIMC_REG_CISCPRERATIO); | ||
245 | |||
246 | cfg = (sc->pre_dst_width << 16) | sc->pre_dst_height; | ||
247 | writel(cfg, dev->regs + FIMC_REG_CISCPREDST); | ||
248 | } | ||
249 | |||
250 | static void fimc_hw_set_scaler(struct fimc_ctx *ctx) | ||
251 | { | ||
252 | struct fimc_dev *dev = ctx->fimc_dev; | ||
253 | struct fimc_scaler *sc = &ctx->scaler; | ||
254 | struct fimc_frame *src_frame = &ctx->s_frame; | ||
255 | struct fimc_frame *dst_frame = &ctx->d_frame; | ||
256 | |||
257 | u32 cfg = readl(dev->regs + FIMC_REG_CISCCTRL); | ||
258 | |||
259 | cfg &= ~(FIMC_REG_CISCCTRL_CSCR2Y_WIDE | FIMC_REG_CISCCTRL_CSCY2R_WIDE | | ||
260 | FIMC_REG_CISCCTRL_SCALEUP_H | FIMC_REG_CISCCTRL_SCALEUP_V | | ||
261 | FIMC_REG_CISCCTRL_SCALERBYPASS | FIMC_REG_CISCCTRL_ONE2ONE | | ||
262 | FIMC_REG_CISCCTRL_INRGB_FMT_MASK | FIMC_REG_CISCCTRL_OUTRGB_FMT_MASK | | ||
263 | FIMC_REG_CISCCTRL_INTERLACE | FIMC_REG_CISCCTRL_RGB_EXT); | ||
264 | |||
265 | if (!(ctx->flags & FIMC_COLOR_RANGE_NARROW)) | ||
266 | cfg |= (FIMC_REG_CISCCTRL_CSCR2Y_WIDE | | ||
267 | FIMC_REG_CISCCTRL_CSCY2R_WIDE); | ||
268 | |||
269 | if (!sc->enabled) | ||
270 | cfg |= FIMC_REG_CISCCTRL_SCALERBYPASS; | ||
271 | |||
272 | if (sc->scaleup_h) | ||
273 | cfg |= FIMC_REG_CISCCTRL_SCALEUP_H; | ||
274 | |||
275 | if (sc->scaleup_v) | ||
276 | cfg |= FIMC_REG_CISCCTRL_SCALEUP_V; | ||
277 | |||
278 | if (sc->copy_mode) | ||
279 | cfg |= FIMC_REG_CISCCTRL_ONE2ONE; | ||
280 | |||
281 | if (ctx->in_path == FIMC_IO_DMA) { | ||
282 | switch (src_frame->fmt->color) { | ||
283 | case FIMC_FMT_RGB565: | ||
284 | cfg |= FIMC_REG_CISCCTRL_INRGB_FMT_RGB565; | ||
285 | break; | ||
286 | case FIMC_FMT_RGB666: | ||
287 | cfg |= FIMC_REG_CISCCTRL_INRGB_FMT_RGB666; | ||
288 | break; | ||
289 | case FIMC_FMT_RGB888: | ||
290 | cfg |= FIMC_REG_CISCCTRL_INRGB_FMT_RGB888; | ||
291 | break; | ||
292 | } | ||
293 | } | ||
294 | |||
295 | if (ctx->out_path == FIMC_IO_DMA) { | ||
296 | u32 color = dst_frame->fmt->color; | ||
297 | |||
298 | if (color >= FIMC_FMT_RGB444 && color <= FIMC_FMT_RGB565) | ||
299 | cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB565; | ||
300 | else if (color == FIMC_FMT_RGB666) | ||
301 | cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB666; | ||
302 | else if (color == FIMC_FMT_RGB888) | ||
303 | cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB888; | ||
304 | } else { | ||
305 | cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB888; | ||
306 | |||
307 | if (ctx->flags & FIMC_SCAN_MODE_INTERLACED) | ||
308 | cfg |= FIMC_REG_CISCCTRL_INTERLACE; | ||
309 | } | ||
310 | |||
311 | writel(cfg, dev->regs + FIMC_REG_CISCCTRL); | ||
312 | } | ||
313 | |||
314 | void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) | ||
315 | { | ||
316 | struct fimc_dev *dev = ctx->fimc_dev; | ||
317 | const struct fimc_variant *variant = dev->variant; | ||
318 | struct fimc_scaler *sc = &ctx->scaler; | ||
319 | u32 cfg; | ||
320 | |||
321 | dbg("main_hratio= 0x%X main_vratio= 0x%X", | ||
322 | sc->main_hratio, sc->main_vratio); | ||
323 | |||
324 | fimc_hw_set_scaler(ctx); | ||
325 | |||
326 | cfg = readl(dev->regs + FIMC_REG_CISCCTRL); | ||
327 | cfg &= ~(FIMC_REG_CISCCTRL_MHRATIO_MASK | | ||
328 | FIMC_REG_CISCCTRL_MVRATIO_MASK); | ||
329 | |||
330 | if (variant->has_mainscaler_ext) { | ||
331 | cfg |= FIMC_REG_CISCCTRL_MHRATIO_EXT(sc->main_hratio); | ||
332 | cfg |= FIMC_REG_CISCCTRL_MVRATIO_EXT(sc->main_vratio); | ||
333 | writel(cfg, dev->regs + FIMC_REG_CISCCTRL); | ||
334 | |||
335 | cfg = readl(dev->regs + FIMC_REG_CIEXTEN); | ||
336 | |||
337 | cfg &= ~(FIMC_REG_CIEXTEN_MVRATIO_EXT_MASK | | ||
338 | FIMC_REG_CIEXTEN_MHRATIO_EXT_MASK); | ||
339 | cfg |= FIMC_REG_CIEXTEN_MHRATIO_EXT(sc->main_hratio); | ||
340 | cfg |= FIMC_REG_CIEXTEN_MVRATIO_EXT(sc->main_vratio); | ||
341 | writel(cfg, dev->regs + FIMC_REG_CIEXTEN); | ||
342 | } else { | ||
343 | cfg |= FIMC_REG_CISCCTRL_MHRATIO(sc->main_hratio); | ||
344 | cfg |= FIMC_REG_CISCCTRL_MVRATIO(sc->main_vratio); | ||
345 | writel(cfg, dev->regs + FIMC_REG_CISCCTRL); | ||
346 | } | ||
347 | } | ||
348 | |||
349 | void fimc_hw_enable_capture(struct fimc_ctx *ctx) | ||
350 | { | ||
351 | struct fimc_dev *dev = ctx->fimc_dev; | ||
352 | u32 cfg; | ||
353 | |||
354 | cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); | ||
355 | cfg |= FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE; | ||
356 | |||
357 | if (ctx->scaler.enabled) | ||
358 | cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN_SC; | ||
359 | else | ||
360 | cfg &= FIMC_REG_CIIMGCPT_IMGCPTEN_SC; | ||
361 | |||
362 | cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN; | ||
363 | writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); | ||
364 | } | ||
365 | |||
366 | void fimc_hw_disable_capture(struct fimc_dev *dev) | ||
367 | { | ||
368 | u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); | ||
369 | cfg &= ~(FIMC_REG_CIIMGCPT_IMGCPTEN | | ||
370 | FIMC_REG_CIIMGCPT_IMGCPTEN_SC); | ||
371 | writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); | ||
372 | } | ||
373 | |||
374 | void fimc_hw_set_effect(struct fimc_ctx *ctx) | ||
375 | { | ||
376 | struct fimc_dev *dev = ctx->fimc_dev; | ||
377 | struct fimc_effect *effect = &ctx->effect; | ||
378 | u32 cfg = 0; | ||
379 | |||
380 | if (effect->type != FIMC_REG_CIIMGEFF_FIN_BYPASS) { | ||
381 | cfg |= FIMC_REG_CIIMGEFF_IE_SC_AFTER | | ||
382 | FIMC_REG_CIIMGEFF_IE_ENABLE; | ||
383 | cfg |= effect->type; | ||
384 | if (effect->type == FIMC_REG_CIIMGEFF_FIN_ARBITRARY) | ||
385 | cfg |= (effect->pat_cb << 13) | effect->pat_cr; | ||
386 | } | ||
387 | |||
388 | writel(cfg, dev->regs + FIMC_REG_CIIMGEFF); | ||
389 | } | ||
390 | |||
391 | void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx) | ||
392 | { | ||
393 | struct fimc_dev *dev = ctx->fimc_dev; | ||
394 | struct fimc_frame *frame = &ctx->d_frame; | ||
395 | u32 cfg; | ||
396 | |||
397 | if (!(frame->fmt->flags & FMT_HAS_ALPHA)) | ||
398 | return; | ||
399 | |||
400 | cfg = readl(dev->regs + FIMC_REG_CIOCTRL); | ||
401 | cfg &= ~FIMC_REG_CIOCTRL_ALPHA_OUT_MASK; | ||
402 | cfg |= (frame->alpha << 4); | ||
403 | writel(cfg, dev->regs + FIMC_REG_CIOCTRL); | ||
404 | } | ||
405 | |||
406 | static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx) | ||
407 | { | ||
408 | struct fimc_dev *dev = ctx->fimc_dev; | ||
409 | struct fimc_frame *frame = &ctx->s_frame; | ||
410 | u32 cfg_o = 0; | ||
411 | u32 cfg_r = 0; | ||
412 | |||
413 | if (FIMC_IO_LCDFIFO == ctx->out_path) | ||
414 | cfg_r |= FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN; | ||
415 | |||
416 | cfg_o |= (frame->f_height << 16) | frame->f_width; | ||
417 | cfg_r |= (frame->height << 16) | frame->width; | ||
418 | |||
419 | writel(cfg_o, dev->regs + FIMC_REG_ORGISIZE); | ||
420 | writel(cfg_r, dev->regs + FIMC_REG_CIREAL_ISIZE); | ||
421 | } | ||
422 | |||
423 | void fimc_hw_set_in_dma(struct fimc_ctx *ctx) | ||
424 | { | ||
425 | struct fimc_dev *dev = ctx->fimc_dev; | ||
426 | struct fimc_frame *frame = &ctx->s_frame; | ||
427 | struct fimc_dma_offset *offset = &frame->dma_offset; | ||
428 | u32 cfg; | ||
429 | |||
430 | /* Set the pixel offsets. */ | ||
431 | cfg = (offset->y_v << 16) | offset->y_h; | ||
432 | writel(cfg, dev->regs + FIMC_REG_CIIYOFF); | ||
433 | |||
434 | cfg = (offset->cb_v << 16) | offset->cb_h; | ||
435 | writel(cfg, dev->regs + FIMC_REG_CIICBOFF); | ||
436 | |||
437 | cfg = (offset->cr_v << 16) | offset->cr_h; | ||
438 | writel(cfg, dev->regs + FIMC_REG_CIICROFF); | ||
439 | |||
440 | /* Input original and real size. */ | ||
441 | fimc_hw_set_in_dma_size(ctx); | ||
442 | |||
443 | /* Use DMA autoload only in FIFO mode. */ | ||
444 | fimc_hw_en_autoload(dev, ctx->out_path == FIMC_IO_LCDFIFO); | ||
445 | |||
446 | /* Set the input DMA to process single frame only. */ | ||
447 | cfg = readl(dev->regs + FIMC_REG_MSCTRL); | ||
448 | cfg &= ~(FIMC_REG_MSCTRL_INFORMAT_MASK | ||
449 | | FIMC_REG_MSCTRL_IN_BURST_COUNT_MASK | ||
450 | | FIMC_REG_MSCTRL_INPUT_MASK | ||
451 | | FIMC_REG_MSCTRL_C_INT_IN_MASK | ||
452 | | FIMC_REG_MSCTRL_2P_IN_ORDER_MASK | ||
453 | | FIMC_REG_MSCTRL_ORDER422_MASK); | ||
454 | |||
455 | cfg |= (FIMC_REG_MSCTRL_IN_BURST_COUNT(4) | ||
456 | | FIMC_REG_MSCTRL_INPUT_MEMORY | ||
457 | | FIMC_REG_MSCTRL_FIFO_CTRL_FULL); | ||
458 | |||
459 | switch (frame->fmt->color) { | ||
460 | case FIMC_FMT_RGB565...FIMC_FMT_RGB888: | ||
461 | cfg |= FIMC_REG_MSCTRL_INFORMAT_RGB; | ||
462 | break; | ||
463 | case FIMC_FMT_YCBCR420: | ||
464 | cfg |= FIMC_REG_MSCTRL_INFORMAT_YCBCR420; | ||
465 | |||
466 | if (frame->fmt->colplanes == 2) | ||
467 | cfg |= ctx->in_order_2p | FIMC_REG_MSCTRL_C_INT_IN_2PLANE; | ||
468 | else | ||
469 | cfg |= FIMC_REG_MSCTRL_C_INT_IN_3PLANE; | ||
470 | |||
471 | break; | ||
472 | case FIMC_FMT_YCBYCR422...FIMC_FMT_CRYCBY422: | ||
473 | if (frame->fmt->colplanes == 1) { | ||
474 | cfg |= ctx->in_order_1p | ||
475 | | FIMC_REG_MSCTRL_INFORMAT_YCBCR422_1P; | ||
476 | } else { | ||
477 | cfg |= FIMC_REG_MSCTRL_INFORMAT_YCBCR422; | ||
478 | |||
479 | if (frame->fmt->colplanes == 2) | ||
480 | cfg |= ctx->in_order_2p | ||
481 | | FIMC_REG_MSCTRL_C_INT_IN_2PLANE; | ||
482 | else | ||
483 | cfg |= FIMC_REG_MSCTRL_C_INT_IN_3PLANE; | ||
484 | } | ||
485 | break; | ||
486 | default: | ||
487 | break; | ||
488 | } | ||
489 | |||
490 | writel(cfg, dev->regs + FIMC_REG_MSCTRL); | ||
491 | |||
492 | /* Input/output DMA linear/tiled mode. */ | ||
493 | cfg = readl(dev->regs + FIMC_REG_CIDMAPARAM); | ||
494 | cfg &= ~FIMC_REG_CIDMAPARAM_TILE_MASK; | ||
495 | |||
496 | if (tiled_fmt(ctx->s_frame.fmt)) | ||
497 | cfg |= FIMC_REG_CIDMAPARAM_R_64X32; | ||
498 | |||
499 | if (tiled_fmt(ctx->d_frame.fmt)) | ||
500 | cfg |= FIMC_REG_CIDMAPARAM_W_64X32; | ||
501 | |||
502 | writel(cfg, dev->regs + FIMC_REG_CIDMAPARAM); | ||
503 | } | ||
504 | |||
505 | |||
506 | void fimc_hw_set_input_path(struct fimc_ctx *ctx) | ||
507 | { | ||
508 | struct fimc_dev *dev = ctx->fimc_dev; | ||
509 | |||
510 | u32 cfg = readl(dev->regs + FIMC_REG_MSCTRL); | ||
511 | cfg &= ~FIMC_REG_MSCTRL_INPUT_MASK; | ||
512 | |||
513 | if (ctx->in_path == FIMC_IO_DMA) | ||
514 | cfg |= FIMC_REG_MSCTRL_INPUT_MEMORY; | ||
515 | else | ||
516 | cfg |= FIMC_REG_MSCTRL_INPUT_EXTCAM; | ||
517 | |||
518 | writel(cfg, dev->regs + FIMC_REG_MSCTRL); | ||
519 | } | ||
520 | |||
521 | void fimc_hw_set_output_path(struct fimc_ctx *ctx) | ||
522 | { | ||
523 | struct fimc_dev *dev = ctx->fimc_dev; | ||
524 | |||
525 | u32 cfg = readl(dev->regs + FIMC_REG_CISCCTRL); | ||
526 | cfg &= ~FIMC_REG_CISCCTRL_LCDPATHEN_FIFO; | ||
527 | if (ctx->out_path == FIMC_IO_LCDFIFO) | ||
528 | cfg |= FIMC_REG_CISCCTRL_LCDPATHEN_FIFO; | ||
529 | writel(cfg, dev->regs + FIMC_REG_CISCCTRL); | ||
530 | } | ||
531 | |||
532 | void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr) | ||
533 | { | ||
534 | u32 cfg = readl(dev->regs + FIMC_REG_CIREAL_ISIZE); | ||
535 | cfg |= FIMC_REG_CIREAL_ISIZE_ADDR_CH_DIS; | ||
536 | writel(cfg, dev->regs + FIMC_REG_CIREAL_ISIZE); | ||
537 | |||
538 | writel(paddr->y, dev->regs + FIMC_REG_CIIYSA(0)); | ||
539 | writel(paddr->cb, dev->regs + FIMC_REG_CIICBSA(0)); | ||
540 | writel(paddr->cr, dev->regs + FIMC_REG_CIICRSA(0)); | ||
541 | |||
542 | cfg &= ~FIMC_REG_CIREAL_ISIZE_ADDR_CH_DIS; | ||
543 | writel(cfg, dev->regs + FIMC_REG_CIREAL_ISIZE); | ||
544 | } | ||
545 | |||
546 | void fimc_hw_set_output_addr(struct fimc_dev *dev, | ||
547 | struct fimc_addr *paddr, int index) | ||
548 | { | ||
549 | int i = (index == -1) ? 0 : index; | ||
550 | do { | ||
551 | writel(paddr->y, dev->regs + FIMC_REG_CIOYSA(i)); | ||
552 | writel(paddr->cb, dev->regs + FIMC_REG_CIOCBSA(i)); | ||
553 | writel(paddr->cr, dev->regs + FIMC_REG_CIOCRSA(i)); | ||
554 | dbg("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", | ||
555 | i, paddr->y, paddr->cb, paddr->cr); | ||
556 | } while (index == -1 && ++i < FIMC_MAX_OUT_BUFS); | ||
557 | } | ||
558 | |||
559 | int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, | ||
560 | struct fimc_source_info *cam) | ||
561 | { | ||
562 | u32 cfg = readl(fimc->regs + FIMC_REG_CIGCTRL); | ||
563 | |||
564 | cfg &= ~(FIMC_REG_CIGCTRL_INVPOLPCLK | FIMC_REG_CIGCTRL_INVPOLVSYNC | | ||
565 | FIMC_REG_CIGCTRL_INVPOLHREF | FIMC_REG_CIGCTRL_INVPOLHSYNC | | ||
566 | FIMC_REG_CIGCTRL_INVPOLFIELD); | ||
567 | |||
568 | if (cam->flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) | ||
569 | cfg |= FIMC_REG_CIGCTRL_INVPOLPCLK; | ||
570 | |||
571 | if (cam->flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) | ||
572 | cfg |= FIMC_REG_CIGCTRL_INVPOLVSYNC; | ||
573 | |||
574 | if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) | ||
575 | cfg |= FIMC_REG_CIGCTRL_INVPOLHREF; | ||
576 | |||
577 | if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) | ||
578 | cfg |= FIMC_REG_CIGCTRL_INVPOLHSYNC; | ||
579 | |||
580 | if (cam->flags & V4L2_MBUS_FIELD_EVEN_LOW) | ||
581 | cfg |= FIMC_REG_CIGCTRL_INVPOLFIELD; | ||
582 | |||
583 | writel(cfg, fimc->regs + FIMC_REG_CIGCTRL); | ||
584 | |||
585 | return 0; | ||
586 | } | ||
587 | |||
588 | struct mbus_pixfmt_desc { | ||
589 | u32 pixelcode; | ||
590 | u32 cisrcfmt; | ||
591 | u16 bus_width; | ||
592 | }; | ||
593 | |||
594 | static const struct mbus_pixfmt_desc pix_desc[] = { | ||
595 | { V4L2_MBUS_FMT_YUYV8_2X8, FIMC_REG_CISRCFMT_ORDER422_YCBYCR, 8 }, | ||
596 | { V4L2_MBUS_FMT_YVYU8_2X8, FIMC_REG_CISRCFMT_ORDER422_YCRYCB, 8 }, | ||
597 | { V4L2_MBUS_FMT_VYUY8_2X8, FIMC_REG_CISRCFMT_ORDER422_CRYCBY, 8 }, | ||
598 | { V4L2_MBUS_FMT_UYVY8_2X8, FIMC_REG_CISRCFMT_ORDER422_CBYCRY, 8 }, | ||
599 | }; | ||
600 | |||
601 | int fimc_hw_set_camera_source(struct fimc_dev *fimc, | ||
602 | struct fimc_source_info *source) | ||
603 | { | ||
604 | struct fimc_vid_cap *vc = &fimc->vid_cap; | ||
605 | struct fimc_frame *f = &vc->ctx->s_frame; | ||
606 | u32 bus_width, cfg = 0; | ||
607 | int i; | ||
608 | |||
609 | switch (source->fimc_bus_type) { | ||
610 | case FIMC_BUS_TYPE_ITU_601: | ||
611 | case FIMC_BUS_TYPE_ITU_656: | ||
612 | for (i = 0; i < ARRAY_SIZE(pix_desc); i++) { | ||
613 | if (vc->ci_fmt.code == pix_desc[i].pixelcode) { | ||
614 | cfg = pix_desc[i].cisrcfmt; | ||
615 | bus_width = pix_desc[i].bus_width; | ||
616 | break; | ||
617 | } | ||
618 | } | ||
619 | |||
620 | if (i == ARRAY_SIZE(pix_desc)) { | ||
621 | v4l2_err(&vc->vfd, | ||
622 | "Camera color format not supported: %d\n", | ||
623 | vc->ci_fmt.code); | ||
624 | return -EINVAL; | ||
625 | } | ||
626 | |||
627 | if (source->fimc_bus_type == FIMC_BUS_TYPE_ITU_601) { | ||
628 | if (bus_width == 8) | ||
629 | cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; | ||
630 | else if (bus_width == 16) | ||
631 | cfg |= FIMC_REG_CISRCFMT_ITU601_16BIT; | ||
632 | } /* else defaults to ITU-R BT.656 8-bit */ | ||
633 | break; | ||
634 | case FIMC_BUS_TYPE_MIPI_CSI2: | ||
635 | if (fimc_fmt_is_user_defined(f->fmt->color)) | ||
636 | cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; | ||
637 | break; | ||
638 | default: | ||
639 | case FIMC_BUS_TYPE_ISP_WRITEBACK: | ||
640 | /* Anything to do here ? */ | ||
641 | break; | ||
642 | } | ||
643 | |||
644 | cfg |= (f->o_width << 16) | f->o_height; | ||
645 | writel(cfg, fimc->regs + FIMC_REG_CISRCFMT); | ||
646 | return 0; | ||
647 | } | ||
648 | |||
649 | void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f) | ||
650 | { | ||
651 | u32 hoff2, voff2; | ||
652 | |||
653 | u32 cfg = readl(fimc->regs + FIMC_REG_CIWDOFST); | ||
654 | |||
655 | cfg &= ~(FIMC_REG_CIWDOFST_HOROFF_MASK | FIMC_REG_CIWDOFST_VEROFF_MASK); | ||
656 | cfg |= FIMC_REG_CIWDOFST_OFF_EN | | ||
657 | (f->offs_h << 16) | f->offs_v; | ||
658 | |||
659 | writel(cfg, fimc->regs + FIMC_REG_CIWDOFST); | ||
660 | |||
661 | /* See CIWDOFSTn register description in the datasheet for details. */ | ||
662 | hoff2 = f->o_width - f->width - f->offs_h; | ||
663 | voff2 = f->o_height - f->height - f->offs_v; | ||
664 | cfg = (hoff2 << 16) | voff2; | ||
665 | writel(cfg, fimc->regs + FIMC_REG_CIWDOFST2); | ||
666 | } | ||
667 | |||
668 | int fimc_hw_set_camera_type(struct fimc_dev *fimc, | ||
669 | struct fimc_source_info *source) | ||
670 | { | ||
671 | struct fimc_vid_cap *vid_cap = &fimc->vid_cap; | ||
672 | u32 csis_data_alignment = 32; | ||
673 | u32 cfg, tmp; | ||
674 | |||
675 | cfg = readl(fimc->regs + FIMC_REG_CIGCTRL); | ||
676 | |||
677 | /* Select ITU B interface, disable Writeback path and test pattern. */ | ||
678 | cfg &= ~(FIMC_REG_CIGCTRL_TESTPAT_MASK | FIMC_REG_CIGCTRL_SELCAM_ITU_A | | ||
679 | FIMC_REG_CIGCTRL_SELCAM_MIPI | FIMC_REG_CIGCTRL_CAMIF_SELWB | | ||
680 | FIMC_REG_CIGCTRL_SELCAM_MIPI_A | FIMC_REG_CIGCTRL_CAM_JPEG | | ||
681 | FIMC_REG_CIGCTRL_SELWB_A); | ||
682 | |||
683 | switch (source->fimc_bus_type) { | ||
684 | case FIMC_BUS_TYPE_MIPI_CSI2: | ||
685 | cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI; | ||
686 | |||
687 | if (source->mux_id == 0) | ||
688 | cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI_A; | ||
689 | |||
690 | /* TODO: add remaining supported formats. */ | ||
691 | switch (vid_cap->ci_fmt.code) { | ||
692 | case V4L2_MBUS_FMT_VYUY8_2X8: | ||
693 | tmp = FIMC_REG_CSIIMGFMT_YCBCR422_8BIT; | ||
694 | break; | ||
695 | case V4L2_MBUS_FMT_JPEG_1X8: | ||
696 | case V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8: | ||
697 | tmp = FIMC_REG_CSIIMGFMT_USER(1); | ||
698 | cfg |= FIMC_REG_CIGCTRL_CAM_JPEG; | ||
699 | break; | ||
700 | default: | ||
701 | v4l2_err(&vid_cap->vfd, | ||
702 | "Not supported camera pixel format: %#x\n", | ||
703 | vid_cap->ci_fmt.code); | ||
704 | return -EINVAL; | ||
705 | } | ||
706 | tmp |= (csis_data_alignment == 32) << 8; | ||
707 | |||
708 | writel(tmp, fimc->regs + FIMC_REG_CSIIMGFMT); | ||
709 | break; | ||
710 | case FIMC_BUS_TYPE_ITU_601...FIMC_BUS_TYPE_ITU_656: | ||
711 | if (source->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */ | ||
712 | cfg |= FIMC_REG_CIGCTRL_SELCAM_ITU_A; | ||
713 | break; | ||
714 | case FIMC_BUS_TYPE_LCD_WRITEBACK_A: | ||
715 | cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB; | ||
716 | /* fall through */ | ||
717 | case FIMC_BUS_TYPE_ISP_WRITEBACK: | ||
718 | if (fimc->variant->has_isp_wb) | ||
719 | cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB; | ||
720 | else | ||
721 | WARN_ONCE(1, "ISP Writeback input is not supported\n"); | ||
722 | break; | ||
723 | default: | ||
724 | v4l2_err(&vid_cap->vfd, "Invalid FIMC bus type selected: %d\n", | ||
725 | source->fimc_bus_type); | ||
726 | return -EINVAL; | ||
727 | } | ||
728 | writel(cfg, fimc->regs + FIMC_REG_CIGCTRL); | ||
729 | |||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | void fimc_hw_clear_irq(struct fimc_dev *dev) | ||
734 | { | ||
735 | u32 cfg = readl(dev->regs + FIMC_REG_CIGCTRL); | ||
736 | cfg |= FIMC_REG_CIGCTRL_IRQ_CLR; | ||
737 | writel(cfg, dev->regs + FIMC_REG_CIGCTRL); | ||
738 | } | ||
739 | |||
740 | void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on) | ||
741 | { | ||
742 | u32 cfg = readl(dev->regs + FIMC_REG_CISCCTRL); | ||
743 | if (on) | ||
744 | cfg |= FIMC_REG_CISCCTRL_SCALERSTART; | ||
745 | else | ||
746 | cfg &= ~FIMC_REG_CISCCTRL_SCALERSTART; | ||
747 | writel(cfg, dev->regs + FIMC_REG_CISCCTRL); | ||
748 | } | ||
749 | |||
750 | void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on) | ||
751 | { | ||
752 | u32 cfg = readl(dev->regs + FIMC_REG_MSCTRL); | ||
753 | if (on) | ||
754 | cfg |= FIMC_REG_MSCTRL_ENVID; | ||
755 | else | ||
756 | cfg &= ~FIMC_REG_MSCTRL_ENVID; | ||
757 | writel(cfg, dev->regs + FIMC_REG_MSCTRL); | ||
758 | } | ||
759 | |||
760 | /* Return an index to the buffer actually being written. */ | ||
761 | s32 fimc_hw_get_frame_index(struct fimc_dev *dev) | ||
762 | { | ||
763 | s32 reg; | ||
764 | |||
765 | if (dev->drv_data->cistatus2) { | ||
766 | reg = readl(dev->regs + FIMC_REG_CISTATUS2) & 0x3f; | ||
767 | return reg - 1; | ||
768 | } | ||
769 | |||
770 | reg = readl(dev->regs + FIMC_REG_CISTATUS); | ||
771 | |||
772 | return (reg & FIMC_REG_CISTATUS_FRAMECNT_MASK) >> | ||
773 | FIMC_REG_CISTATUS_FRAMECNT_SHIFT; | ||
774 | } | ||
775 | |||
776 | /* Return an index to the buffer being written previously. */ | ||
777 | s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev) | ||
778 | { | ||
779 | s32 reg; | ||
780 | |||
781 | if (!dev->drv_data->cistatus2) | ||
782 | return -1; | ||
783 | |||
784 | reg = readl(dev->regs + FIMC_REG_CISTATUS2); | ||
785 | return ((reg >> 7) & 0x3f) - 1; | ||
786 | } | ||
787 | |||
788 | /* Locking: the caller holds fimc->slock */ | ||
789 | void fimc_activate_capture(struct fimc_ctx *ctx) | ||
790 | { | ||
791 | fimc_hw_enable_scaler(ctx->fimc_dev, ctx->scaler.enabled); | ||
792 | fimc_hw_enable_capture(ctx); | ||
793 | } | ||
794 | |||
795 | void fimc_deactivate_capture(struct fimc_dev *fimc) | ||
796 | { | ||
797 | fimc_hw_en_lastirq(fimc, true); | ||
798 | fimc_hw_disable_capture(fimc); | ||
799 | fimc_hw_enable_scaler(fimc, false); | ||
800 | fimc_hw_en_lastirq(fimc, false); | ||
801 | } | ||
802 | |||
803 | int fimc_hw_camblk_cfg_writeback(struct fimc_dev *fimc) | ||
804 | { | ||
805 | struct regmap *map = fimc->sysreg; | ||
806 | unsigned int mask, val, camblk_cfg; | ||
807 | int ret; | ||
808 | |||
809 | if (map == NULL) | ||
810 | return 0; | ||
811 | |||
812 | ret = regmap_read(map, SYSREG_CAMBLK, &camblk_cfg); | ||
813 | if (ret < 0 || ((camblk_cfg & 0x00700000) >> 20 != 0x3)) | ||
814 | return ret; | ||
815 | |||
816 | if (!WARN(fimc->id >= 3, "not supported id: %d\n", fimc->id)) | ||
817 | val = 0x1 << (fimc->id + 20); | ||
818 | else | ||
819 | val = 0; | ||
820 | |||
821 | mask = SYSREG_CAMBLK_FIFORST_ISP | SYSREG_CAMBLK_ISPWB_FULL_EN; | ||
822 | ret = regmap_update_bits(map, SYSREG_CAMBLK, mask, val); | ||
823 | if (ret < 0) | ||
824 | return ret; | ||
825 | |||
826 | usleep_range(1000, 2000); | ||
827 | |||
828 | val |= SYSREG_CAMBLK_FIFORST_ISP; | ||
829 | ret = regmap_update_bits(map, SYSREG_CAMBLK, mask, val); | ||
830 | if (ret < 0) | ||
831 | return ret; | ||
832 | |||
833 | mask = SYSREG_ISPBLK_FIFORST_CAM_BLK; | ||
834 | ret = regmap_update_bits(map, SYSREG_ISPBLK, mask, ~mask); | ||
835 | if (ret < 0) | ||
836 | return ret; | ||
837 | |||
838 | usleep_range(1000, 2000); | ||
839 | |||
840 | return regmap_update_bits(map, SYSREG_ISPBLK, mask, mask); | ||
841 | } | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-reg.h b/drivers/media/platform/exynos4-is/fimc-reg.h new file mode 100644 index 000000000000..6c97798c75a5 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-reg.h | |||
@@ -0,0 +1,338 @@ | |||
1 | /* | ||
2 | * Samsung camera host interface (FIMC) registers definition | ||
3 | * | ||
4 | * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #ifndef FIMC_REG_H_ | ||
12 | #define FIMC_REG_H_ | ||
13 | |||
14 | #include "fimc-core.h" | ||
15 | |||
16 | /* Input source format */ | ||
17 | #define FIMC_REG_CISRCFMT 0x00 | ||
18 | #define FIMC_REG_CISRCFMT_ITU601_8BIT (1 << 31) | ||
19 | #define FIMC_REG_CISRCFMT_ITU601_16BIT (1 << 29) | ||
20 | #define FIMC_REG_CISRCFMT_ORDER422_YCBYCR (0 << 14) | ||
21 | #define FIMC_REG_CISRCFMT_ORDER422_YCRYCB (1 << 14) | ||
22 | #define FIMC_REG_CISRCFMT_ORDER422_CBYCRY (2 << 14) | ||
23 | #define FIMC_REG_CISRCFMT_ORDER422_CRYCBY (3 << 14) | ||
24 | |||
25 | /* Window offset */ | ||
26 | #define FIMC_REG_CIWDOFST 0x04 | ||
27 | #define FIMC_REG_CIWDOFST_OFF_EN (1 << 31) | ||
28 | #define FIMC_REG_CIWDOFST_CLROVFIY (1 << 30) | ||
29 | #define FIMC_REG_CIWDOFST_CLROVRLB (1 << 29) | ||
30 | #define FIMC_REG_CIWDOFST_HOROFF_MASK (0x7ff << 16) | ||
31 | #define FIMC_REG_CIWDOFST_CLROVFICB (1 << 15) | ||
32 | #define FIMC_REG_CIWDOFST_CLROVFICR (1 << 14) | ||
33 | #define FIMC_REG_CIWDOFST_VEROFF_MASK (0xfff << 0) | ||
34 | |||
35 | /* Global control */ | ||
36 | #define FIMC_REG_CIGCTRL 0x08 | ||
37 | #define FIMC_REG_CIGCTRL_SWRST (1 << 31) | ||
38 | #define FIMC_REG_CIGCTRL_CAMRST_A (1 << 30) | ||
39 | #define FIMC_REG_CIGCTRL_SELCAM_ITU_A (1 << 29) | ||
40 | #define FIMC_REG_CIGCTRL_TESTPAT_NORMAL (0 << 27) | ||
41 | #define FIMC_REG_CIGCTRL_TESTPAT_COLOR_BAR (1 << 27) | ||
42 | #define FIMC_REG_CIGCTRL_TESTPAT_HOR_INC (2 << 27) | ||
43 | #define FIMC_REG_CIGCTRL_TESTPAT_VER_INC (3 << 27) | ||
44 | #define FIMC_REG_CIGCTRL_TESTPAT_MASK (3 << 27) | ||
45 | #define FIMC_REG_CIGCTRL_TESTPAT_SHIFT 27 | ||
46 | #define FIMC_REG_CIGCTRL_INVPOLPCLK (1 << 26) | ||
47 | #define FIMC_REG_CIGCTRL_INVPOLVSYNC (1 << 25) | ||
48 | #define FIMC_REG_CIGCTRL_INVPOLHREF (1 << 24) | ||
49 | #define FIMC_REG_CIGCTRL_IRQ_OVFEN (1 << 22) | ||
50 | #define FIMC_REG_CIGCTRL_HREF_MASK (1 << 21) | ||
51 | #define FIMC_REG_CIGCTRL_IRQ_LEVEL (1 << 20) | ||
52 | #define FIMC_REG_CIGCTRL_IRQ_CLR (1 << 19) | ||
53 | #define FIMC_REG_CIGCTRL_IRQ_ENABLE (1 << 16) | ||
54 | #define FIMC_REG_CIGCTRL_SHDW_DISABLE (1 << 12) | ||
55 | /* 0 - selects Writeback A (LCD), 1 - selects Writeback B (LCD/ISP) */ | ||
56 | #define FIMC_REG_CIGCTRL_SELWB_A (1 << 10) | ||
57 | #define FIMC_REG_CIGCTRL_CAM_JPEG (1 << 8) | ||
58 | #define FIMC_REG_CIGCTRL_SELCAM_MIPI_A (1 << 7) | ||
59 | #define FIMC_REG_CIGCTRL_CAMIF_SELWB (1 << 6) | ||
60 | /* 0 - ITU601; 1 - ITU709 */ | ||
61 | #define FIMC_REG_CIGCTRL_CSC_ITU601_709 (1 << 5) | ||
62 | #define FIMC_REG_CIGCTRL_INVPOLHSYNC (1 << 4) | ||
63 | #define FIMC_REG_CIGCTRL_SELCAM_MIPI (1 << 3) | ||
64 | #define FIMC_REG_CIGCTRL_INVPOLFIELD (1 << 1) | ||
65 | #define FIMC_REG_CIGCTRL_INTERLACE (1 << 0) | ||
66 | |||
67 | /* Window offset 2 */ | ||
68 | #define FIMC_REG_CIWDOFST2 0x14 | ||
69 | #define FIMC_REG_CIWDOFST2_HOROFF_MASK (0xfff << 16) | ||
70 | #define FIMC_REG_CIWDOFST2_VEROFF_MASK (0xfff << 0) | ||
71 | |||
72 | /* Output DMA Y/Cb/Cr plane start addresses */ | ||
73 | #define FIMC_REG_CIOYSA(n) (0x18 + (n) * 4) | ||
74 | #define FIMC_REG_CIOCBSA(n) (0x28 + (n) * 4) | ||
75 | #define FIMC_REG_CIOCRSA(n) (0x38 + (n) * 4) | ||
76 | |||
77 | /* Target image format */ | ||
78 | #define FIMC_REG_CITRGFMT 0x48 | ||
79 | #define FIMC_REG_CITRGFMT_INROT90 (1 << 31) | ||
80 | #define FIMC_REG_CITRGFMT_YCBCR420 (0 << 29) | ||
81 | #define FIMC_REG_CITRGFMT_YCBCR422 (1 << 29) | ||
82 | #define FIMC_REG_CITRGFMT_YCBCR422_1P (2 << 29) | ||
83 | #define FIMC_REG_CITRGFMT_RGB (3 << 29) | ||
84 | #define FIMC_REG_CITRGFMT_FMT_MASK (3 << 29) | ||
85 | #define FIMC_REG_CITRGFMT_HSIZE_MASK (0xfff << 16) | ||
86 | #define FIMC_REG_CITRGFMT_FLIP_SHIFT 14 | ||
87 | #define FIMC_REG_CITRGFMT_FLIP_NORMAL (0 << 14) | ||
88 | #define FIMC_REG_CITRGFMT_FLIP_X_MIRROR (1 << 14) | ||
89 | #define FIMC_REG_CITRGFMT_FLIP_Y_MIRROR (2 << 14) | ||
90 | #define FIMC_REG_CITRGFMT_FLIP_180 (3 << 14) | ||
91 | #define FIMC_REG_CITRGFMT_FLIP_MASK (3 << 14) | ||
92 | #define FIMC_REG_CITRGFMT_OUTROT90 (1 << 13) | ||
93 | #define FIMC_REG_CITRGFMT_VSIZE_MASK (0xfff << 0) | ||
94 | |||
95 | /* Output DMA control */ | ||
96 | #define FIMC_REG_CIOCTRL 0x4c | ||
97 | #define FIMC_REG_CIOCTRL_ORDER422_MASK (3 << 0) | ||
98 | #define FIMC_REG_CIOCTRL_ORDER422_YCBYCR (0 << 0) | ||
99 | #define FIMC_REG_CIOCTRL_ORDER422_YCRYCB (1 << 0) | ||
100 | #define FIMC_REG_CIOCTRL_ORDER422_CBYCRY (2 << 0) | ||
101 | #define FIMC_REG_CIOCTRL_ORDER422_CRYCBY (3 << 0) | ||
102 | #define FIMC_REG_CIOCTRL_LASTIRQ_ENABLE (1 << 2) | ||
103 | #define FIMC_REG_CIOCTRL_YCBCR_3PLANE (0 << 3) | ||
104 | #define FIMC_REG_CIOCTRL_YCBCR_2PLANE (1 << 3) | ||
105 | #define FIMC_REG_CIOCTRL_YCBCR_PLANE_MASK (1 << 3) | ||
106 | #define FIMC_REG_CIOCTRL_ALPHA_OUT_MASK (0xff << 4) | ||
107 | #define FIMC_REG_CIOCTRL_RGB16FMT_MASK (3 << 16) | ||
108 | #define FIMC_REG_CIOCTRL_RGB565 (0 << 16) | ||
109 | #define FIMC_REG_CIOCTRL_ARGB1555 (1 << 16) | ||
110 | #define FIMC_REG_CIOCTRL_ARGB4444 (2 << 16) | ||
111 | #define FIMC_REG_CIOCTRL_ORDER2P_SHIFT 24 | ||
112 | #define FIMC_REG_CIOCTRL_ORDER2P_MASK (3 << 24) | ||
113 | #define FIMC_REG_CIOCTRL_ORDER422_2P_LSB_CRCB (0 << 24) | ||
114 | |||
115 | /* Pre-scaler control 1 */ | ||
116 | #define FIMC_REG_CISCPRERATIO 0x50 | ||
117 | |||
118 | #define FIMC_REG_CISCPREDST 0x54 | ||
119 | |||
120 | /* Main scaler control */ | ||
121 | #define FIMC_REG_CISCCTRL 0x58 | ||
122 | #define FIMC_REG_CISCCTRL_SCALERBYPASS (1 << 31) | ||
123 | #define FIMC_REG_CISCCTRL_SCALEUP_H (1 << 30) | ||
124 | #define FIMC_REG_CISCCTRL_SCALEUP_V (1 << 29) | ||
125 | #define FIMC_REG_CISCCTRL_CSCR2Y_WIDE (1 << 28) | ||
126 | #define FIMC_REG_CISCCTRL_CSCY2R_WIDE (1 << 27) | ||
127 | #define FIMC_REG_CISCCTRL_LCDPATHEN_FIFO (1 << 26) | ||
128 | #define FIMC_REG_CISCCTRL_INTERLACE (1 << 25) | ||
129 | #define FIMC_REG_CISCCTRL_SCALERSTART (1 << 15) | ||
130 | #define FIMC_REG_CISCCTRL_INRGB_FMT_RGB565 (0 << 13) | ||
131 | #define FIMC_REG_CISCCTRL_INRGB_FMT_RGB666 (1 << 13) | ||
132 | #define FIMC_REG_CISCCTRL_INRGB_FMT_RGB888 (2 << 13) | ||
133 | #define FIMC_REG_CISCCTRL_INRGB_FMT_MASK (3 << 13) | ||
134 | #define FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11) | ||
135 | #define FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11) | ||
136 | #define FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11) | ||
137 | #define FIMC_REG_CISCCTRL_OUTRGB_FMT_MASK (3 << 11) | ||
138 | #define FIMC_REG_CISCCTRL_RGB_EXT (1 << 10) | ||
139 | #define FIMC_REG_CISCCTRL_ONE2ONE (1 << 9) | ||
140 | #define FIMC_REG_CISCCTRL_MHRATIO(x) ((x) << 16) | ||
141 | #define FIMC_REG_CISCCTRL_MVRATIO(x) ((x) << 0) | ||
142 | #define FIMC_REG_CISCCTRL_MHRATIO_MASK (0x1ff << 16) | ||
143 | #define FIMC_REG_CISCCTRL_MVRATIO_MASK (0x1ff << 0) | ||
144 | #define FIMC_REG_CISCCTRL_MHRATIO_EXT(x) (((x) >> 6) << 16) | ||
145 | #define FIMC_REG_CISCCTRL_MVRATIO_EXT(x) (((x) >> 6) << 0) | ||
146 | |||
147 | /* Target area */ | ||
148 | #define FIMC_REG_CITAREA 0x5c | ||
149 | #define FIMC_REG_CITAREA_MASK 0x0fffffff | ||
150 | |||
151 | /* General status */ | ||
152 | #define FIMC_REG_CISTATUS 0x64 | ||
153 | #define FIMC_REG_CISTATUS_OVFIY (1 << 31) | ||
154 | #define FIMC_REG_CISTATUS_OVFICB (1 << 30) | ||
155 | #define FIMC_REG_CISTATUS_OVFICR (1 << 29) | ||
156 | #define FIMC_REG_CISTATUS_VSYNC (1 << 28) | ||
157 | #define FIMC_REG_CISTATUS_FRAMECNT_MASK (3 << 26) | ||
158 | #define FIMC_REG_CISTATUS_FRAMECNT_SHIFT 26 | ||
159 | #define FIMC_REG_CISTATUS_WINOFF_EN (1 << 25) | ||
160 | #define FIMC_REG_CISTATUS_IMGCPT_EN (1 << 22) | ||
161 | #define FIMC_REG_CISTATUS_IMGCPT_SCEN (1 << 21) | ||
162 | #define FIMC_REG_CISTATUS_VSYNC_A (1 << 20) | ||
163 | #define FIMC_REG_CISTATUS_VSYNC_B (1 << 19) | ||
164 | #define FIMC_REG_CISTATUS_OVRLB (1 << 18) | ||
165 | #define FIMC_REG_CISTATUS_FRAME_END (1 << 17) | ||
166 | #define FIMC_REG_CISTATUS_LASTCAPT_END (1 << 16) | ||
167 | #define FIMC_REG_CISTATUS_VVALID_A (1 << 15) | ||
168 | #define FIMC_REG_CISTATUS_VVALID_B (1 << 14) | ||
169 | |||
170 | /* Indexes to the last and the currently processed buffer. */ | ||
171 | #define FIMC_REG_CISTATUS2 0x68 | ||
172 | |||
173 | /* Image capture control */ | ||
174 | #define FIMC_REG_CIIMGCPT 0xc0 | ||
175 | #define FIMC_REG_CIIMGCPT_IMGCPTEN (1 << 31) | ||
176 | #define FIMC_REG_CIIMGCPT_IMGCPTEN_SC (1 << 30) | ||
177 | #define FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE (1 << 25) | ||
178 | #define FIMC_REG_CIIMGCPT_CPT_FRMOD_CNT (1 << 18) | ||
179 | |||
180 | /* Frame capture sequence */ | ||
181 | #define FIMC_REG_CICPTSEQ 0xc4 | ||
182 | |||
183 | /* Image effect */ | ||
184 | #define FIMC_REG_CIIMGEFF 0xd0 | ||
185 | #define FIMC_REG_CIIMGEFF_IE_ENABLE (1 << 30) | ||
186 | #define FIMC_REG_CIIMGEFF_IE_SC_BEFORE (0 << 29) | ||
187 | #define FIMC_REG_CIIMGEFF_IE_SC_AFTER (1 << 29) | ||
188 | #define FIMC_REG_CIIMGEFF_FIN_BYPASS (0 << 26) | ||
189 | #define FIMC_REG_CIIMGEFF_FIN_ARBITRARY (1 << 26) | ||
190 | #define FIMC_REG_CIIMGEFF_FIN_NEGATIVE (2 << 26) | ||
191 | #define FIMC_REG_CIIMGEFF_FIN_ARTFREEZE (3 << 26) | ||
192 | #define FIMC_REG_CIIMGEFF_FIN_EMBOSSING (4 << 26) | ||
193 | #define FIMC_REG_CIIMGEFF_FIN_SILHOUETTE (5 << 26) | ||
194 | #define FIMC_REG_CIIMGEFF_FIN_MASK (7 << 26) | ||
195 | #define FIMC_REG_CIIMGEFF_PAT_CBCR_MASK ((0xff << 13) | 0xff) | ||
196 | |||
197 | /* Input DMA Y/Cb/Cr plane start address 0/1 */ | ||
198 | #define FIMC_REG_CIIYSA(n) (0xd4 + (n) * 0x70) | ||
199 | #define FIMC_REG_CIICBSA(n) (0xd8 + (n) * 0x70) | ||
200 | #define FIMC_REG_CIICRSA(n) (0xdc + (n) * 0x70) | ||
201 | |||
202 | /* Real input DMA image size */ | ||
203 | #define FIMC_REG_CIREAL_ISIZE 0xf8 | ||
204 | #define FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN (1 << 31) | ||
205 | #define FIMC_REG_CIREAL_ISIZE_ADDR_CH_DIS (1 << 30) | ||
206 | |||
207 | /* Input DMA control */ | ||
208 | #define FIMC_REG_MSCTRL 0xfc | ||
209 | #define FIMC_REG_MSCTRL_IN_BURST_COUNT_MASK (0xf << 24) | ||
210 | #define FIMC_REG_MSCTRL_2P_IN_ORDER_MASK (3 << 16) | ||
211 | #define FIMC_REG_MSCTRL_2P_IN_ORDER_SHIFT 16 | ||
212 | #define FIMC_REG_MSCTRL_C_INT_IN_3PLANE (0 << 15) | ||
213 | #define FIMC_REG_MSCTRL_C_INT_IN_2PLANE (1 << 15) | ||
214 | #define FIMC_REG_MSCTRL_C_INT_IN_MASK (1 << 15) | ||
215 | #define FIMC_REG_MSCTRL_FLIP_SHIFT 13 | ||
216 | #define FIMC_REG_MSCTRL_FLIP_MASK (3 << 13) | ||
217 | #define FIMC_REG_MSCTRL_FLIP_NORMAL (0 << 13) | ||
218 | #define FIMC_REG_MSCTRL_FLIP_X_MIRROR (1 << 13) | ||
219 | #define FIMC_REG_MSCTRL_FLIP_Y_MIRROR (2 << 13) | ||
220 | #define FIMC_REG_MSCTRL_FLIP_180 (3 << 13) | ||
221 | #define FIMC_REG_MSCTRL_FIFO_CTRL_FULL (1 << 12) | ||
222 | #define FIMC_REG_MSCTRL_ORDER422_SHIFT 4 | ||
223 | #define FIMC_REG_MSCTRL_ORDER422_CRYCBY (0 << 4) | ||
224 | #define FIMC_REG_MSCTRL_ORDER422_YCRYCB (1 << 4) | ||
225 | #define FIMC_REG_MSCTRL_ORDER422_CBYCRY (2 << 4) | ||
226 | #define FIMC_REG_MSCTRL_ORDER422_YCBYCR (3 << 4) | ||
227 | #define FIMC_REG_MSCTRL_ORDER422_MASK (3 << 4) | ||
228 | #define FIMC_REG_MSCTRL_INPUT_EXTCAM (0 << 3) | ||
229 | #define FIMC_REG_MSCTRL_INPUT_MEMORY (1 << 3) | ||
230 | #define FIMC_REG_MSCTRL_INPUT_MASK (1 << 3) | ||
231 | #define FIMC_REG_MSCTRL_INFORMAT_YCBCR420 (0 << 1) | ||
232 | #define FIMC_REG_MSCTRL_INFORMAT_YCBCR422 (1 << 1) | ||
233 | #define FIMC_REG_MSCTRL_INFORMAT_YCBCR422_1P (2 << 1) | ||
234 | #define FIMC_REG_MSCTRL_INFORMAT_RGB (3 << 1) | ||
235 | #define FIMC_REG_MSCTRL_INFORMAT_MASK (3 << 1) | ||
236 | #define FIMC_REG_MSCTRL_ENVID (1 << 0) | ||
237 | #define FIMC_REG_MSCTRL_IN_BURST_COUNT(x) ((x) << 24) | ||
238 | |||
239 | /* Output DMA Y/Cb/Cr offset */ | ||
240 | #define FIMC_REG_CIOYOFF 0x168 | ||
241 | #define FIMC_REG_CIOCBOFF 0x16c | ||
242 | #define FIMC_REG_CIOCROFF 0x170 | ||
243 | |||
244 | /* Input DMA Y/Cb/Cr offset */ | ||
245 | #define FIMC_REG_CIIYOFF 0x174 | ||
246 | #define FIMC_REG_CIICBOFF 0x178 | ||
247 | #define FIMC_REG_CIICROFF 0x17c | ||
248 | |||
249 | /* Input DMA original image size */ | ||
250 | #define FIMC_REG_ORGISIZE 0x180 | ||
251 | |||
252 | /* Output DMA original image size */ | ||
253 | #define FIMC_REG_ORGOSIZE 0x184 | ||
254 | |||
255 | /* Real output DMA image size (extension register) */ | ||
256 | #define FIMC_REG_CIEXTEN 0x188 | ||
257 | #define FIMC_REG_CIEXTEN_MHRATIO_EXT(x) (((x) & 0x3f) << 10) | ||
258 | #define FIMC_REG_CIEXTEN_MVRATIO_EXT(x) ((x) & 0x3f) | ||
259 | #define FIMC_REG_CIEXTEN_MHRATIO_EXT_MASK (0x3f << 10) | ||
260 | #define FIMC_REG_CIEXTEN_MVRATIO_EXT_MASK 0x3f | ||
261 | |||
262 | #define FIMC_REG_CIDMAPARAM 0x18c | ||
263 | #define FIMC_REG_CIDMAPARAM_R_LINEAR (0 << 29) | ||
264 | #define FIMC_REG_CIDMAPARAM_R_64X32 (3 << 29) | ||
265 | #define FIMC_REG_CIDMAPARAM_W_LINEAR (0 << 13) | ||
266 | #define FIMC_REG_CIDMAPARAM_W_64X32 (3 << 13) | ||
267 | #define FIMC_REG_CIDMAPARAM_TILE_MASK ((3 << 29) | (3 << 13)) | ||
268 | |||
269 | /* MIPI CSI image format */ | ||
270 | #define FIMC_REG_CSIIMGFMT 0x194 | ||
271 | #define FIMC_REG_CSIIMGFMT_YCBCR422_8BIT 0x1e | ||
272 | #define FIMC_REG_CSIIMGFMT_RAW8 0x2a | ||
273 | #define FIMC_REG_CSIIMGFMT_RAW10 0x2b | ||
274 | #define FIMC_REG_CSIIMGFMT_RAW12 0x2c | ||
275 | /* User defined formats. x = 0...16. */ | ||
276 | #define FIMC_REG_CSIIMGFMT_USER(x) (0x30 + x - 1) | ||
277 | |||
278 | /* Output frame buffer sequence mask */ | ||
279 | #define FIMC_REG_CIFCNTSEQ 0x1fc | ||
280 | |||
281 | /* SYSREG ISP Writeback register address offsets */ | ||
282 | #define SYSREG_ISPBLK 0x020c | ||
283 | #define SYSREG_ISPBLK_FIFORST_CAM_BLK (1 << 7) | ||
284 | |||
285 | #define SYSREG_CAMBLK 0x0218 | ||
286 | #define SYSREG_CAMBLK_FIFORST_ISP (1 << 15) | ||
287 | #define SYSREG_CAMBLK_ISPWB_FULL_EN (7 << 20) | ||
288 | |||
289 | /* | ||
290 | * Function declarations | ||
291 | */ | ||
292 | void fimc_hw_reset(struct fimc_dev *fimc); | ||
293 | void fimc_hw_set_rotation(struct fimc_ctx *ctx); | ||
294 | void fimc_hw_set_target_format(struct fimc_ctx *ctx); | ||
295 | void fimc_hw_set_out_dma(struct fimc_ctx *ctx); | ||
296 | void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable); | ||
297 | void fimc_hw_en_irq(struct fimc_dev *fimc, int enable); | ||
298 | void fimc_hw_set_prescaler(struct fimc_ctx *ctx); | ||
299 | void fimc_hw_set_mainscaler(struct fimc_ctx *ctx); | ||
300 | void fimc_hw_enable_capture(struct fimc_ctx *ctx); | ||
301 | void fimc_hw_set_effect(struct fimc_ctx *ctx); | ||
302 | void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx); | ||
303 | void fimc_hw_set_in_dma(struct fimc_ctx *ctx); | ||
304 | void fimc_hw_set_input_path(struct fimc_ctx *ctx); | ||
305 | void fimc_hw_set_output_path(struct fimc_ctx *ctx); | ||
306 | void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr); | ||
307 | void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr, | ||
308 | int index); | ||
309 | int fimc_hw_set_camera_source(struct fimc_dev *fimc, | ||
310 | struct fimc_source_info *cam); | ||
311 | void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f); | ||
312 | int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, | ||
313 | struct fimc_source_info *cam); | ||
314 | int fimc_hw_set_camera_type(struct fimc_dev *fimc, | ||
315 | struct fimc_source_info *cam); | ||
316 | void fimc_hw_clear_irq(struct fimc_dev *dev); | ||
317 | void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on); | ||
318 | void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on); | ||
319 | void fimc_hw_disable_capture(struct fimc_dev *dev); | ||
320 | s32 fimc_hw_get_frame_index(struct fimc_dev *dev); | ||
321 | s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev); | ||
322 | int fimc_hw_camblk_cfg_writeback(struct fimc_dev *fimc); | ||
323 | void fimc_activate_capture(struct fimc_ctx *ctx); | ||
324 | void fimc_deactivate_capture(struct fimc_dev *fimc); | ||
325 | |||
326 | /** | ||
327 | * fimc_hw_set_dma_seq - configure output DMA buffer sequence | ||
328 | * @mask: bitmask for the DMA output buffer registers, set to 0 to skip buffer | ||
329 | * This function masks output DMA ring buffers, it allows to select which of | ||
330 | * the 32 available output buffer address registers will be used by the DMA | ||
331 | * engine. | ||
332 | */ | ||
333 | static inline void fimc_hw_set_dma_seq(struct fimc_dev *dev, u32 mask) | ||
334 | { | ||
335 | writel(mask, dev->regs + FIMC_REG_CIFCNTSEQ); | ||
336 | } | ||
337 | |||
338 | #endif /* FIMC_REG_H_ */ | ||
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c new file mode 100644 index 000000000000..15ef8f28239b --- /dev/null +++ b/drivers/media/platform/exynos4-is/media-dev.c | |||
@@ -0,0 +1,1511 @@ | |||
1 | /* | ||
2 | * S5P/EXYNOS4 SoC series camera host interface media device driver | ||
3 | * | ||
4 | * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. | ||
5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published | ||
9 | * by the Free Software Foundation, either version 2 of the License, | ||
10 | * or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/bug.h> | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/i2c.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/list.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <linux/of_platform.h> | ||
22 | #include <linux/of_device.h> | ||
23 | #include <linux/of_i2c.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/pm_runtime.h> | ||
26 | #include <linux/types.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <media/v4l2-ctrls.h> | ||
29 | #include <media/v4l2-of.h> | ||
30 | #include <media/media-device.h> | ||
31 | #include <media/s5p_fimc.h> | ||
32 | |||
33 | #include "media-dev.h" | ||
34 | #include "fimc-core.h" | ||
35 | #include "fimc-is.h" | ||
36 | #include "fimc-lite.h" | ||
37 | #include "mipi-csis.h" | ||
38 | |||
39 | static int __fimc_md_set_camclk(struct fimc_md *fmd, | ||
40 | struct fimc_source_info *si, | ||
41 | bool on); | ||
42 | /** | ||
43 | * fimc_pipeline_prepare - update pipeline information with subdevice pointers | ||
44 | * @me: media entity terminating the pipeline | ||
45 | * | ||
46 | * Caller holds the graph mutex. | ||
47 | */ | ||
48 | static void fimc_pipeline_prepare(struct fimc_pipeline *p, | ||
49 | struct media_entity *me) | ||
50 | { | ||
51 | struct v4l2_subdev *sd; | ||
52 | int i; | ||
53 | |||
54 | for (i = 0; i < IDX_MAX; i++) | ||
55 | p->subdevs[i] = NULL; | ||
56 | |||
57 | while (1) { | ||
58 | struct media_pad *pad = NULL; | ||
59 | |||
60 | /* Find remote source pad */ | ||
61 | for (i = 0; i < me->num_pads; i++) { | ||
62 | struct media_pad *spad = &me->pads[i]; | ||
63 | if (!(spad->flags & MEDIA_PAD_FL_SINK)) | ||
64 | continue; | ||
65 | pad = media_entity_remote_source(spad); | ||
66 | if (pad) | ||
67 | break; | ||
68 | } | ||
69 | |||
70 | if (pad == NULL || | ||
71 | media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
72 | break; | ||
73 | sd = media_entity_to_v4l2_subdev(pad->entity); | ||
74 | |||
75 | switch (sd->grp_id) { | ||
76 | case GRP_ID_FIMC_IS_SENSOR: | ||
77 | case GRP_ID_SENSOR: | ||
78 | p->subdevs[IDX_SENSOR] = sd; | ||
79 | break; | ||
80 | case GRP_ID_CSIS: | ||
81 | p->subdevs[IDX_CSIS] = sd; | ||
82 | break; | ||
83 | case GRP_ID_FLITE: | ||
84 | p->subdevs[IDX_FLITE] = sd; | ||
85 | break; | ||
86 | case GRP_ID_FIMC: | ||
87 | /* No need to control FIMC subdev through subdev ops */ | ||
88 | break; | ||
89 | case GRP_ID_FIMC_IS: | ||
90 | p->subdevs[IDX_IS_ISP] = sd; | ||
91 | break; | ||
92 | default: | ||
93 | break; | ||
94 | } | ||
95 | me = &sd->entity; | ||
96 | if (me->num_pads == 1) | ||
97 | break; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * __subdev_set_power - change power state of a single subdev | ||
103 | * @sd: subdevice to change power state for | ||
104 | * @on: 1 to enable power or 0 to disable | ||
105 | * | ||
106 | * Return result of s_power subdev operation or -ENXIO if sd argument | ||
107 | * is NULL. Return 0 if the subdevice does not implement s_power. | ||
108 | */ | ||
109 | static int __subdev_set_power(struct v4l2_subdev *sd, int on) | ||
110 | { | ||
111 | int *use_count; | ||
112 | int ret; | ||
113 | |||
114 | if (sd == NULL) | ||
115 | return -ENXIO; | ||
116 | |||
117 | use_count = &sd->entity.use_count; | ||
118 | if (on && (*use_count)++ > 0) | ||
119 | return 0; | ||
120 | else if (!on && (*use_count == 0 || --(*use_count) > 0)) | ||
121 | return 0; | ||
122 | ret = v4l2_subdev_call(sd, core, s_power, on); | ||
123 | |||
124 | return ret != -ENOIOCTLCMD ? ret : 0; | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * fimc_pipeline_s_power - change power state of all pipeline subdevs | ||
129 | * @fimc: fimc device terminating the pipeline | ||
130 | * @state: true to power on, false to power off | ||
131 | * | ||
132 | * Needs to be called with the graph mutex held. | ||
133 | */ | ||
134 | static int fimc_pipeline_s_power(struct fimc_pipeline *p, bool on) | ||
135 | { | ||
136 | static const u8 seq[2][IDX_MAX - 1] = { | ||
137 | { IDX_IS_ISP, IDX_SENSOR, IDX_CSIS, IDX_FLITE }, | ||
138 | { IDX_CSIS, IDX_FLITE, IDX_SENSOR, IDX_IS_ISP }, | ||
139 | }; | ||
140 | int i, ret = 0; | ||
141 | |||
142 | if (p->subdevs[IDX_SENSOR] == NULL) | ||
143 | return -ENXIO; | ||
144 | |||
145 | for (i = 0; i < IDX_MAX - 1; i++) { | ||
146 | unsigned int idx = seq[on][i]; | ||
147 | |||
148 | ret = __subdev_set_power(p->subdevs[idx], on); | ||
149 | |||
150 | |||
151 | if (ret < 0 && ret != -ENXIO) | ||
152 | goto error; | ||
153 | } | ||
154 | return 0; | ||
155 | error: | ||
156 | for (; i >= 0; i--) { | ||
157 | unsigned int idx = seq[on][i]; | ||
158 | __subdev_set_power(p->subdevs[idx], !on); | ||
159 | } | ||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | /** | ||
164 | * __fimc_pipeline_open - update the pipeline information, enable power | ||
165 | * of all pipeline subdevs and the sensor clock | ||
166 | * @me: media entity to start graph walk with | ||
167 | * @prepare: true to walk the current pipeline and acquire all subdevs | ||
168 | * | ||
169 | * Called with the graph mutex held. | ||
170 | */ | ||
171 | static int __fimc_pipeline_open(struct fimc_pipeline *p, | ||
172 | struct media_entity *me, bool prepare) | ||
173 | { | ||
174 | struct fimc_md *fmd = entity_to_fimc_mdev(me); | ||
175 | struct v4l2_subdev *sd; | ||
176 | int ret; | ||
177 | |||
178 | if (WARN_ON(p == NULL || me == NULL)) | ||
179 | return -EINVAL; | ||
180 | |||
181 | if (prepare) | ||
182 | fimc_pipeline_prepare(p, me); | ||
183 | |||
184 | sd = p->subdevs[IDX_SENSOR]; | ||
185 | if (sd == NULL) | ||
186 | return -EINVAL; | ||
187 | |||
188 | /* Disable PXLASYNC clock if this pipeline includes FIMC-IS */ | ||
189 | if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) { | ||
190 | ret = clk_prepare_enable(fmd->wbclk[CLK_IDX_WB_B]); | ||
191 | if (ret < 0) | ||
192 | return ret; | ||
193 | } | ||
194 | ret = fimc_md_set_camclk(sd, true); | ||
195 | if (ret < 0) | ||
196 | goto err_wbclk; | ||
197 | |||
198 | ret = fimc_pipeline_s_power(p, 1); | ||
199 | if (!ret) | ||
200 | return 0; | ||
201 | |||
202 | fimc_md_set_camclk(sd, false); | ||
203 | |||
204 | err_wbclk: | ||
205 | if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) | ||
206 | clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]); | ||
207 | |||
208 | return ret; | ||
209 | } | ||
210 | |||
211 | /** | ||
212 | * __fimc_pipeline_close - disable the sensor clock and pipeline power | ||
213 | * @fimc: fimc device terminating the pipeline | ||
214 | * | ||
215 | * Disable power of all subdevs and turn the external sensor clock off. | ||
216 | */ | ||
217 | static int __fimc_pipeline_close(struct fimc_pipeline *p) | ||
218 | { | ||
219 | struct v4l2_subdev *sd = p ? p->subdevs[IDX_SENSOR] : NULL; | ||
220 | struct fimc_md *fmd; | ||
221 | int ret = 0; | ||
222 | |||
223 | if (WARN_ON(sd == NULL)) | ||
224 | return -EINVAL; | ||
225 | |||
226 | if (p->subdevs[IDX_SENSOR]) { | ||
227 | ret = fimc_pipeline_s_power(p, 0); | ||
228 | fimc_md_set_camclk(sd, false); | ||
229 | } | ||
230 | |||
231 | fmd = entity_to_fimc_mdev(&sd->entity); | ||
232 | |||
233 | /* Disable PXLASYNC clock if this pipeline includes FIMC-IS */ | ||
234 | if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) | ||
235 | clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]); | ||
236 | |||
237 | return ret == -ENXIO ? 0 : ret; | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * __fimc_pipeline_s_stream - call s_stream() on pipeline subdevs | ||
242 | * @pipeline: video pipeline structure | ||
243 | * @on: passed as the s_stream() callback argument | ||
244 | */ | ||
245 | static int __fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) | ||
246 | { | ||
247 | static const u8 seq[2][IDX_MAX] = { | ||
248 | { IDX_FIMC, IDX_SENSOR, IDX_IS_ISP, IDX_CSIS, IDX_FLITE }, | ||
249 | { IDX_CSIS, IDX_FLITE, IDX_FIMC, IDX_SENSOR, IDX_IS_ISP }, | ||
250 | }; | ||
251 | int i, ret = 0; | ||
252 | |||
253 | if (p->subdevs[IDX_SENSOR] == NULL) | ||
254 | return -ENODEV; | ||
255 | |||
256 | for (i = 0; i < IDX_MAX; i++) { | ||
257 | unsigned int idx = seq[on][i]; | ||
258 | |||
259 | ret = v4l2_subdev_call(p->subdevs[idx], video, s_stream, on); | ||
260 | |||
261 | if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) | ||
262 | goto error; | ||
263 | } | ||
264 | return 0; | ||
265 | error: | ||
266 | for (; i >= 0; i--) { | ||
267 | unsigned int idx = seq[on][i]; | ||
268 | v4l2_subdev_call(p->subdevs[idx], video, s_stream, !on); | ||
269 | } | ||
270 | return ret; | ||
271 | } | ||
272 | |||
273 | /* Media pipeline operations for the FIMC/FIMC-LITE video device driver */ | ||
274 | static const struct fimc_pipeline_ops fimc_pipeline_ops = { | ||
275 | .open = __fimc_pipeline_open, | ||
276 | .close = __fimc_pipeline_close, | ||
277 | .set_stream = __fimc_pipeline_s_stream, | ||
278 | }; | ||
279 | |||
280 | /* | ||
281 | * Sensor subdevice helper functions | ||
282 | */ | ||
283 | static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, | ||
284 | struct fimc_source_info *si) | ||
285 | { | ||
286 | struct i2c_adapter *adapter; | ||
287 | struct v4l2_subdev *sd = NULL; | ||
288 | |||
289 | if (!si || !fmd) | ||
290 | return NULL; | ||
291 | /* | ||
292 | * If FIMC bus type is not Writeback FIFO assume it is same | ||
293 | * as sensor_bus_type. | ||
294 | */ | ||
295 | si->fimc_bus_type = si->sensor_bus_type; | ||
296 | |||
297 | adapter = i2c_get_adapter(si->i2c_bus_num); | ||
298 | if (!adapter) { | ||
299 | v4l2_warn(&fmd->v4l2_dev, | ||
300 | "Failed to get I2C adapter %d, deferring probe\n", | ||
301 | si->i2c_bus_num); | ||
302 | return ERR_PTR(-EPROBE_DEFER); | ||
303 | } | ||
304 | sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter, | ||
305 | si->board_info, NULL); | ||
306 | if (IS_ERR_OR_NULL(sd)) { | ||
307 | i2c_put_adapter(adapter); | ||
308 | v4l2_warn(&fmd->v4l2_dev, | ||
309 | "Failed to acquire subdev %s, deferring probe\n", | ||
310 | si->board_info->type); | ||
311 | return ERR_PTR(-EPROBE_DEFER); | ||
312 | } | ||
313 | v4l2_set_subdev_hostdata(sd, si); | ||
314 | sd->grp_id = GRP_ID_SENSOR; | ||
315 | |||
316 | v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n", | ||
317 | sd->name); | ||
318 | return sd; | ||
319 | } | ||
320 | |||
321 | static void fimc_md_unregister_sensor(struct v4l2_subdev *sd) | ||
322 | { | ||
323 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
324 | struct i2c_adapter *adapter; | ||
325 | |||
326 | if (!client) | ||
327 | return; | ||
328 | |||
329 | v4l2_device_unregister_subdev(sd); | ||
330 | |||
331 | if (!client->dev.of_node) { | ||
332 | adapter = client->adapter; | ||
333 | i2c_unregister_device(client); | ||
334 | if (adapter) | ||
335 | i2c_put_adapter(adapter); | ||
336 | } | ||
337 | } | ||
338 | |||
339 | #ifdef CONFIG_OF | ||
340 | /* Register I2C client subdev associated with @node. */ | ||
341 | static int fimc_md_of_add_sensor(struct fimc_md *fmd, | ||
342 | struct device_node *node, int index) | ||
343 | { | ||
344 | struct fimc_sensor_info *si; | ||
345 | struct i2c_client *client; | ||
346 | struct v4l2_subdev *sd; | ||
347 | int ret; | ||
348 | |||
349 | if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) | ||
350 | return -EINVAL; | ||
351 | si = &fmd->sensor[index]; | ||
352 | |||
353 | client = of_find_i2c_device_by_node(node); | ||
354 | if (!client) | ||
355 | return -EPROBE_DEFER; | ||
356 | |||
357 | device_lock(&client->dev); | ||
358 | |||
359 | if (!client->driver || | ||
360 | !try_module_get(client->driver->driver.owner)) { | ||
361 | ret = -EPROBE_DEFER; | ||
362 | v4l2_info(&fmd->v4l2_dev, "No driver found for %s\n", | ||
363 | node->full_name); | ||
364 | goto dev_put; | ||
365 | } | ||
366 | |||
367 | /* Enable sensor's master clock */ | ||
368 | ret = __fimc_md_set_camclk(fmd, &si->pdata, true); | ||
369 | if (ret < 0) | ||
370 | goto mod_put; | ||
371 | sd = i2c_get_clientdata(client); | ||
372 | |||
373 | ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); | ||
374 | __fimc_md_set_camclk(fmd, &si->pdata, false); | ||
375 | if (ret < 0) | ||
376 | goto mod_put; | ||
377 | |||
378 | v4l2_set_subdev_hostdata(sd, &si->pdata); | ||
379 | if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) | ||
380 | sd->grp_id = GRP_ID_FIMC_IS_SENSOR; | ||
381 | else | ||
382 | sd->grp_id = GRP_ID_SENSOR; | ||
383 | |||
384 | si->subdev = sd; | ||
385 | v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n", | ||
386 | sd->name, fmd->num_sensors); | ||
387 | fmd->num_sensors++; | ||
388 | |||
389 | mod_put: | ||
390 | module_put(client->driver->driver.owner); | ||
391 | dev_put: | ||
392 | device_unlock(&client->dev); | ||
393 | put_device(&client->dev); | ||
394 | return ret; | ||
395 | } | ||
396 | |||
397 | /* Parse port node and register as a sub-device any sensor specified there. */ | ||
398 | static int fimc_md_parse_port_node(struct fimc_md *fmd, | ||
399 | struct device_node *port, | ||
400 | unsigned int index) | ||
401 | { | ||
402 | struct device_node *rem, *ep, *np; | ||
403 | struct fimc_source_info *pd; | ||
404 | struct v4l2_of_endpoint endpoint; | ||
405 | int ret; | ||
406 | u32 val; | ||
407 | |||
408 | pd = &fmd->sensor[index].pdata; | ||
409 | |||
410 | /* Assume here a port node can have only one endpoint node. */ | ||
411 | ep = of_get_next_child(port, NULL); | ||
412 | if (!ep) | ||
413 | return 0; | ||
414 | |||
415 | v4l2_of_parse_endpoint(ep, &endpoint); | ||
416 | if (WARN_ON(endpoint.port == 0) || index >= FIMC_MAX_SENSORS) | ||
417 | return -EINVAL; | ||
418 | |||
419 | pd->mux_id = (endpoint.port - 1) & 0x1; | ||
420 | |||
421 | rem = v4l2_of_get_remote_port_parent(ep); | ||
422 | of_node_put(ep); | ||
423 | if (rem == NULL) { | ||
424 | v4l2_info(&fmd->v4l2_dev, "Remote device at %s not found\n", | ||
425 | ep->full_name); | ||
426 | return 0; | ||
427 | } | ||
428 | if (!of_property_read_u32(rem, "samsung,camclk-out", &val)) | ||
429 | pd->clk_id = val; | ||
430 | |||
431 | if (!of_property_read_u32(rem, "clock-frequency", &val)) | ||
432 | pd->clk_frequency = val; | ||
433 | |||
434 | if (pd->clk_frequency == 0) { | ||
435 | v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n", | ||
436 | rem->full_name); | ||
437 | of_node_put(rem); | ||
438 | return -EINVAL; | ||
439 | } | ||
440 | |||
441 | if (fimc_input_is_parallel(endpoint.port)) { | ||
442 | if (endpoint.bus_type == V4L2_MBUS_PARALLEL) | ||
443 | pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_601; | ||
444 | else | ||
445 | pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_656; | ||
446 | pd->flags = endpoint.bus.parallel.flags; | ||
447 | } else if (fimc_input_is_mipi_csi(endpoint.port)) { | ||
448 | /* | ||
449 | * MIPI CSI-2: only input mux selection and | ||
450 | * the sensor's clock frequency is needed. | ||
451 | */ | ||
452 | pd->sensor_bus_type = FIMC_BUS_TYPE_MIPI_CSI2; | ||
453 | } else { | ||
454 | v4l2_err(&fmd->v4l2_dev, "Wrong port id (%u) at node %s\n", | ||
455 | endpoint.port, rem->full_name); | ||
456 | } | ||
457 | /* | ||
458 | * For FIMC-IS handled sensors, that are placed under i2c-isp device | ||
459 | * node, FIMC is connected to the FIMC-IS through its ISP Writeback | ||
460 | * input. Sensors are attached to the FIMC-LITE hostdata interface | ||
461 | * directly or through MIPI-CSIS, depending on the external media bus | ||
462 | * used. This needs to be handled in a more reliable way, not by just | ||
463 | * checking parent's node name. | ||
464 | */ | ||
465 | np = of_get_parent(rem); | ||
466 | |||
467 | if (np && !of_node_cmp(np->name, "i2c-isp")) | ||
468 | pd->fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK; | ||
469 | else | ||
470 | pd->fimc_bus_type = pd->sensor_bus_type; | ||
471 | |||
472 | ret = fimc_md_of_add_sensor(fmd, rem, index); | ||
473 | of_node_put(rem); | ||
474 | |||
475 | return ret; | ||
476 | } | ||
477 | |||
478 | /* Register all SoC external sub-devices */ | ||
479 | static int fimc_md_of_sensors_register(struct fimc_md *fmd, | ||
480 | struct device_node *np) | ||
481 | { | ||
482 | struct device_node *parent = fmd->pdev->dev.of_node; | ||
483 | struct device_node *node, *ports; | ||
484 | int index = 0; | ||
485 | int ret; | ||
486 | |||
487 | /* Attach sensors linked to MIPI CSI-2 receivers */ | ||
488 | for_each_available_child_of_node(parent, node) { | ||
489 | struct device_node *port; | ||
490 | |||
491 | if (of_node_cmp(node->name, "csis")) | ||
492 | continue; | ||
493 | /* The csis node can have only port subnode. */ | ||
494 | port = of_get_next_child(node, NULL); | ||
495 | if (!port) | ||
496 | continue; | ||
497 | |||
498 | ret = fimc_md_parse_port_node(fmd, port, index); | ||
499 | if (ret < 0) | ||
500 | return ret; | ||
501 | index++; | ||
502 | } | ||
503 | |||
504 | /* Attach sensors listed in the parallel-ports node */ | ||
505 | ports = of_get_child_by_name(parent, "parallel-ports"); | ||
506 | if (!ports) | ||
507 | return 0; | ||
508 | |||
509 | for_each_child_of_node(ports, node) { | ||
510 | ret = fimc_md_parse_port_node(fmd, node, index); | ||
511 | if (ret < 0) | ||
512 | break; | ||
513 | index++; | ||
514 | } | ||
515 | |||
516 | return 0; | ||
517 | } | ||
518 | |||
519 | static int __of_get_csis_id(struct device_node *np) | ||
520 | { | ||
521 | u32 reg = 0; | ||
522 | |||
523 | np = of_get_child_by_name(np, "port"); | ||
524 | if (!np) | ||
525 | return -EINVAL; | ||
526 | of_property_read_u32(np, "reg", ®); | ||
527 | return reg - FIMC_INPUT_MIPI_CSI2_0; | ||
528 | } | ||
529 | #else | ||
530 | #define fimc_md_of_sensors_register(fmd, np) (-ENOSYS) | ||
531 | #define __of_get_csis_id(np) (-ENOSYS) | ||
532 | #endif | ||
533 | |||
534 | static int fimc_md_register_sensor_entities(struct fimc_md *fmd) | ||
535 | { | ||
536 | struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; | ||
537 | struct device_node *of_node = fmd->pdev->dev.of_node; | ||
538 | int num_clients = 0; | ||
539 | int ret, i; | ||
540 | |||
541 | /* | ||
542 | * Runtime resume one of the FIMC entities to make sure | ||
543 | * the sclk_cam clocks are not globally disabled. | ||
544 | */ | ||
545 | if (!fmd->pmf) | ||
546 | return -ENXIO; | ||
547 | |||
548 | ret = pm_runtime_get_sync(fmd->pmf); | ||
549 | if (ret < 0) | ||
550 | return ret; | ||
551 | |||
552 | if (of_node) { | ||
553 | fmd->num_sensors = 0; | ||
554 | ret = fimc_md_of_sensors_register(fmd, of_node); | ||
555 | } else if (pdata) { | ||
556 | WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor)); | ||
557 | num_clients = min_t(u32, pdata->num_clients, | ||
558 | ARRAY_SIZE(fmd->sensor)); | ||
559 | fmd->num_sensors = num_clients; | ||
560 | |||
561 | for (i = 0; i < num_clients; i++) { | ||
562 | struct fimc_sensor_info *si = &fmd->sensor[i]; | ||
563 | struct v4l2_subdev *sd; | ||
564 | |||
565 | si->pdata = pdata->source_info[i]; | ||
566 | ret = __fimc_md_set_camclk(fmd, &si->pdata, true); | ||
567 | if (ret) | ||
568 | break; | ||
569 | sd = fimc_md_register_sensor(fmd, &si->pdata); | ||
570 | ret = __fimc_md_set_camclk(fmd, &si->pdata, false); | ||
571 | |||
572 | if (IS_ERR(sd)) { | ||
573 | si->subdev = NULL; | ||
574 | ret = PTR_ERR(sd); | ||
575 | break; | ||
576 | } | ||
577 | si->subdev = sd; | ||
578 | if (ret) | ||
579 | break; | ||
580 | } | ||
581 | } | ||
582 | |||
583 | pm_runtime_put(fmd->pmf); | ||
584 | return ret; | ||
585 | } | ||
586 | |||
587 | /* | ||
588 | * MIPI-CSIS, FIMC and FIMC-LITE platform devices registration. | ||
589 | */ | ||
590 | |||
591 | static int register_fimc_lite_entity(struct fimc_md *fmd, | ||
592 | struct fimc_lite *fimc_lite) | ||
593 | { | ||
594 | struct v4l2_subdev *sd; | ||
595 | int ret; | ||
596 | |||
597 | if (WARN_ON(fimc_lite->index >= FIMC_LITE_MAX_DEVS || | ||
598 | fmd->fimc_lite[fimc_lite->index])) | ||
599 | return -EBUSY; | ||
600 | |||
601 | sd = &fimc_lite->subdev; | ||
602 | sd->grp_id = GRP_ID_FLITE; | ||
603 | v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops); | ||
604 | |||
605 | ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); | ||
606 | if (!ret) | ||
607 | fmd->fimc_lite[fimc_lite->index] = fimc_lite; | ||
608 | else | ||
609 | v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.LITE%d\n", | ||
610 | fimc_lite->index); | ||
611 | return ret; | ||
612 | } | ||
613 | |||
614 | static int register_fimc_entity(struct fimc_md *fmd, struct fimc_dev *fimc) | ||
615 | { | ||
616 | struct v4l2_subdev *sd; | ||
617 | int ret; | ||
618 | |||
619 | if (WARN_ON(fimc->id >= FIMC_MAX_DEVS || fmd->fimc[fimc->id])) | ||
620 | return -EBUSY; | ||
621 | |||
622 | sd = &fimc->vid_cap.subdev; | ||
623 | sd->grp_id = GRP_ID_FIMC; | ||
624 | v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops); | ||
625 | |||
626 | ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); | ||
627 | if (!ret) { | ||
628 | if (!fmd->pmf && fimc->pdev) | ||
629 | fmd->pmf = &fimc->pdev->dev; | ||
630 | fmd->fimc[fimc->id] = fimc; | ||
631 | fimc->vid_cap.user_subdev_api = fmd->user_subdev_api; | ||
632 | } else { | ||
633 | v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n", | ||
634 | fimc->id, ret); | ||
635 | } | ||
636 | return ret; | ||
637 | } | ||
638 | |||
639 | static int register_csis_entity(struct fimc_md *fmd, | ||
640 | struct platform_device *pdev, | ||
641 | struct v4l2_subdev *sd) | ||
642 | { | ||
643 | struct device_node *node = pdev->dev.of_node; | ||
644 | int id, ret; | ||
645 | |||
646 | id = node ? __of_get_csis_id(node) : max(0, pdev->id); | ||
647 | |||
648 | if (WARN_ON(id < 0 || id >= CSIS_MAX_ENTITIES)) | ||
649 | return -ENOENT; | ||
650 | |||
651 | if (WARN_ON(fmd->csis[id].sd)) | ||
652 | return -EBUSY; | ||
653 | |||
654 | sd->grp_id = GRP_ID_CSIS; | ||
655 | ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); | ||
656 | if (!ret) | ||
657 | fmd->csis[id].sd = sd; | ||
658 | else | ||
659 | v4l2_err(&fmd->v4l2_dev, | ||
660 | "Failed to register MIPI-CSIS.%d (%d)\n", id, ret); | ||
661 | return ret; | ||
662 | } | ||
663 | |||
664 | static int register_fimc_is_entity(struct fimc_md *fmd, struct fimc_is *is) | ||
665 | { | ||
666 | struct v4l2_subdev *sd = &is->isp.subdev; | ||
667 | int ret; | ||
668 | |||
669 | ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); | ||
670 | if (ret) { | ||
671 | v4l2_err(&fmd->v4l2_dev, | ||
672 | "Failed to register FIMC-ISP (%d)\n", ret); | ||
673 | return ret; | ||
674 | } | ||
675 | |||
676 | fmd->fimc_is = is; | ||
677 | return 0; | ||
678 | } | ||
679 | |||
680 | static int fimc_md_register_platform_entity(struct fimc_md *fmd, | ||
681 | struct platform_device *pdev, | ||
682 | int plat_entity) | ||
683 | { | ||
684 | struct device *dev = &pdev->dev; | ||
685 | int ret = -EPROBE_DEFER; | ||
686 | void *drvdata; | ||
687 | |||
688 | /* Lock to ensure dev->driver won't change. */ | ||
689 | device_lock(dev); | ||
690 | |||
691 | if (!dev->driver || !try_module_get(dev->driver->owner)) | ||
692 | goto dev_unlock; | ||
693 | |||
694 | drvdata = dev_get_drvdata(dev); | ||
695 | /* Some subdev didn't probe succesfully id drvdata is NULL */ | ||
696 | if (drvdata) { | ||
697 | switch (plat_entity) { | ||
698 | case IDX_FIMC: | ||
699 | ret = register_fimc_entity(fmd, drvdata); | ||
700 | break; | ||
701 | case IDX_FLITE: | ||
702 | ret = register_fimc_lite_entity(fmd, drvdata); | ||
703 | break; | ||
704 | case IDX_CSIS: | ||
705 | ret = register_csis_entity(fmd, pdev, drvdata); | ||
706 | break; | ||
707 | case IDX_IS_ISP: | ||
708 | ret = register_fimc_is_entity(fmd, drvdata); | ||
709 | break; | ||
710 | default: | ||
711 | ret = -ENODEV; | ||
712 | } | ||
713 | } | ||
714 | |||
715 | module_put(dev->driver->owner); | ||
716 | dev_unlock: | ||
717 | device_unlock(dev); | ||
718 | if (ret == -EPROBE_DEFER) | ||
719 | dev_info(&fmd->pdev->dev, "deferring %s device registration\n", | ||
720 | dev_name(dev)); | ||
721 | else if (ret < 0) | ||
722 | dev_err(&fmd->pdev->dev, "%s device registration failed (%d)\n", | ||
723 | dev_name(dev), ret); | ||
724 | return ret; | ||
725 | } | ||
726 | |||
727 | static int fimc_md_pdev_match(struct device *dev, void *data) | ||
728 | { | ||
729 | struct platform_device *pdev = to_platform_device(dev); | ||
730 | int plat_entity = -1; | ||
731 | int ret; | ||
732 | char *p; | ||
733 | |||
734 | if (!get_device(dev)) | ||
735 | return -ENODEV; | ||
736 | |||
737 | if (!strcmp(pdev->name, CSIS_DRIVER_NAME)) { | ||
738 | plat_entity = IDX_CSIS; | ||
739 | } else if (!strcmp(pdev->name, FIMC_LITE_DRV_NAME)) { | ||
740 | plat_entity = IDX_FLITE; | ||
741 | } else { | ||
742 | p = strstr(pdev->name, "fimc"); | ||
743 | if (p && *(p + 4) == 0) | ||
744 | plat_entity = IDX_FIMC; | ||
745 | } | ||
746 | |||
747 | if (plat_entity >= 0) | ||
748 | ret = fimc_md_register_platform_entity(data, pdev, | ||
749 | plat_entity); | ||
750 | put_device(dev); | ||
751 | return 0; | ||
752 | } | ||
753 | |||
754 | /* Register FIMC, FIMC-LITE and CSIS media entities */ | ||
755 | #ifdef CONFIG_OF | ||
756 | static int fimc_md_register_of_platform_entities(struct fimc_md *fmd, | ||
757 | struct device_node *parent) | ||
758 | { | ||
759 | struct device_node *node; | ||
760 | int ret = 0; | ||
761 | |||
762 | for_each_available_child_of_node(parent, node) { | ||
763 | struct platform_device *pdev; | ||
764 | int plat_entity = -1; | ||
765 | |||
766 | pdev = of_find_device_by_node(node); | ||
767 | if (!pdev) | ||
768 | continue; | ||
769 | |||
770 | /* If driver of any entity isn't ready try all again later. */ | ||
771 | if (!strcmp(node->name, CSIS_OF_NODE_NAME)) | ||
772 | plat_entity = IDX_CSIS; | ||
773 | else if (!strcmp(node->name, FIMC_IS_OF_NODE_NAME)) | ||
774 | plat_entity = IDX_IS_ISP; | ||
775 | else if (!strcmp(node->name, FIMC_LITE_OF_NODE_NAME)) | ||
776 | plat_entity = IDX_FLITE; | ||
777 | else if (!strcmp(node->name, FIMC_OF_NODE_NAME) && | ||
778 | !of_property_read_bool(node, "samsung,lcd-wb")) | ||
779 | plat_entity = IDX_FIMC; | ||
780 | |||
781 | if (plat_entity >= 0) | ||
782 | ret = fimc_md_register_platform_entity(fmd, pdev, | ||
783 | plat_entity); | ||
784 | put_device(&pdev->dev); | ||
785 | if (ret < 0) | ||
786 | break; | ||
787 | } | ||
788 | |||
789 | return ret; | ||
790 | } | ||
791 | #else | ||
792 | #define fimc_md_register_of_platform_entities(fmd, node) (-ENOSYS) | ||
793 | #endif | ||
794 | |||
795 | static void fimc_md_unregister_entities(struct fimc_md *fmd) | ||
796 | { | ||
797 | int i; | ||
798 | |||
799 | for (i = 0; i < FIMC_MAX_DEVS; i++) { | ||
800 | if (fmd->fimc[i] == NULL) | ||
801 | continue; | ||
802 | v4l2_device_unregister_subdev(&fmd->fimc[i]->vid_cap.subdev); | ||
803 | fmd->fimc[i]->pipeline_ops = NULL; | ||
804 | fmd->fimc[i] = NULL; | ||
805 | } | ||
806 | for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { | ||
807 | if (fmd->fimc_lite[i] == NULL) | ||
808 | continue; | ||
809 | v4l2_device_unregister_subdev(&fmd->fimc_lite[i]->subdev); | ||
810 | fmd->fimc_lite[i]->pipeline_ops = NULL; | ||
811 | fmd->fimc_lite[i] = NULL; | ||
812 | } | ||
813 | for (i = 0; i < CSIS_MAX_ENTITIES; i++) { | ||
814 | if (fmd->csis[i].sd == NULL) | ||
815 | continue; | ||
816 | v4l2_device_unregister_subdev(fmd->csis[i].sd); | ||
817 | fmd->csis[i].sd = NULL; | ||
818 | } | ||
819 | for (i = 0; i < fmd->num_sensors; i++) { | ||
820 | if (fmd->sensor[i].subdev == NULL) | ||
821 | continue; | ||
822 | fimc_md_unregister_sensor(fmd->sensor[i].subdev); | ||
823 | fmd->sensor[i].subdev = NULL; | ||
824 | } | ||
825 | |||
826 | if (fmd->fimc_is) | ||
827 | v4l2_device_unregister_subdev(&fmd->fimc_is->isp.subdev); | ||
828 | |||
829 | v4l2_info(&fmd->v4l2_dev, "Unregistered all entities\n"); | ||
830 | } | ||
831 | |||
832 | /** | ||
833 | * __fimc_md_create_fimc_links - create links to all FIMC entities | ||
834 | * @fmd: fimc media device | ||
835 | * @source: the source entity to create links to all fimc entities from | ||
836 | * @sensor: sensor subdev linked to FIMC[fimc_id] entity, may be null | ||
837 | * @pad: the source entity pad index | ||
838 | * @link_mask: bitmask of the fimc devices for which link should be enabled | ||
839 | */ | ||
840 | static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, | ||
841 | struct media_entity *source, | ||
842 | struct v4l2_subdev *sensor, | ||
843 | int pad, int link_mask) | ||
844 | { | ||
845 | struct fimc_source_info *si = NULL; | ||
846 | struct media_entity *sink; | ||
847 | unsigned int flags = 0; | ||
848 | int i, ret = 0; | ||
849 | |||
850 | if (sensor) { | ||
851 | si = v4l2_get_subdev_hostdata(sensor); | ||
852 | /* Skip direct FIMC links in the logical FIMC-IS sensor path */ | ||
853 | if (si && si->fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) | ||
854 | ret = 1; | ||
855 | } | ||
856 | |||
857 | for (i = 0; !ret && i < FIMC_MAX_DEVS; i++) { | ||
858 | if (!fmd->fimc[i]) | ||
859 | continue; | ||
860 | /* | ||
861 | * Some FIMC variants are not fitted with camera capture | ||
862 | * interface. Skip creating a link from sensor for those. | ||
863 | */ | ||
864 | if (!fmd->fimc[i]->variant->has_cam_if) | ||
865 | continue; | ||
866 | |||
867 | flags = ((1 << i) & link_mask) ? MEDIA_LNK_FL_ENABLED : 0; | ||
868 | |||
869 | sink = &fmd->fimc[i]->vid_cap.subdev.entity; | ||
870 | ret = media_entity_create_link(source, pad, sink, | ||
871 | FIMC_SD_PAD_SINK_CAM, flags); | ||
872 | if (ret) | ||
873 | return ret; | ||
874 | |||
875 | /* Notify FIMC capture subdev entity */ | ||
876 | ret = media_entity_call(sink, link_setup, &sink->pads[0], | ||
877 | &source->pads[pad], flags); | ||
878 | if (ret) | ||
879 | break; | ||
880 | |||
881 | v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]\n", | ||
882 | source->name, flags ? '=' : '-', sink->name); | ||
883 | |||
884 | if (flags == 0 || sensor == NULL) | ||
885 | continue; | ||
886 | |||
887 | if (!WARN_ON(si == NULL)) { | ||
888 | unsigned long irq_flags; | ||
889 | struct fimc_sensor_info *inf = source_to_sensor_info(si); | ||
890 | |||
891 | spin_lock_irqsave(&fmd->slock, irq_flags); | ||
892 | inf->host = fmd->fimc[i]; | ||
893 | spin_unlock_irqrestore(&fmd->slock, irq_flags); | ||
894 | } | ||
895 | } | ||
896 | |||
897 | for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { | ||
898 | if (!fmd->fimc_lite[i]) | ||
899 | continue; | ||
900 | |||
901 | sink = &fmd->fimc_lite[i]->subdev.entity; | ||
902 | ret = media_entity_create_link(source, pad, sink, | ||
903 | FLITE_SD_PAD_SINK, 0); | ||
904 | if (ret) | ||
905 | return ret; | ||
906 | |||
907 | /* Notify FIMC-LITE subdev entity */ | ||
908 | ret = media_entity_call(sink, link_setup, &sink->pads[0], | ||
909 | &source->pads[pad], 0); | ||
910 | if (ret) | ||
911 | break; | ||
912 | |||
913 | v4l2_info(&fmd->v4l2_dev, "created link [%s] -> [%s]\n", | ||
914 | source->name, sink->name); | ||
915 | } | ||
916 | return 0; | ||
917 | } | ||
918 | |||
919 | /* Create links from FIMC-LITE source pads to other entities */ | ||
920 | static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) | ||
921 | { | ||
922 | struct media_entity *source, *sink; | ||
923 | int i, ret = 0; | ||
924 | |||
925 | for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { | ||
926 | struct fimc_lite *fimc = fmd->fimc_lite[i]; | ||
927 | |||
928 | if (fimc == NULL) | ||
929 | continue; | ||
930 | |||
931 | source = &fimc->subdev.entity; | ||
932 | sink = &fimc->vfd.entity; | ||
933 | /* FIMC-LITE's subdev and video node */ | ||
934 | ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_DMA, | ||
935 | sink, 0, 0); | ||
936 | if (ret) | ||
937 | break; | ||
938 | /* Link from FIMC-LITE to IS-ISP subdev */ | ||
939 | sink = &fmd->fimc_is->isp.subdev.entity; | ||
940 | ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_ISP, | ||
941 | sink, 0, 0); | ||
942 | if (ret) | ||
943 | break; | ||
944 | } | ||
945 | |||
946 | return ret; | ||
947 | } | ||
948 | |||
949 | /* Create FIMC-IS links */ | ||
950 | static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd) | ||
951 | { | ||
952 | struct media_entity *source, *sink; | ||
953 | int i, ret; | ||
954 | |||
955 | source = &fmd->fimc_is->isp.subdev.entity; | ||
956 | |||
957 | for (i = 0; i < FIMC_MAX_DEVS; i++) { | ||
958 | if (fmd->fimc[i] == NULL) | ||
959 | continue; | ||
960 | |||
961 | /* Link from IS-ISP subdev to FIMC */ | ||
962 | sink = &fmd->fimc[i]->vid_cap.subdev.entity; | ||
963 | ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO, | ||
964 | sink, FIMC_SD_PAD_SINK_FIFO, 0); | ||
965 | if (ret) | ||
966 | return ret; | ||
967 | } | ||
968 | |||
969 | return ret; | ||
970 | } | ||
971 | |||
972 | /** | ||
973 | * fimc_md_create_links - create default links between registered entities | ||
974 | * | ||
975 | * Parallel interface sensor entities are connected directly to FIMC capture | ||
976 | * entities. The sensors using MIPI CSIS bus are connected through immutable | ||
977 | * link with CSI receiver entity specified by mux_id. Any registered CSIS | ||
978 | * entity has a link to each registered FIMC capture entity. Enabled links | ||
979 | * are created by default between each subsequent registered sensor and | ||
980 | * subsequent FIMC capture entity. The number of default active links is | ||
981 | * determined by the number of available sensors or FIMC entities, | ||
982 | * whichever is less. | ||
983 | */ | ||
984 | static int fimc_md_create_links(struct fimc_md *fmd) | ||
985 | { | ||
986 | struct v4l2_subdev *csi_sensors[CSIS_MAX_ENTITIES] = { NULL }; | ||
987 | struct v4l2_subdev *sensor, *csis; | ||
988 | struct fimc_source_info *pdata; | ||
989 | struct media_entity *source, *sink; | ||
990 | int i, pad, fimc_id = 0, ret = 0; | ||
991 | u32 flags, link_mask = 0; | ||
992 | |||
993 | for (i = 0; i < fmd->num_sensors; i++) { | ||
994 | if (fmd->sensor[i].subdev == NULL) | ||
995 | continue; | ||
996 | |||
997 | sensor = fmd->sensor[i].subdev; | ||
998 | pdata = v4l2_get_subdev_hostdata(sensor); | ||
999 | if (!pdata) | ||
1000 | continue; | ||
1001 | |||
1002 | source = NULL; | ||
1003 | |||
1004 | switch (pdata->sensor_bus_type) { | ||
1005 | case FIMC_BUS_TYPE_MIPI_CSI2: | ||
1006 | if (WARN(pdata->mux_id >= CSIS_MAX_ENTITIES, | ||
1007 | "Wrong CSI channel id: %d\n", pdata->mux_id)) | ||
1008 | return -EINVAL; | ||
1009 | |||
1010 | csis = fmd->csis[pdata->mux_id].sd; | ||
1011 | if (WARN(csis == NULL, | ||
1012 | "MIPI-CSI interface specified " | ||
1013 | "but s5p-csis module is not loaded!\n")) | ||
1014 | return -EINVAL; | ||
1015 | |||
1016 | pad = sensor->entity.num_pads - 1; | ||
1017 | ret = media_entity_create_link(&sensor->entity, pad, | ||
1018 | &csis->entity, CSIS_PAD_SINK, | ||
1019 | MEDIA_LNK_FL_IMMUTABLE | | ||
1020 | MEDIA_LNK_FL_ENABLED); | ||
1021 | if (ret) | ||
1022 | return ret; | ||
1023 | |||
1024 | v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]\n", | ||
1025 | sensor->entity.name, csis->entity.name); | ||
1026 | |||
1027 | source = NULL; | ||
1028 | csi_sensors[pdata->mux_id] = sensor; | ||
1029 | break; | ||
1030 | |||
1031 | case FIMC_BUS_TYPE_ITU_601...FIMC_BUS_TYPE_ITU_656: | ||
1032 | source = &sensor->entity; | ||
1033 | pad = 0; | ||
1034 | break; | ||
1035 | |||
1036 | default: | ||
1037 | v4l2_err(&fmd->v4l2_dev, "Wrong bus_type: %x\n", | ||
1038 | pdata->sensor_bus_type); | ||
1039 | return -EINVAL; | ||
1040 | } | ||
1041 | if (source == NULL) | ||
1042 | continue; | ||
1043 | |||
1044 | link_mask = 1 << fimc_id++; | ||
1045 | ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor, | ||
1046 | pad, link_mask); | ||
1047 | } | ||
1048 | |||
1049 | for (i = 0; i < CSIS_MAX_ENTITIES; i++) { | ||
1050 | if (fmd->csis[i].sd == NULL) | ||
1051 | continue; | ||
1052 | |||
1053 | source = &fmd->csis[i].sd->entity; | ||
1054 | pad = CSIS_PAD_SOURCE; | ||
1055 | sensor = csi_sensors[i]; | ||
1056 | |||
1057 | link_mask = 1 << fimc_id++; | ||
1058 | ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor, | ||
1059 | pad, link_mask); | ||
1060 | } | ||
1061 | |||
1062 | /* Create immutable links between each FIMC's subdev and video node */ | ||
1063 | flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; | ||
1064 | for (i = 0; i < FIMC_MAX_DEVS; i++) { | ||
1065 | if (!fmd->fimc[i]) | ||
1066 | continue; | ||
1067 | |||
1068 | source = &fmd->fimc[i]->vid_cap.subdev.entity; | ||
1069 | sink = &fmd->fimc[i]->vid_cap.vfd.entity; | ||
1070 | |||
1071 | ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, | ||
1072 | sink, 0, flags); | ||
1073 | if (ret) | ||
1074 | break; | ||
1075 | } | ||
1076 | |||
1077 | ret = __fimc_md_create_flite_source_links(fmd); | ||
1078 | if (ret < 0) | ||
1079 | return ret; | ||
1080 | |||
1081 | if (fmd->use_isp) | ||
1082 | ret = __fimc_md_create_fimc_is_links(fmd); | ||
1083 | |||
1084 | return ret; | ||
1085 | } | ||
1086 | |||
1087 | /* | ||
1088 | * The peripheral sensor and CAM_BLK (PIXELASYNCMx) clocks management. | ||
1089 | */ | ||
1090 | static void fimc_md_put_clocks(struct fimc_md *fmd) | ||
1091 | { | ||
1092 | int i = FIMC_MAX_CAMCLKS; | ||
1093 | |||
1094 | while (--i >= 0) { | ||
1095 | if (IS_ERR(fmd->camclk[i].clock)) | ||
1096 | continue; | ||
1097 | clk_unprepare(fmd->camclk[i].clock); | ||
1098 | clk_put(fmd->camclk[i].clock); | ||
1099 | fmd->camclk[i].clock = ERR_PTR(-EINVAL); | ||
1100 | } | ||
1101 | |||
1102 | /* Writeback (PIXELASYNCMx) clocks */ | ||
1103 | for (i = 0; i < FIMC_MAX_WBCLKS; i++) { | ||
1104 | if (IS_ERR(fmd->wbclk[i])) | ||
1105 | continue; | ||
1106 | clk_put(fmd->wbclk[i]); | ||
1107 | fmd->wbclk[i] = ERR_PTR(-EINVAL); | ||
1108 | } | ||
1109 | } | ||
1110 | |||
1111 | static int fimc_md_get_clocks(struct fimc_md *fmd) | ||
1112 | { | ||
1113 | struct device *dev = NULL; | ||
1114 | char clk_name[32]; | ||
1115 | struct clk *clock; | ||
1116 | int ret, i; | ||
1117 | |||
1118 | for (i = 0; i < FIMC_MAX_CAMCLKS; i++) | ||
1119 | fmd->camclk[i].clock = ERR_PTR(-EINVAL); | ||
1120 | |||
1121 | if (fmd->pdev->dev.of_node) | ||
1122 | dev = &fmd->pdev->dev; | ||
1123 | |||
1124 | for (i = 0; i < FIMC_MAX_CAMCLKS; i++) { | ||
1125 | snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i); | ||
1126 | clock = clk_get(dev, clk_name); | ||
1127 | |||
1128 | if (IS_ERR(clock)) { | ||
1129 | dev_err(&fmd->pdev->dev, "Failed to get clock: %s\n", | ||
1130 | clk_name); | ||
1131 | ret = PTR_ERR(clock); | ||
1132 | break; | ||
1133 | } | ||
1134 | ret = clk_prepare(clock); | ||
1135 | if (ret < 0) { | ||
1136 | clk_put(clock); | ||
1137 | fmd->camclk[i].clock = ERR_PTR(-EINVAL); | ||
1138 | break; | ||
1139 | } | ||
1140 | fmd->camclk[i].clock = clock; | ||
1141 | } | ||
1142 | if (ret) | ||
1143 | fimc_md_put_clocks(fmd); | ||
1144 | |||
1145 | if (!fmd->use_isp) | ||
1146 | return 0; | ||
1147 | /* | ||
1148 | * For now get only PIXELASYNCM1 clock (Writeback B/ISP), | ||
1149 | * leave PIXELASYNCM0 out for the LCD Writeback driver. | ||
1150 | */ | ||
1151 | fmd->wbclk[CLK_IDX_WB_A] = ERR_PTR(-EINVAL); | ||
1152 | |||
1153 | for (i = CLK_IDX_WB_B; i < FIMC_MAX_WBCLKS; i++) { | ||
1154 | snprintf(clk_name, sizeof(clk_name), "pxl_async%u", i); | ||
1155 | clock = clk_get(dev, clk_name); | ||
1156 | if (IS_ERR(clock)) { | ||
1157 | v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s\n", | ||
1158 | clk_name); | ||
1159 | ret = PTR_ERR(clock); | ||
1160 | break; | ||
1161 | } | ||
1162 | fmd->wbclk[i] = clock; | ||
1163 | } | ||
1164 | if (ret) | ||
1165 | fimc_md_put_clocks(fmd); | ||
1166 | |||
1167 | return ret; | ||
1168 | } | ||
1169 | |||
1170 | static int __fimc_md_set_camclk(struct fimc_md *fmd, | ||
1171 | struct fimc_source_info *si, | ||
1172 | bool on) | ||
1173 | { | ||
1174 | struct fimc_camclk_info *camclk; | ||
1175 | int ret = 0; | ||
1176 | |||
1177 | if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf) | ||
1178 | return -EINVAL; | ||
1179 | |||
1180 | camclk = &fmd->camclk[si->clk_id]; | ||
1181 | |||
1182 | dbg("camclk %d, f: %lu, use_count: %d, on: %d", | ||
1183 | si->clk_id, si->clk_frequency, camclk->use_count, on); | ||
1184 | |||
1185 | if (on) { | ||
1186 | if (camclk->use_count > 0 && | ||
1187 | camclk->frequency != si->clk_frequency) | ||
1188 | return -EINVAL; | ||
1189 | |||
1190 | if (camclk->use_count++ == 0) { | ||
1191 | clk_set_rate(camclk->clock, si->clk_frequency); | ||
1192 | camclk->frequency = si->clk_frequency; | ||
1193 | ret = pm_runtime_get_sync(fmd->pmf); | ||
1194 | if (ret < 0) | ||
1195 | return ret; | ||
1196 | ret = clk_enable(camclk->clock); | ||
1197 | dbg("Enabled camclk %d: f: %lu", si->clk_id, | ||
1198 | clk_get_rate(camclk->clock)); | ||
1199 | } | ||
1200 | return ret; | ||
1201 | } | ||
1202 | |||
1203 | if (WARN_ON(camclk->use_count == 0)) | ||
1204 | return 0; | ||
1205 | |||
1206 | if (--camclk->use_count == 0) { | ||
1207 | clk_disable(camclk->clock); | ||
1208 | pm_runtime_put(fmd->pmf); | ||
1209 | dbg("Disabled camclk %d", si->clk_id); | ||
1210 | } | ||
1211 | return ret; | ||
1212 | } | ||
1213 | |||
1214 | /** | ||
1215 | * fimc_md_set_camclk - peripheral sensor clock setup | ||
1216 | * @sd: sensor subdev to configure sclk_cam clock for | ||
1217 | * @on: 1 to enable or 0 to disable the clock | ||
1218 | * | ||
1219 | * There are 2 separate clock outputs available in the SoC for external | ||
1220 | * image processors. These clocks are shared between all registered FIMC | ||
1221 | * devices to which sensors can be attached, either directly or through | ||
1222 | * the MIPI CSI receiver. The clock is allowed here to be used by | ||
1223 | * multiple sensors concurrently if they use same frequency. | ||
1224 | * This function should only be called when the graph mutex is held. | ||
1225 | */ | ||
1226 | int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) | ||
1227 | { | ||
1228 | struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd); | ||
1229 | struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity); | ||
1230 | |||
1231 | return __fimc_md_set_camclk(fmd, si, on); | ||
1232 | } | ||
1233 | |||
1234 | static int fimc_md_link_notify(struct media_pad *source, | ||
1235 | struct media_pad *sink, u32 flags) | ||
1236 | { | ||
1237 | struct fimc_lite *fimc_lite = NULL; | ||
1238 | struct fimc_dev *fimc = NULL; | ||
1239 | struct fimc_pipeline *pipeline; | ||
1240 | struct v4l2_subdev *sd; | ||
1241 | struct mutex *lock; | ||
1242 | int i, ret = 0; | ||
1243 | int ref_count; | ||
1244 | |||
1245 | if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
1246 | return 0; | ||
1247 | |||
1248 | sd = media_entity_to_v4l2_subdev(sink->entity); | ||
1249 | |||
1250 | switch (sd->grp_id) { | ||
1251 | case GRP_ID_FLITE: | ||
1252 | fimc_lite = v4l2_get_subdevdata(sd); | ||
1253 | if (WARN_ON(fimc_lite == NULL)) | ||
1254 | return 0; | ||
1255 | pipeline = &fimc_lite->pipeline; | ||
1256 | lock = &fimc_lite->lock; | ||
1257 | break; | ||
1258 | case GRP_ID_FIMC: | ||
1259 | fimc = v4l2_get_subdevdata(sd); | ||
1260 | if (WARN_ON(fimc == NULL)) | ||
1261 | return 0; | ||
1262 | pipeline = &fimc->pipeline; | ||
1263 | lock = &fimc->lock; | ||
1264 | break; | ||
1265 | default: | ||
1266 | return 0; | ||
1267 | } | ||
1268 | |||
1269 | mutex_lock(lock); | ||
1270 | ref_count = fimc ? fimc->vid_cap.refcnt : fimc_lite->ref_count; | ||
1271 | |||
1272 | if (!(flags & MEDIA_LNK_FL_ENABLED)) { | ||
1273 | if (ref_count > 0) { | ||
1274 | ret = __fimc_pipeline_close(pipeline); | ||
1275 | if (!ret && fimc) | ||
1276 | fimc_ctrls_delete(fimc->vid_cap.ctx); | ||
1277 | } | ||
1278 | for (i = 0; i < IDX_MAX; i++) | ||
1279 | pipeline->subdevs[i] = NULL; | ||
1280 | } else if (ref_count > 0) { | ||
1281 | /* | ||
1282 | * Link activation. Enable power of pipeline elements only if | ||
1283 | * the pipeline is already in use, i.e. its video node is open. | ||
1284 | * Recreate the controls destroyed during the link deactivation. | ||
1285 | */ | ||
1286 | ret = __fimc_pipeline_open(pipeline, | ||
1287 | source->entity, true); | ||
1288 | if (!ret && fimc) | ||
1289 | ret = fimc_capture_ctrls_create(fimc); | ||
1290 | } | ||
1291 | |||
1292 | mutex_unlock(lock); | ||
1293 | return ret ? -EPIPE : ret; | ||
1294 | } | ||
1295 | |||
1296 | static ssize_t fimc_md_sysfs_show(struct device *dev, | ||
1297 | struct device_attribute *attr, char *buf) | ||
1298 | { | ||
1299 | struct platform_device *pdev = to_platform_device(dev); | ||
1300 | struct fimc_md *fmd = platform_get_drvdata(pdev); | ||
1301 | |||
1302 | if (fmd->user_subdev_api) | ||
1303 | return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE); | ||
1304 | |||
1305 | return strlcpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE); | ||
1306 | } | ||
1307 | |||
1308 | static ssize_t fimc_md_sysfs_store(struct device *dev, | ||
1309 | struct device_attribute *attr, | ||
1310 | const char *buf, size_t count) | ||
1311 | { | ||
1312 | struct platform_device *pdev = to_platform_device(dev); | ||
1313 | struct fimc_md *fmd = platform_get_drvdata(pdev); | ||
1314 | bool subdev_api; | ||
1315 | int i; | ||
1316 | |||
1317 | if (!strcmp(buf, "vid-dev\n")) | ||
1318 | subdev_api = false; | ||
1319 | else if (!strcmp(buf, "sub-dev\n")) | ||
1320 | subdev_api = true; | ||
1321 | else | ||
1322 | return count; | ||
1323 | |||
1324 | fmd->user_subdev_api = subdev_api; | ||
1325 | for (i = 0; i < FIMC_MAX_DEVS; i++) | ||
1326 | if (fmd->fimc[i]) | ||
1327 | fmd->fimc[i]->vid_cap.user_subdev_api = subdev_api; | ||
1328 | return count; | ||
1329 | } | ||
1330 | /* | ||
1331 | * This device attribute is to select video pipeline configuration method. | ||
1332 | * There are following valid values: | ||
1333 | * vid-dev - for V4L2 video node API only, subdevice will be configured | ||
1334 | * by the host driver. | ||
1335 | * sub-dev - for media controller API, subdevs must be configured in user | ||
1336 | * space before starting streaming. | ||
1337 | */ | ||
1338 | static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, | ||
1339 | fimc_md_sysfs_show, fimc_md_sysfs_store); | ||
1340 | |||
1341 | static int fimc_md_get_pinctrl(struct fimc_md *fmd) | ||
1342 | { | ||
1343 | struct device *dev = &fmd->pdev->dev; | ||
1344 | struct fimc_pinctrl *pctl = &fmd->pinctl; | ||
1345 | |||
1346 | pctl->pinctrl = devm_pinctrl_get(dev); | ||
1347 | if (IS_ERR(pctl->pinctrl)) | ||
1348 | return PTR_ERR(pctl->pinctrl); | ||
1349 | |||
1350 | pctl->state_default = pinctrl_lookup_state(pctl->pinctrl, | ||
1351 | PINCTRL_STATE_DEFAULT); | ||
1352 | if (IS_ERR(pctl->state_default)) | ||
1353 | return PTR_ERR(pctl->state_default); | ||
1354 | |||
1355 | pctl->state_idle = pinctrl_lookup_state(pctl->pinctrl, | ||
1356 | PINCTRL_STATE_IDLE); | ||
1357 | return 0; | ||
1358 | } | ||
1359 | |||
1360 | static int fimc_md_probe(struct platform_device *pdev) | ||
1361 | { | ||
1362 | struct device *dev = &pdev->dev; | ||
1363 | struct v4l2_device *v4l2_dev; | ||
1364 | struct fimc_md *fmd; | ||
1365 | int ret; | ||
1366 | |||
1367 | fmd = devm_kzalloc(dev, sizeof(*fmd), GFP_KERNEL); | ||
1368 | if (!fmd) | ||
1369 | return -ENOMEM; | ||
1370 | |||
1371 | spin_lock_init(&fmd->slock); | ||
1372 | fmd->pdev = pdev; | ||
1373 | |||
1374 | strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC", | ||
1375 | sizeof(fmd->media_dev.model)); | ||
1376 | fmd->media_dev.link_notify = fimc_md_link_notify; | ||
1377 | fmd->media_dev.dev = dev; | ||
1378 | |||
1379 | v4l2_dev = &fmd->v4l2_dev; | ||
1380 | v4l2_dev->mdev = &fmd->media_dev; | ||
1381 | v4l2_dev->notify = fimc_sensor_notify; | ||
1382 | strlcpy(v4l2_dev->name, "s5p-fimc-md", sizeof(v4l2_dev->name)); | ||
1383 | |||
1384 | fmd->use_isp = fimc_md_is_isp_available(dev->of_node); | ||
1385 | |||
1386 | ret = v4l2_device_register(dev, &fmd->v4l2_dev); | ||
1387 | if (ret < 0) { | ||
1388 | v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); | ||
1389 | return ret; | ||
1390 | } | ||
1391 | ret = media_device_register(&fmd->media_dev); | ||
1392 | if (ret < 0) { | ||
1393 | v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret); | ||
1394 | goto err_md; | ||
1395 | } | ||
1396 | ret = fimc_md_get_clocks(fmd); | ||
1397 | if (ret) | ||
1398 | goto err_clk; | ||
1399 | |||
1400 | fmd->user_subdev_api = (dev->of_node != NULL); | ||
1401 | |||
1402 | /* Protect the media graph while we're registering entities */ | ||
1403 | mutex_lock(&fmd->media_dev.graph_mutex); | ||
1404 | |||
1405 | ret = fimc_md_get_pinctrl(fmd); | ||
1406 | if (ret < 0) { | ||
1407 | if (ret != EPROBE_DEFER) | ||
1408 | dev_err(dev, "Failed to get pinctrl: %d\n", ret); | ||
1409 | goto err_unlock; | ||
1410 | } | ||
1411 | |||
1412 | if (dev->of_node) | ||
1413 | ret = fimc_md_register_of_platform_entities(fmd, dev->of_node); | ||
1414 | else | ||
1415 | ret = bus_for_each_dev(&platform_bus_type, NULL, fmd, | ||
1416 | fimc_md_pdev_match); | ||
1417 | if (ret) | ||
1418 | goto err_unlock; | ||
1419 | |||
1420 | if (dev->platform_data || dev->of_node) { | ||
1421 | ret = fimc_md_register_sensor_entities(fmd); | ||
1422 | if (ret) | ||
1423 | goto err_unlock; | ||
1424 | } | ||
1425 | |||
1426 | ret = fimc_md_create_links(fmd); | ||
1427 | if (ret) | ||
1428 | goto err_unlock; | ||
1429 | ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); | ||
1430 | if (ret) | ||
1431 | goto err_unlock; | ||
1432 | |||
1433 | ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode); | ||
1434 | if (ret) | ||
1435 | goto err_unlock; | ||
1436 | |||
1437 | platform_set_drvdata(pdev, fmd); | ||
1438 | mutex_unlock(&fmd->media_dev.graph_mutex); | ||
1439 | return 0; | ||
1440 | |||
1441 | err_unlock: | ||
1442 | mutex_unlock(&fmd->media_dev.graph_mutex); | ||
1443 | err_clk: | ||
1444 | media_device_unregister(&fmd->media_dev); | ||
1445 | fimc_md_put_clocks(fmd); | ||
1446 | fimc_md_unregister_entities(fmd); | ||
1447 | err_md: | ||
1448 | v4l2_device_unregister(&fmd->v4l2_dev); | ||
1449 | return ret; | ||
1450 | } | ||
1451 | |||
1452 | static int fimc_md_remove(struct platform_device *pdev) | ||
1453 | { | ||
1454 | struct fimc_md *fmd = platform_get_drvdata(pdev); | ||
1455 | |||
1456 | if (!fmd) | ||
1457 | return 0; | ||
1458 | device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode); | ||
1459 | fimc_md_unregister_entities(fmd); | ||
1460 | media_device_unregister(&fmd->media_dev); | ||
1461 | fimc_md_put_clocks(fmd); | ||
1462 | return 0; | ||
1463 | } | ||
1464 | |||
1465 | static struct platform_device_id fimc_driver_ids[] __always_unused = { | ||
1466 | { .name = "s5p-fimc-md" }, | ||
1467 | { }, | ||
1468 | }; | ||
1469 | MODULE_DEVICE_TABLE(platform, fimc_driver_ids); | ||
1470 | |||
1471 | static const struct of_device_id fimc_md_of_match[] = { | ||
1472 | { .compatible = "samsung,fimc" }, | ||
1473 | { }, | ||
1474 | }; | ||
1475 | MODULE_DEVICE_TABLE(of, fimc_md_of_match); | ||
1476 | |||
1477 | static struct platform_driver fimc_md_driver = { | ||
1478 | .probe = fimc_md_probe, | ||
1479 | .remove = fimc_md_remove, | ||
1480 | .driver = { | ||
1481 | .of_match_table = of_match_ptr(fimc_md_of_match), | ||
1482 | .name = "s5p-fimc-md", | ||
1483 | .owner = THIS_MODULE, | ||
1484 | } | ||
1485 | }; | ||
1486 | |||
1487 | static int __init fimc_md_init(void) | ||
1488 | { | ||
1489 | int ret; | ||
1490 | |||
1491 | request_module("s5p-csis"); | ||
1492 | ret = fimc_register_driver(); | ||
1493 | if (ret) | ||
1494 | return ret; | ||
1495 | |||
1496 | return platform_driver_register(&fimc_md_driver); | ||
1497 | } | ||
1498 | |||
1499 | static void __exit fimc_md_exit(void) | ||
1500 | { | ||
1501 | platform_driver_unregister(&fimc_md_driver); | ||
1502 | fimc_unregister_driver(); | ||
1503 | } | ||
1504 | |||
1505 | module_init(fimc_md_init); | ||
1506 | module_exit(fimc_md_exit); | ||
1507 | |||
1508 | MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); | ||
1509 | MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver"); | ||
1510 | MODULE_LICENSE("GPL"); | ||
1511 | MODULE_VERSION("2.0.1"); | ||
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h new file mode 100644 index 000000000000..44d86b61d660 --- /dev/null +++ b/drivers/media/platform/exynos4-is/media-dev.h | |||
@@ -0,0 +1,152 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef FIMC_MDEVICE_H_ | ||
10 | #define FIMC_MDEVICE_H_ | ||
11 | |||
12 | #include <linux/clk.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/mutex.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/pinctrl/consumer.h> | ||
17 | #include <media/media-device.h> | ||
18 | #include <media/media-entity.h> | ||
19 | #include <media/v4l2-device.h> | ||
20 | #include <media/v4l2-subdev.h> | ||
21 | |||
22 | #include "fimc-core.h" | ||
23 | #include "fimc-lite.h" | ||
24 | #include "mipi-csis.h" | ||
25 | |||
26 | #define FIMC_OF_NODE_NAME "fimc" | ||
27 | #define FIMC_LITE_OF_NODE_NAME "fimc-lite" | ||
28 | #define FIMC_IS_OF_NODE_NAME "fimc-is" | ||
29 | #define CSIS_OF_NODE_NAME "csis" | ||
30 | |||
31 | #define PINCTRL_STATE_IDLE "idle" | ||
32 | |||
33 | #define FIMC_MAX_SENSORS 8 | ||
34 | #define FIMC_MAX_CAMCLKS 2 | ||
35 | |||
36 | /* LCD/ISP Writeback clocks (PIXELASYNCMx) */ | ||
37 | enum { | ||
38 | CLK_IDX_WB_A, | ||
39 | CLK_IDX_WB_B, | ||
40 | FIMC_MAX_WBCLKS | ||
41 | }; | ||
42 | |||
43 | struct fimc_csis_info { | ||
44 | struct v4l2_subdev *sd; | ||
45 | int id; | ||
46 | }; | ||
47 | |||
48 | struct fimc_camclk_info { | ||
49 | struct clk *clock; | ||
50 | int use_count; | ||
51 | unsigned long frequency; | ||
52 | }; | ||
53 | |||
54 | /** | ||
55 | * struct fimc_sensor_info - image data source subdev information | ||
56 | * @pdata: sensor's atrributes passed as media device's platform data | ||
57 | * @subdev: image sensor v4l2 subdev | ||
58 | * @host: fimc device the sensor is currently linked to | ||
59 | * | ||
60 | * This data structure applies to image sensor and the writeback subdevs. | ||
61 | */ | ||
62 | struct fimc_sensor_info { | ||
63 | struct fimc_source_info pdata; | ||
64 | struct v4l2_subdev *subdev; | ||
65 | struct fimc_dev *host; | ||
66 | }; | ||
67 | |||
68 | /** | ||
69 | * struct fimc_md - fimc media device information | ||
70 | * @csis: MIPI CSIS subdevs data | ||
71 | * @sensor: array of registered sensor subdevs | ||
72 | * @num_sensors: actual number of registered sensors | ||
73 | * @camclk: external sensor clock information | ||
74 | * @fimc: array of registered fimc devices | ||
75 | * @fimc_is: fimc-is data structure | ||
76 | * @use_isp: set to true when FIMC-IS subsystem is used | ||
77 | * @pmf: handle to the CAMCLK clock control FIMC helper device | ||
78 | * @media_dev: top level media device | ||
79 | * @v4l2_dev: top level v4l2_device holding up the subdevs | ||
80 | * @pdev: platform device this media device is hooked up into | ||
81 | * @pinctrl: camera port pinctrl handle | ||
82 | * @state_default: pinctrl default state handle | ||
83 | * @state_idle: pinctrl idle state handle | ||
84 | * @user_subdev_api: true if subdevs are not configured by the host driver | ||
85 | * @slock: spinlock protecting @sensor array | ||
86 | */ | ||
87 | struct fimc_md { | ||
88 | struct fimc_csis_info csis[CSIS_MAX_ENTITIES]; | ||
89 | struct fimc_sensor_info sensor[FIMC_MAX_SENSORS]; | ||
90 | int num_sensors; | ||
91 | struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS]; | ||
92 | struct clk *wbclk[FIMC_MAX_WBCLKS]; | ||
93 | struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS]; | ||
94 | struct fimc_dev *fimc[FIMC_MAX_DEVS]; | ||
95 | struct fimc_is *fimc_is; | ||
96 | bool use_isp; | ||
97 | struct device *pmf; | ||
98 | struct media_device media_dev; | ||
99 | struct v4l2_device v4l2_dev; | ||
100 | struct platform_device *pdev; | ||
101 | struct fimc_pinctrl { | ||
102 | struct pinctrl *pinctrl; | ||
103 | struct pinctrl_state *state_default; | ||
104 | struct pinctrl_state *state_idle; | ||
105 | } pinctl; | ||
106 | bool user_subdev_api; | ||
107 | spinlock_t slock; | ||
108 | }; | ||
109 | |||
110 | #define is_subdev_pad(pad) (pad == NULL || \ | ||
111 | media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV) | ||
112 | |||
113 | #define me_subtype(me) \ | ||
114 | ((me->type) & (MEDIA_ENT_TYPE_MASK | MEDIA_ENT_SUBTYPE_MASK)) | ||
115 | |||
116 | #define subdev_has_devnode(__sd) (__sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE) | ||
117 | |||
118 | static inline | ||
119 | struct fimc_sensor_info *source_to_sensor_info(struct fimc_source_info *si) | ||
120 | { | ||
121 | return container_of(si, struct fimc_sensor_info, pdata); | ||
122 | } | ||
123 | |||
124 | static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me) | ||
125 | { | ||
126 | return me->parent == NULL ? NULL : | ||
127 | container_of(me->parent, struct fimc_md, media_dev); | ||
128 | } | ||
129 | |||
130 | static inline void fimc_md_graph_lock(struct fimc_dev *fimc) | ||
131 | { | ||
132 | mutex_lock(&fimc->vid_cap.vfd.entity.parent->graph_mutex); | ||
133 | } | ||
134 | |||
135 | static inline void fimc_md_graph_unlock(struct fimc_dev *fimc) | ||
136 | { | ||
137 | mutex_unlock(&fimc->vid_cap.vfd.entity.parent->graph_mutex); | ||
138 | } | ||
139 | |||
140 | int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on); | ||
141 | |||
142 | #ifdef CONFIG_OF | ||
143 | static inline bool fimc_md_is_isp_available(struct device_node *node) | ||
144 | { | ||
145 | node = of_get_child_by_name(node, FIMC_IS_OF_NODE_NAME); | ||
146 | return node ? of_device_is_available(node) : false; | ||
147 | } | ||
148 | #else | ||
149 | #define fimc_md_is_isp_available(node) (false) | ||
150 | #endif /* CONFIG_OF */ | ||
151 | |||
152 | #endif | ||
diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c new file mode 100644 index 000000000000..a2eda9d5ac87 --- /dev/null +++ b/drivers/media/platform/exynos4-is/mipi-csis.c | |||
@@ -0,0 +1,1019 @@ | |||
1 | /* | ||
2 | * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver | ||
3 | * | ||
4 | * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. | ||
5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/clk.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/irq.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/memory.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/of.h> | ||
23 | #include <linux/platform_data/mipi-csis.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/pm_runtime.h> | ||
26 | #include <linux/regulator/consumer.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/spinlock.h> | ||
29 | #include <linux/videodev2.h> | ||
30 | #include <media/s5p_fimc.h> | ||
31 | #include <media/v4l2-of.h> | ||
32 | #include <media/v4l2-subdev.h> | ||
33 | |||
34 | #include "mipi-csis.h" | ||
35 | |||
36 | static int debug; | ||
37 | module_param(debug, int, 0644); | ||
38 | MODULE_PARM_DESC(debug, "Debug level (0-2)"); | ||
39 | |||
40 | /* Register map definition */ | ||
41 | |||
42 | /* CSIS global control */ | ||
43 | #define S5PCSIS_CTRL 0x00 | ||
44 | #define S5PCSIS_CTRL_DPDN_DEFAULT (0 << 31) | ||
45 | #define S5PCSIS_CTRL_DPDN_SWAP (1 << 31) | ||
46 | #define S5PCSIS_CTRL_ALIGN_32BIT (1 << 20) | ||
47 | #define S5PCSIS_CTRL_UPDATE_SHADOW (1 << 16) | ||
48 | #define S5PCSIS_CTRL_WCLK_EXTCLK (1 << 8) | ||
49 | #define S5PCSIS_CTRL_RESET (1 << 4) | ||
50 | #define S5PCSIS_CTRL_ENABLE (1 << 0) | ||
51 | |||
52 | /* D-PHY control */ | ||
53 | #define S5PCSIS_DPHYCTRL 0x04 | ||
54 | #define S5PCSIS_DPHYCTRL_HSS_MASK (0x1f << 27) | ||
55 | #define S5PCSIS_DPHYCTRL_ENABLE (0x1f << 0) | ||
56 | |||
57 | #define S5PCSIS_CONFIG 0x08 | ||
58 | #define S5PCSIS_CFG_FMT_YCBCR422_8BIT (0x1e << 2) | ||
59 | #define S5PCSIS_CFG_FMT_RAW8 (0x2a << 2) | ||
60 | #define S5PCSIS_CFG_FMT_RAW10 (0x2b << 2) | ||
61 | #define S5PCSIS_CFG_FMT_RAW12 (0x2c << 2) | ||
62 | /* User defined formats, x = 1...4 */ | ||
63 | #define S5PCSIS_CFG_FMT_USER(x) ((0x30 + x - 1) << 2) | ||
64 | #define S5PCSIS_CFG_FMT_MASK (0x3f << 2) | ||
65 | #define S5PCSIS_CFG_NR_LANE_MASK 3 | ||
66 | |||
67 | /* Interrupt mask */ | ||
68 | #define S5PCSIS_INTMSK 0x10 | ||
69 | #define S5PCSIS_INTMSK_EN_ALL 0xf000103f | ||
70 | #define S5PCSIS_INTMSK_EVEN_BEFORE (1 << 31) | ||
71 | #define S5PCSIS_INTMSK_EVEN_AFTER (1 << 30) | ||
72 | #define S5PCSIS_INTMSK_ODD_BEFORE (1 << 29) | ||
73 | #define S5PCSIS_INTMSK_ODD_AFTER (1 << 28) | ||
74 | #define S5PCSIS_INTMSK_ERR_SOT_HS (1 << 12) | ||
75 | #define S5PCSIS_INTMSK_ERR_LOST_FS (1 << 5) | ||
76 | #define S5PCSIS_INTMSK_ERR_LOST_FE (1 << 4) | ||
77 | #define S5PCSIS_INTMSK_ERR_OVER (1 << 3) | ||
78 | #define S5PCSIS_INTMSK_ERR_ECC (1 << 2) | ||
79 | #define S5PCSIS_INTMSK_ERR_CRC (1 << 1) | ||
80 | #define S5PCSIS_INTMSK_ERR_UNKNOWN (1 << 0) | ||
81 | |||
82 | /* Interrupt source */ | ||
83 | #define S5PCSIS_INTSRC 0x14 | ||
84 | #define S5PCSIS_INTSRC_EVEN_BEFORE (1 << 31) | ||
85 | #define S5PCSIS_INTSRC_EVEN_AFTER (1 << 30) | ||
86 | #define S5PCSIS_INTSRC_EVEN (0x3 << 30) | ||
87 | #define S5PCSIS_INTSRC_ODD_BEFORE (1 << 29) | ||
88 | #define S5PCSIS_INTSRC_ODD_AFTER (1 << 28) | ||
89 | #define S5PCSIS_INTSRC_ODD (0x3 << 28) | ||
90 | #define S5PCSIS_INTSRC_NON_IMAGE_DATA (0xff << 28) | ||
91 | #define S5PCSIS_INTSRC_ERR_SOT_HS (0xf << 12) | ||
92 | #define S5PCSIS_INTSRC_ERR_LOST_FS (1 << 5) | ||
93 | #define S5PCSIS_INTSRC_ERR_LOST_FE (1 << 4) | ||
94 | #define S5PCSIS_INTSRC_ERR_OVER (1 << 3) | ||
95 | #define S5PCSIS_INTSRC_ERR_ECC (1 << 2) | ||
96 | #define S5PCSIS_INTSRC_ERR_CRC (1 << 1) | ||
97 | #define S5PCSIS_INTSRC_ERR_UNKNOWN (1 << 0) | ||
98 | #define S5PCSIS_INTSRC_ERRORS 0xf03f | ||
99 | |||
100 | /* Pixel resolution */ | ||
101 | #define S5PCSIS_RESOL 0x2c | ||
102 | #define CSIS_MAX_PIX_WIDTH 0xffff | ||
103 | #define CSIS_MAX_PIX_HEIGHT 0xffff | ||
104 | |||
105 | /* Non-image packet data buffers */ | ||
106 | #define S5PCSIS_PKTDATA_ODD 0x2000 | ||
107 | #define S5PCSIS_PKTDATA_EVEN 0x3000 | ||
108 | #define S5PCSIS_PKTDATA_SIZE SZ_4K | ||
109 | |||
110 | enum { | ||
111 | CSIS_CLK_MUX, | ||
112 | CSIS_CLK_GATE, | ||
113 | }; | ||
114 | |||
115 | static char *csi_clock_name[] = { | ||
116 | [CSIS_CLK_MUX] = "sclk_csis", | ||
117 | [CSIS_CLK_GATE] = "csis", | ||
118 | }; | ||
119 | #define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name) | ||
120 | #define DEFAULT_SCLK_CSIS_FREQ 166000000UL | ||
121 | |||
122 | static const char * const csis_supply_name[] = { | ||
123 | "vddcore", /* CSIS Core (1.0V, 1.1V or 1.2V) suppply */ | ||
124 | "vddio", /* CSIS I/O and PLL (1.8V) supply */ | ||
125 | }; | ||
126 | #define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name) | ||
127 | |||
128 | enum { | ||
129 | ST_POWERED = 1, | ||
130 | ST_STREAMING = 2, | ||
131 | ST_SUSPENDED = 4, | ||
132 | }; | ||
133 | |||
134 | struct s5pcsis_event { | ||
135 | u32 mask; | ||
136 | const char * const name; | ||
137 | unsigned int counter; | ||
138 | }; | ||
139 | |||
140 | static const struct s5pcsis_event s5pcsis_events[] = { | ||
141 | /* Errors */ | ||
142 | { S5PCSIS_INTSRC_ERR_SOT_HS, "SOT Error" }, | ||
143 | { S5PCSIS_INTSRC_ERR_LOST_FS, "Lost Frame Start Error" }, | ||
144 | { S5PCSIS_INTSRC_ERR_LOST_FE, "Lost Frame End Error" }, | ||
145 | { S5PCSIS_INTSRC_ERR_OVER, "FIFO Overflow Error" }, | ||
146 | { S5PCSIS_INTSRC_ERR_ECC, "ECC Error" }, | ||
147 | { S5PCSIS_INTSRC_ERR_CRC, "CRC Error" }, | ||
148 | { S5PCSIS_INTSRC_ERR_UNKNOWN, "Unknown Error" }, | ||
149 | /* Non-image data receive events */ | ||
150 | { S5PCSIS_INTSRC_EVEN_BEFORE, "Non-image data before even frame" }, | ||
151 | { S5PCSIS_INTSRC_EVEN_AFTER, "Non-image data after even frame" }, | ||
152 | { S5PCSIS_INTSRC_ODD_BEFORE, "Non-image data before odd frame" }, | ||
153 | { S5PCSIS_INTSRC_ODD_AFTER, "Non-image data after odd frame" }, | ||
154 | }; | ||
155 | #define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events) | ||
156 | |||
157 | struct csis_pktbuf { | ||
158 | u32 *data; | ||
159 | unsigned int len; | ||
160 | }; | ||
161 | |||
162 | /** | ||
163 | * struct csis_state - the driver's internal state data structure | ||
164 | * @lock: mutex serializing the subdev and power management operations, | ||
165 | * protecting @format and @flags members | ||
166 | * @pads: CSIS pads array | ||
167 | * @sd: v4l2_subdev associated with CSIS device instance | ||
168 | * @index: the hardware instance index | ||
169 | * @pdev: CSIS platform device | ||
170 | * @regs: mmaped I/O registers memory | ||
171 | * @supplies: CSIS regulator supplies | ||
172 | * @clock: CSIS clocks | ||
173 | * @irq: requested s5p-mipi-csis irq number | ||
174 | * @flags: the state variable for power and streaming control | ||
175 | * @clock_frequency: device bus clock frequency | ||
176 | * @hs_settle: HS-RX settle time | ||
177 | * @num_lanes: number of MIPI-CSI data lanes used | ||
178 | * @max_num_lanes: maximum number of MIPI-CSI data lanes supported | ||
179 | * @wclk_ext: CSI wrapper clock: 0 - bus clock, 1 - external SCLK_CAM | ||
180 | * @csis_fmt: current CSIS pixel format | ||
181 | * @format: common media bus format for the source and sink pad | ||
182 | * @slock: spinlock protecting structure members below | ||
183 | * @pkt_buf: the frame embedded (non-image) data buffer | ||
184 | * @events: MIPI-CSIS event (error) counters | ||
185 | */ | ||
186 | struct csis_state { | ||
187 | struct mutex lock; | ||
188 | struct media_pad pads[CSIS_PADS_NUM]; | ||
189 | struct v4l2_subdev sd; | ||
190 | u8 index; | ||
191 | struct platform_device *pdev; | ||
192 | void __iomem *regs; | ||
193 | struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES]; | ||
194 | struct clk *clock[NUM_CSIS_CLOCKS]; | ||
195 | int irq; | ||
196 | u32 flags; | ||
197 | |||
198 | u32 clk_frequency; | ||
199 | u32 hs_settle; | ||
200 | u32 num_lanes; | ||
201 | u32 max_num_lanes; | ||
202 | u8 wclk_ext; | ||
203 | |||
204 | const struct csis_pix_format *csis_fmt; | ||
205 | struct v4l2_mbus_framefmt format; | ||
206 | |||
207 | spinlock_t slock; | ||
208 | struct csis_pktbuf pkt_buf; | ||
209 | struct s5pcsis_event events[S5PCSIS_NUM_EVENTS]; | ||
210 | }; | ||
211 | |||
212 | /** | ||
213 | * struct csis_pix_format - CSIS pixel format description | ||
214 | * @pix_width_alignment: horizontal pixel alignment, width will be | ||
215 | * multiple of 2^pix_width_alignment | ||
216 | * @code: corresponding media bus code | ||
217 | * @fmt_reg: S5PCSIS_CONFIG register value | ||
218 | * @data_alignment: MIPI-CSI data alignment in bits | ||
219 | */ | ||
220 | struct csis_pix_format { | ||
221 | unsigned int pix_width_alignment; | ||
222 | enum v4l2_mbus_pixelcode code; | ||
223 | u32 fmt_reg; | ||
224 | u8 data_alignment; | ||
225 | }; | ||
226 | |||
227 | static const struct csis_pix_format s5pcsis_formats[] = { | ||
228 | { | ||
229 | .code = V4L2_MBUS_FMT_VYUY8_2X8, | ||
230 | .fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT, | ||
231 | .data_alignment = 32, | ||
232 | }, { | ||
233 | .code = V4L2_MBUS_FMT_JPEG_1X8, | ||
234 | .fmt_reg = S5PCSIS_CFG_FMT_USER(1), | ||
235 | .data_alignment = 32, | ||
236 | }, { | ||
237 | .code = V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8, | ||
238 | .fmt_reg = S5PCSIS_CFG_FMT_USER(1), | ||
239 | .data_alignment = 32, | ||
240 | }, { | ||
241 | .code = V4L2_MBUS_FMT_SGRBG8_1X8, | ||
242 | .fmt_reg = S5PCSIS_CFG_FMT_RAW8, | ||
243 | .data_alignment = 24, | ||
244 | }, { | ||
245 | .code = V4L2_MBUS_FMT_SGRBG10_1X10, | ||
246 | .fmt_reg = S5PCSIS_CFG_FMT_RAW10, | ||
247 | .data_alignment = 24, | ||
248 | }, { | ||
249 | .code = V4L2_MBUS_FMT_SGRBG12_1X12, | ||
250 | .fmt_reg = S5PCSIS_CFG_FMT_RAW12, | ||
251 | .data_alignment = 24, | ||
252 | } | ||
253 | }; | ||
254 | |||
255 | #define s5pcsis_write(__csis, __r, __v) writel(__v, __csis->regs + __r) | ||
256 | #define s5pcsis_read(__csis, __r) readl(__csis->regs + __r) | ||
257 | |||
258 | static struct csis_state *sd_to_csis_state(struct v4l2_subdev *sdev) | ||
259 | { | ||
260 | return container_of(sdev, struct csis_state, sd); | ||
261 | } | ||
262 | |||
263 | static const struct csis_pix_format *find_csis_format( | ||
264 | struct v4l2_mbus_framefmt *mf) | ||
265 | { | ||
266 | int i; | ||
267 | |||
268 | for (i = 0; i < ARRAY_SIZE(s5pcsis_formats); i++) | ||
269 | if (mf->code == s5pcsis_formats[i].code) | ||
270 | return &s5pcsis_formats[i]; | ||
271 | return NULL; | ||
272 | } | ||
273 | |||
274 | static void s5pcsis_enable_interrupts(struct csis_state *state, bool on) | ||
275 | { | ||
276 | u32 val = s5pcsis_read(state, S5PCSIS_INTMSK); | ||
277 | |||
278 | val = on ? val | S5PCSIS_INTMSK_EN_ALL : | ||
279 | val & ~S5PCSIS_INTMSK_EN_ALL; | ||
280 | s5pcsis_write(state, S5PCSIS_INTMSK, val); | ||
281 | } | ||
282 | |||
283 | static void s5pcsis_reset(struct csis_state *state) | ||
284 | { | ||
285 | u32 val = s5pcsis_read(state, S5PCSIS_CTRL); | ||
286 | |||
287 | s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_RESET); | ||
288 | udelay(10); | ||
289 | } | ||
290 | |||
291 | static void s5pcsis_system_enable(struct csis_state *state, int on) | ||
292 | { | ||
293 | u32 val, mask; | ||
294 | |||
295 | val = s5pcsis_read(state, S5PCSIS_CTRL); | ||
296 | if (on) | ||
297 | val |= S5PCSIS_CTRL_ENABLE; | ||
298 | else | ||
299 | val &= ~S5PCSIS_CTRL_ENABLE; | ||
300 | s5pcsis_write(state, S5PCSIS_CTRL, val); | ||
301 | |||
302 | val = s5pcsis_read(state, S5PCSIS_DPHYCTRL); | ||
303 | val &= ~S5PCSIS_DPHYCTRL_ENABLE; | ||
304 | if (on) { | ||
305 | mask = (1 << (state->num_lanes + 1)) - 1; | ||
306 | val |= (mask & S5PCSIS_DPHYCTRL_ENABLE); | ||
307 | } | ||
308 | s5pcsis_write(state, S5PCSIS_DPHYCTRL, val); | ||
309 | } | ||
310 | |||
311 | /* Called with the state.lock mutex held */ | ||
312 | static void __s5pcsis_set_format(struct csis_state *state) | ||
313 | { | ||
314 | struct v4l2_mbus_framefmt *mf = &state->format; | ||
315 | u32 val; | ||
316 | |||
317 | v4l2_dbg(1, debug, &state->sd, "fmt: %#x, %d x %d\n", | ||
318 | mf->code, mf->width, mf->height); | ||
319 | |||
320 | /* Color format */ | ||
321 | val = s5pcsis_read(state, S5PCSIS_CONFIG); | ||
322 | val = (val & ~S5PCSIS_CFG_FMT_MASK) | state->csis_fmt->fmt_reg; | ||
323 | s5pcsis_write(state, S5PCSIS_CONFIG, val); | ||
324 | |||
325 | /* Pixel resolution */ | ||
326 | val = (mf->width << 16) | mf->height; | ||
327 | s5pcsis_write(state, S5PCSIS_RESOL, val); | ||
328 | } | ||
329 | |||
330 | static void s5pcsis_set_hsync_settle(struct csis_state *state, int settle) | ||
331 | { | ||
332 | u32 val = s5pcsis_read(state, S5PCSIS_DPHYCTRL); | ||
333 | |||
334 | val = (val & ~S5PCSIS_DPHYCTRL_HSS_MASK) | (settle << 27); | ||
335 | s5pcsis_write(state, S5PCSIS_DPHYCTRL, val); | ||
336 | } | ||
337 | |||
338 | static void s5pcsis_set_params(struct csis_state *state) | ||
339 | { | ||
340 | u32 val; | ||
341 | |||
342 | val = s5pcsis_read(state, S5PCSIS_CONFIG); | ||
343 | val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (state->num_lanes - 1); | ||
344 | s5pcsis_write(state, S5PCSIS_CONFIG, val); | ||
345 | |||
346 | __s5pcsis_set_format(state); | ||
347 | s5pcsis_set_hsync_settle(state, state->hs_settle); | ||
348 | |||
349 | val = s5pcsis_read(state, S5PCSIS_CTRL); | ||
350 | if (state->csis_fmt->data_alignment == 32) | ||
351 | val |= S5PCSIS_CTRL_ALIGN_32BIT; | ||
352 | else /* 24-bits */ | ||
353 | val &= ~S5PCSIS_CTRL_ALIGN_32BIT; | ||
354 | |||
355 | val &= ~S5PCSIS_CTRL_WCLK_EXTCLK; | ||
356 | if (state->wclk_ext) | ||
357 | val |= S5PCSIS_CTRL_WCLK_EXTCLK; | ||
358 | s5pcsis_write(state, S5PCSIS_CTRL, val); | ||
359 | |||
360 | /* Update the shadow register. */ | ||
361 | val = s5pcsis_read(state, S5PCSIS_CTRL); | ||
362 | s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_UPDATE_SHADOW); | ||
363 | } | ||
364 | |||
365 | static void s5pcsis_clk_put(struct csis_state *state) | ||
366 | { | ||
367 | int i; | ||
368 | |||
369 | for (i = 0; i < NUM_CSIS_CLOCKS; i++) { | ||
370 | if (IS_ERR(state->clock[i])) | ||
371 | continue; | ||
372 | clk_unprepare(state->clock[i]); | ||
373 | clk_put(state->clock[i]); | ||
374 | state->clock[i] = ERR_PTR(-EINVAL); | ||
375 | } | ||
376 | } | ||
377 | |||
378 | static int s5pcsis_clk_get(struct csis_state *state) | ||
379 | { | ||
380 | struct device *dev = &state->pdev->dev; | ||
381 | int i, ret; | ||
382 | |||
383 | for (i = 0; i < NUM_CSIS_CLOCKS; i++) | ||
384 | state->clock[i] = ERR_PTR(-EINVAL); | ||
385 | |||
386 | for (i = 0; i < NUM_CSIS_CLOCKS; i++) { | ||
387 | state->clock[i] = clk_get(dev, csi_clock_name[i]); | ||
388 | if (IS_ERR(state->clock[i])) { | ||
389 | ret = PTR_ERR(state->clock[i]); | ||
390 | goto err; | ||
391 | } | ||
392 | ret = clk_prepare(state->clock[i]); | ||
393 | if (ret < 0) { | ||
394 | clk_put(state->clock[i]); | ||
395 | state->clock[i] = ERR_PTR(-EINVAL); | ||
396 | goto err; | ||
397 | } | ||
398 | } | ||
399 | return 0; | ||
400 | err: | ||
401 | s5pcsis_clk_put(state); | ||
402 | dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]); | ||
403 | return ret; | ||
404 | } | ||
405 | |||
406 | static void dump_regs(struct csis_state *state, const char *label) | ||
407 | { | ||
408 | struct { | ||
409 | u32 offset; | ||
410 | const char * const name; | ||
411 | } registers[] = { | ||
412 | { 0x00, "CTRL" }, | ||
413 | { 0x04, "DPHYCTRL" }, | ||
414 | { 0x08, "CONFIG" }, | ||
415 | { 0x0c, "DPHYSTS" }, | ||
416 | { 0x10, "INTMSK" }, | ||
417 | { 0x2c, "RESOL" }, | ||
418 | { 0x38, "SDW_CONFIG" }, | ||
419 | }; | ||
420 | u32 i; | ||
421 | |||
422 | v4l2_info(&state->sd, "--- %s ---\n", label); | ||
423 | |||
424 | for (i = 0; i < ARRAY_SIZE(registers); i++) { | ||
425 | u32 cfg = s5pcsis_read(state, registers[i].offset); | ||
426 | v4l2_info(&state->sd, "%10s: 0x%08x\n", registers[i].name, cfg); | ||
427 | } | ||
428 | } | ||
429 | |||
430 | static void s5pcsis_start_stream(struct csis_state *state) | ||
431 | { | ||
432 | s5pcsis_reset(state); | ||
433 | s5pcsis_set_params(state); | ||
434 | s5pcsis_system_enable(state, true); | ||
435 | s5pcsis_enable_interrupts(state, true); | ||
436 | } | ||
437 | |||
438 | static void s5pcsis_stop_stream(struct csis_state *state) | ||
439 | { | ||
440 | s5pcsis_enable_interrupts(state, false); | ||
441 | s5pcsis_system_enable(state, false); | ||
442 | } | ||
443 | |||
444 | static void s5pcsis_clear_counters(struct csis_state *state) | ||
445 | { | ||
446 | unsigned long flags; | ||
447 | int i; | ||
448 | |||
449 | spin_lock_irqsave(&state->slock, flags); | ||
450 | for (i = 0; i < S5PCSIS_NUM_EVENTS; i++) | ||
451 | state->events[i].counter = 0; | ||
452 | spin_unlock_irqrestore(&state->slock, flags); | ||
453 | } | ||
454 | |||
455 | static void s5pcsis_log_counters(struct csis_state *state, bool non_errors) | ||
456 | { | ||
457 | int i = non_errors ? S5PCSIS_NUM_EVENTS : S5PCSIS_NUM_EVENTS - 4; | ||
458 | unsigned long flags; | ||
459 | |||
460 | spin_lock_irqsave(&state->slock, flags); | ||
461 | |||
462 | for (i--; i >= 0; i--) { | ||
463 | if (state->events[i].counter > 0 || debug) | ||
464 | v4l2_info(&state->sd, "%s events: %d\n", | ||
465 | state->events[i].name, | ||
466 | state->events[i].counter); | ||
467 | } | ||
468 | spin_unlock_irqrestore(&state->slock, flags); | ||
469 | } | ||
470 | |||
471 | /* | ||
472 | * V4L2 subdev operations | ||
473 | */ | ||
474 | static int s5pcsis_s_power(struct v4l2_subdev *sd, int on) | ||
475 | { | ||
476 | struct csis_state *state = sd_to_csis_state(sd); | ||
477 | struct device *dev = &state->pdev->dev; | ||
478 | |||
479 | if (on) | ||
480 | return pm_runtime_get_sync(dev); | ||
481 | |||
482 | return pm_runtime_put_sync(dev); | ||
483 | } | ||
484 | |||
485 | static int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable) | ||
486 | { | ||
487 | struct csis_state *state = sd_to_csis_state(sd); | ||
488 | int ret = 0; | ||
489 | |||
490 | v4l2_dbg(1, debug, sd, "%s: %d, state: 0x%x\n", | ||
491 | __func__, enable, state->flags); | ||
492 | |||
493 | if (enable) { | ||
494 | s5pcsis_clear_counters(state); | ||
495 | ret = pm_runtime_get_sync(&state->pdev->dev); | ||
496 | if (ret && ret != 1) | ||
497 | return ret; | ||
498 | } | ||
499 | |||
500 | mutex_lock(&state->lock); | ||
501 | if (enable) { | ||
502 | if (state->flags & ST_SUSPENDED) { | ||
503 | ret = -EBUSY; | ||
504 | goto unlock; | ||
505 | } | ||
506 | s5pcsis_start_stream(state); | ||
507 | state->flags |= ST_STREAMING; | ||
508 | } else { | ||
509 | s5pcsis_stop_stream(state); | ||
510 | state->flags &= ~ST_STREAMING; | ||
511 | if (debug > 0) | ||
512 | s5pcsis_log_counters(state, true); | ||
513 | } | ||
514 | unlock: | ||
515 | mutex_unlock(&state->lock); | ||
516 | if (!enable) | ||
517 | pm_runtime_put(&state->pdev->dev); | ||
518 | |||
519 | return ret == 1 ? 0 : ret; | ||
520 | } | ||
521 | |||
522 | static int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd, | ||
523 | struct v4l2_subdev_fh *fh, | ||
524 | struct v4l2_subdev_mbus_code_enum *code) | ||
525 | { | ||
526 | if (code->index >= ARRAY_SIZE(s5pcsis_formats)) | ||
527 | return -EINVAL; | ||
528 | |||
529 | code->code = s5pcsis_formats[code->index].code; | ||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | static struct csis_pix_format const *s5pcsis_try_format( | ||
534 | struct v4l2_mbus_framefmt *mf) | ||
535 | { | ||
536 | struct csis_pix_format const *csis_fmt; | ||
537 | |||
538 | csis_fmt = find_csis_format(mf); | ||
539 | if (csis_fmt == NULL) | ||
540 | csis_fmt = &s5pcsis_formats[0]; | ||
541 | |||
542 | mf->code = csis_fmt->code; | ||
543 | v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH, | ||
544 | csis_fmt->pix_width_alignment, | ||
545 | &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1, | ||
546 | 0); | ||
547 | return csis_fmt; | ||
548 | } | ||
549 | |||
550 | static struct v4l2_mbus_framefmt *__s5pcsis_get_format( | ||
551 | struct csis_state *state, struct v4l2_subdev_fh *fh, | ||
552 | enum v4l2_subdev_format_whence which) | ||
553 | { | ||
554 | if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
555 | return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL; | ||
556 | |||
557 | return &state->format; | ||
558 | } | ||
559 | |||
560 | static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
561 | struct v4l2_subdev_format *fmt) | ||
562 | { | ||
563 | struct csis_state *state = sd_to_csis_state(sd); | ||
564 | struct csis_pix_format const *csis_fmt; | ||
565 | struct v4l2_mbus_framefmt *mf; | ||
566 | |||
567 | mf = __s5pcsis_get_format(state, fh, fmt->which); | ||
568 | |||
569 | if (fmt->pad == CSIS_PAD_SOURCE) { | ||
570 | if (mf) { | ||
571 | mutex_lock(&state->lock); | ||
572 | fmt->format = *mf; | ||
573 | mutex_unlock(&state->lock); | ||
574 | } | ||
575 | return 0; | ||
576 | } | ||
577 | csis_fmt = s5pcsis_try_format(&fmt->format); | ||
578 | if (mf) { | ||
579 | mutex_lock(&state->lock); | ||
580 | *mf = fmt->format; | ||
581 | if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) | ||
582 | state->csis_fmt = csis_fmt; | ||
583 | mutex_unlock(&state->lock); | ||
584 | } | ||
585 | return 0; | ||
586 | } | ||
587 | |||
588 | static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
589 | struct v4l2_subdev_format *fmt) | ||
590 | { | ||
591 | struct csis_state *state = sd_to_csis_state(sd); | ||
592 | struct v4l2_mbus_framefmt *mf; | ||
593 | |||
594 | mf = __s5pcsis_get_format(state, fh, fmt->which); | ||
595 | if (!mf) | ||
596 | return -EINVAL; | ||
597 | |||
598 | mutex_lock(&state->lock); | ||
599 | fmt->format = *mf; | ||
600 | mutex_unlock(&state->lock); | ||
601 | return 0; | ||
602 | } | ||
603 | |||
604 | static int s5pcsis_s_rx_buffer(struct v4l2_subdev *sd, void *buf, | ||
605 | unsigned int *size) | ||
606 | { | ||
607 | struct csis_state *state = sd_to_csis_state(sd); | ||
608 | unsigned long flags; | ||
609 | |||
610 | *size = min_t(unsigned int, *size, S5PCSIS_PKTDATA_SIZE); | ||
611 | |||
612 | spin_lock_irqsave(&state->slock, flags); | ||
613 | state->pkt_buf.data = buf; | ||
614 | state->pkt_buf.len = *size; | ||
615 | spin_unlock_irqrestore(&state->slock, flags); | ||
616 | |||
617 | return 0; | ||
618 | } | ||
619 | |||
620 | static int s5pcsis_log_status(struct v4l2_subdev *sd) | ||
621 | { | ||
622 | struct csis_state *state = sd_to_csis_state(sd); | ||
623 | |||
624 | mutex_lock(&state->lock); | ||
625 | s5pcsis_log_counters(state, true); | ||
626 | if (debug && (state->flags & ST_POWERED)) | ||
627 | dump_regs(state, __func__); | ||
628 | mutex_unlock(&state->lock); | ||
629 | return 0; | ||
630 | } | ||
631 | |||
632 | static int s5pcsis_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
633 | { | ||
634 | struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); | ||
635 | |||
636 | format->colorspace = V4L2_COLORSPACE_JPEG; | ||
637 | format->code = s5pcsis_formats[0].code; | ||
638 | format->width = S5PCSIS_DEF_PIX_WIDTH; | ||
639 | format->height = S5PCSIS_DEF_PIX_HEIGHT; | ||
640 | format->field = V4L2_FIELD_NONE; | ||
641 | |||
642 | return 0; | ||
643 | } | ||
644 | |||
645 | static const struct v4l2_subdev_internal_ops s5pcsis_sd_internal_ops = { | ||
646 | .open = s5pcsis_open, | ||
647 | }; | ||
648 | |||
649 | static struct v4l2_subdev_core_ops s5pcsis_core_ops = { | ||
650 | .s_power = s5pcsis_s_power, | ||
651 | .log_status = s5pcsis_log_status, | ||
652 | }; | ||
653 | |||
654 | static struct v4l2_subdev_pad_ops s5pcsis_pad_ops = { | ||
655 | .enum_mbus_code = s5pcsis_enum_mbus_code, | ||
656 | .get_fmt = s5pcsis_get_fmt, | ||
657 | .set_fmt = s5pcsis_set_fmt, | ||
658 | }; | ||
659 | |||
660 | static struct v4l2_subdev_video_ops s5pcsis_video_ops = { | ||
661 | .s_rx_buffer = s5pcsis_s_rx_buffer, | ||
662 | .s_stream = s5pcsis_s_stream, | ||
663 | }; | ||
664 | |||
665 | static struct v4l2_subdev_ops s5pcsis_subdev_ops = { | ||
666 | .core = &s5pcsis_core_ops, | ||
667 | .pad = &s5pcsis_pad_ops, | ||
668 | .video = &s5pcsis_video_ops, | ||
669 | }; | ||
670 | |||
671 | static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id) | ||
672 | { | ||
673 | struct csis_state *state = dev_id; | ||
674 | struct csis_pktbuf *pktbuf = &state->pkt_buf; | ||
675 | unsigned long flags; | ||
676 | u32 status; | ||
677 | |||
678 | status = s5pcsis_read(state, S5PCSIS_INTSRC); | ||
679 | spin_lock_irqsave(&state->slock, flags); | ||
680 | |||
681 | if ((status & S5PCSIS_INTSRC_NON_IMAGE_DATA) && pktbuf->data) { | ||
682 | u32 offset; | ||
683 | |||
684 | if (status & S5PCSIS_INTSRC_EVEN) | ||
685 | offset = S5PCSIS_PKTDATA_EVEN; | ||
686 | else | ||
687 | offset = S5PCSIS_PKTDATA_ODD; | ||
688 | |||
689 | memcpy(pktbuf->data, state->regs + offset, pktbuf->len); | ||
690 | pktbuf->data = NULL; | ||
691 | rmb(); | ||
692 | } | ||
693 | |||
694 | /* Update the event/error counters */ | ||
695 | if ((status & S5PCSIS_INTSRC_ERRORS) || debug) { | ||
696 | int i; | ||
697 | for (i = 0; i < S5PCSIS_NUM_EVENTS; i++) { | ||
698 | if (!(status & state->events[i].mask)) | ||
699 | continue; | ||
700 | state->events[i].counter++; | ||
701 | v4l2_dbg(2, debug, &state->sd, "%s: %d\n", | ||
702 | state->events[i].name, | ||
703 | state->events[i].counter); | ||
704 | } | ||
705 | v4l2_dbg(2, debug, &state->sd, "status: %08x\n", status); | ||
706 | } | ||
707 | spin_unlock_irqrestore(&state->slock, flags); | ||
708 | |||
709 | s5pcsis_write(state, S5PCSIS_INTSRC, status); | ||
710 | return IRQ_HANDLED; | ||
711 | } | ||
712 | |||
713 | static int s5pcsis_get_platform_data(struct platform_device *pdev, | ||
714 | struct csis_state *state) | ||
715 | { | ||
716 | struct s5p_platform_mipi_csis *pdata = pdev->dev.platform_data; | ||
717 | |||
718 | if (pdata == NULL) { | ||
719 | dev_err(&pdev->dev, "Platform data not specified\n"); | ||
720 | return -EINVAL; | ||
721 | } | ||
722 | |||
723 | state->clk_frequency = pdata->clk_rate; | ||
724 | state->num_lanes = pdata->lanes; | ||
725 | state->hs_settle = pdata->hs_settle; | ||
726 | state->index = max(0, pdev->id); | ||
727 | state->max_num_lanes = state->index ? CSIS1_MAX_LANES : | ||
728 | CSIS0_MAX_LANES; | ||
729 | return 0; | ||
730 | } | ||
731 | |||
732 | #ifdef CONFIG_OF | ||
733 | static int s5pcsis_parse_dt(struct platform_device *pdev, | ||
734 | struct csis_state *state) | ||
735 | { | ||
736 | struct device_node *node = pdev->dev.of_node; | ||
737 | struct v4l2_of_endpoint endpoint; | ||
738 | |||
739 | if (of_property_read_u32(node, "clock-frequency", | ||
740 | &state->clk_frequency)) | ||
741 | state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ; | ||
742 | if (of_property_read_u32(node, "bus-width", | ||
743 | &state->max_num_lanes)) | ||
744 | return -EINVAL; | ||
745 | |||
746 | node = v4l2_of_get_next_endpoint(node, NULL); | ||
747 | if (!node) { | ||
748 | dev_err(&pdev->dev, "No port node at %s\n", | ||
749 | node->full_name); | ||
750 | return -EINVAL; | ||
751 | } | ||
752 | /* Get port node and validate MIPI-CSI channel id. */ | ||
753 | v4l2_of_parse_endpoint(node, &endpoint); | ||
754 | |||
755 | state->index = endpoint.port - FIMC_INPUT_MIPI_CSI2_0; | ||
756 | if (state->index < 0 || state->index >= CSIS_MAX_ENTITIES) | ||
757 | return -ENXIO; | ||
758 | |||
759 | /* Get MIPI CSI-2 bus configration from the endpoint node. */ | ||
760 | of_property_read_u32(node, "samsung,csis-hs-settle", | ||
761 | &state->hs_settle); | ||
762 | state->wclk_ext = of_property_read_bool(node, | ||
763 | "samsung,csis-wclk"); | ||
764 | |||
765 | state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes; | ||
766 | |||
767 | of_node_put(node); | ||
768 | return 0; | ||
769 | } | ||
770 | #else | ||
771 | #define s5pcsis_parse_dt(pdev, state) (-ENOSYS) | ||
772 | #endif | ||
773 | |||
774 | static int s5pcsis_probe(struct platform_device *pdev) | ||
775 | { | ||
776 | struct device *dev = &pdev->dev; | ||
777 | struct resource *mem_res; | ||
778 | struct csis_state *state; | ||
779 | int ret = -ENOMEM; | ||
780 | int i; | ||
781 | |||
782 | state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); | ||
783 | if (!state) | ||
784 | return -ENOMEM; | ||
785 | |||
786 | mutex_init(&state->lock); | ||
787 | spin_lock_init(&state->slock); | ||
788 | state->pdev = pdev; | ||
789 | |||
790 | if (dev->of_node) | ||
791 | ret = s5pcsis_parse_dt(pdev, state); | ||
792 | else | ||
793 | ret = s5pcsis_get_platform_data(pdev, state); | ||
794 | if (ret < 0) | ||
795 | return ret; | ||
796 | |||
797 | if (state->num_lanes == 0 || state->num_lanes > state->max_num_lanes) { | ||
798 | dev_err(dev, "Unsupported number of data lanes: %d (max. %d)\n", | ||
799 | state->num_lanes, state->max_num_lanes); | ||
800 | return -EINVAL; | ||
801 | } | ||
802 | |||
803 | mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
804 | state->regs = devm_ioremap_resource(dev, mem_res); | ||
805 | if (IS_ERR(state->regs)) | ||
806 | return PTR_ERR(state->regs); | ||
807 | |||
808 | state->irq = platform_get_irq(pdev, 0); | ||
809 | if (state->irq < 0) { | ||
810 | dev_err(dev, "Failed to get irq\n"); | ||
811 | return state->irq; | ||
812 | } | ||
813 | |||
814 | for (i = 0; i < CSIS_NUM_SUPPLIES; i++) | ||
815 | state->supplies[i].supply = csis_supply_name[i]; | ||
816 | |||
817 | ret = devm_regulator_bulk_get(dev, CSIS_NUM_SUPPLIES, | ||
818 | state->supplies); | ||
819 | if (ret) | ||
820 | return ret; | ||
821 | |||
822 | ret = s5pcsis_clk_get(state); | ||
823 | if (ret < 0) | ||
824 | return ret; | ||
825 | |||
826 | if (state->clk_frequency) | ||
827 | ret = clk_set_rate(state->clock[CSIS_CLK_MUX], | ||
828 | state->clk_frequency); | ||
829 | else | ||
830 | dev_WARN(dev, "No clock frequency specified!\n"); | ||
831 | if (ret < 0) | ||
832 | goto e_clkput; | ||
833 | |||
834 | ret = clk_enable(state->clock[CSIS_CLK_MUX]); | ||
835 | if (ret < 0) | ||
836 | goto e_clkput; | ||
837 | |||
838 | ret = devm_request_irq(dev, state->irq, s5pcsis_irq_handler, | ||
839 | 0, dev_name(dev), state); | ||
840 | if (ret) { | ||
841 | dev_err(dev, "Interrupt request failed\n"); | ||
842 | goto e_clkdis; | ||
843 | } | ||
844 | |||
845 | v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops); | ||
846 | state->sd.owner = THIS_MODULE; | ||
847 | snprintf(state->sd.name, sizeof(state->sd.name), "%s.%d", | ||
848 | CSIS_SUBDEV_NAME, state->index); | ||
849 | state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
850 | state->csis_fmt = &s5pcsis_formats[0]; | ||
851 | |||
852 | state->format.code = s5pcsis_formats[0].code; | ||
853 | state->format.width = S5PCSIS_DEF_PIX_WIDTH; | ||
854 | state->format.height = S5PCSIS_DEF_PIX_HEIGHT; | ||
855 | |||
856 | state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
857 | state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; | ||
858 | ret = media_entity_init(&state->sd.entity, | ||
859 | CSIS_PADS_NUM, state->pads, 0); | ||
860 | if (ret < 0) | ||
861 | goto e_clkdis; | ||
862 | |||
863 | /* This allows to retrieve the platform device id by the host driver */ | ||
864 | v4l2_set_subdevdata(&state->sd, pdev); | ||
865 | |||
866 | /* .. and a pointer to the subdev. */ | ||
867 | platform_set_drvdata(pdev, &state->sd); | ||
868 | memcpy(state->events, s5pcsis_events, sizeof(state->events)); | ||
869 | pm_runtime_enable(dev); | ||
870 | |||
871 | dev_info(&pdev->dev, "lanes: %d, hs_settle: %d, wclk: %d, freq: %u\n", | ||
872 | state->num_lanes, state->hs_settle, state->wclk_ext, | ||
873 | state->clk_frequency); | ||
874 | return 0; | ||
875 | |||
876 | e_clkdis: | ||
877 | clk_disable(state->clock[CSIS_CLK_MUX]); | ||
878 | e_clkput: | ||
879 | s5pcsis_clk_put(state); | ||
880 | return ret; | ||
881 | } | ||
882 | |||
883 | static int s5pcsis_pm_suspend(struct device *dev, bool runtime) | ||
884 | { | ||
885 | struct platform_device *pdev = to_platform_device(dev); | ||
886 | struct v4l2_subdev *sd = platform_get_drvdata(pdev); | ||
887 | struct csis_state *state = sd_to_csis_state(sd); | ||
888 | int ret = 0; | ||
889 | |||
890 | v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n", | ||
891 | __func__, state->flags); | ||
892 | |||
893 | mutex_lock(&state->lock); | ||
894 | if (state->flags & ST_POWERED) { | ||
895 | s5pcsis_stop_stream(state); | ||
896 | ret = s5p_csis_phy_enable(state->index, false); | ||
897 | if (ret) | ||
898 | goto unlock; | ||
899 | ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES, | ||
900 | state->supplies); | ||
901 | if (ret) | ||
902 | goto unlock; | ||
903 | clk_disable(state->clock[CSIS_CLK_GATE]); | ||
904 | state->flags &= ~ST_POWERED; | ||
905 | if (!runtime) | ||
906 | state->flags |= ST_SUSPENDED; | ||
907 | } | ||
908 | unlock: | ||
909 | mutex_unlock(&state->lock); | ||
910 | return ret ? -EAGAIN : 0; | ||
911 | } | ||
912 | |||
913 | static int s5pcsis_pm_resume(struct device *dev, bool runtime) | ||
914 | { | ||
915 | struct platform_device *pdev = to_platform_device(dev); | ||
916 | struct v4l2_subdev *sd = platform_get_drvdata(pdev); | ||
917 | struct csis_state *state = sd_to_csis_state(sd); | ||
918 | int ret = 0; | ||
919 | |||
920 | v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n", | ||
921 | __func__, state->flags); | ||
922 | |||
923 | mutex_lock(&state->lock); | ||
924 | if (!runtime && !(state->flags & ST_SUSPENDED)) | ||
925 | goto unlock; | ||
926 | |||
927 | if (!(state->flags & ST_POWERED)) { | ||
928 | ret = regulator_bulk_enable(CSIS_NUM_SUPPLIES, | ||
929 | state->supplies); | ||
930 | if (ret) | ||
931 | goto unlock; | ||
932 | ret = s5p_csis_phy_enable(state->index, true); | ||
933 | if (!ret) { | ||
934 | state->flags |= ST_POWERED; | ||
935 | } else { | ||
936 | regulator_bulk_disable(CSIS_NUM_SUPPLIES, | ||
937 | state->supplies); | ||
938 | goto unlock; | ||
939 | } | ||
940 | clk_enable(state->clock[CSIS_CLK_GATE]); | ||
941 | } | ||
942 | if (state->flags & ST_STREAMING) | ||
943 | s5pcsis_start_stream(state); | ||
944 | |||
945 | state->flags &= ~ST_SUSPENDED; | ||
946 | unlock: | ||
947 | mutex_unlock(&state->lock); | ||
948 | return ret ? -EAGAIN : 0; | ||
949 | } | ||
950 | |||
951 | #ifdef CONFIG_PM_SLEEP | ||
952 | static int s5pcsis_suspend(struct device *dev) | ||
953 | { | ||
954 | return s5pcsis_pm_suspend(dev, false); | ||
955 | } | ||
956 | |||
957 | static int s5pcsis_resume(struct device *dev) | ||
958 | { | ||
959 | return s5pcsis_pm_resume(dev, false); | ||
960 | } | ||
961 | #endif | ||
962 | |||
963 | #ifdef CONFIG_PM_RUNTIME | ||
964 | static int s5pcsis_runtime_suspend(struct device *dev) | ||
965 | { | ||
966 | return s5pcsis_pm_suspend(dev, true); | ||
967 | } | ||
968 | |||
969 | static int s5pcsis_runtime_resume(struct device *dev) | ||
970 | { | ||
971 | return s5pcsis_pm_resume(dev, true); | ||
972 | } | ||
973 | #endif | ||
974 | |||
975 | static int s5pcsis_remove(struct platform_device *pdev) | ||
976 | { | ||
977 | struct v4l2_subdev *sd = platform_get_drvdata(pdev); | ||
978 | struct csis_state *state = sd_to_csis_state(sd); | ||
979 | |||
980 | pm_runtime_disable(&pdev->dev); | ||
981 | s5pcsis_pm_suspend(&pdev->dev, false); | ||
982 | clk_disable(state->clock[CSIS_CLK_MUX]); | ||
983 | pm_runtime_set_suspended(&pdev->dev); | ||
984 | s5pcsis_clk_put(state); | ||
985 | |||
986 | media_entity_cleanup(&state->sd.entity); | ||
987 | |||
988 | return 0; | ||
989 | } | ||
990 | |||
991 | static const struct dev_pm_ops s5pcsis_pm_ops = { | ||
992 | SET_RUNTIME_PM_OPS(s5pcsis_runtime_suspend, s5pcsis_runtime_resume, | ||
993 | NULL) | ||
994 | SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume) | ||
995 | }; | ||
996 | |||
997 | static const struct of_device_id s5pcsis_of_match[] = { | ||
998 | { .compatible = "samsung,s5pv210-csis" }, | ||
999 | { .compatible = "samsung,exynos4210-csis" }, | ||
1000 | { /* sentinel */ }, | ||
1001 | }; | ||
1002 | MODULE_DEVICE_TABLE(of, s5pcsis_of_match); | ||
1003 | |||
1004 | static struct platform_driver s5pcsis_driver = { | ||
1005 | .probe = s5pcsis_probe, | ||
1006 | .remove = s5pcsis_remove, | ||
1007 | .driver = { | ||
1008 | .of_match_table = s5pcsis_of_match, | ||
1009 | .name = CSIS_DRIVER_NAME, | ||
1010 | .owner = THIS_MODULE, | ||
1011 | .pm = &s5pcsis_pm_ops, | ||
1012 | }, | ||
1013 | }; | ||
1014 | |||
1015 | module_platform_driver(s5pcsis_driver); | ||
1016 | |||
1017 | MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); | ||
1018 | MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver"); | ||
1019 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/platform/exynos4-is/mipi-csis.h b/drivers/media/platform/exynos4-is/mipi-csis.h new file mode 100644 index 000000000000..28c11c4085d8 --- /dev/null +++ b/drivers/media/platform/exynos4-is/mipi-csis.h | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver | ||
3 | * | ||
4 | * Copyright (C) 2011 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #ifndef S5P_MIPI_CSIS_H_ | ||
11 | #define S5P_MIPI_CSIS_H_ | ||
12 | |||
13 | #define CSIS_DRIVER_NAME "s5p-mipi-csis" | ||
14 | #define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME | ||
15 | #define CSIS_MAX_ENTITIES 2 | ||
16 | #define CSIS0_MAX_LANES 4 | ||
17 | #define CSIS1_MAX_LANES 2 | ||
18 | |||
19 | #define CSIS_PAD_SINK 0 | ||
20 | #define CSIS_PAD_SOURCE 1 | ||
21 | #define CSIS_PADS_NUM 2 | ||
22 | |||
23 | #define S5PCSIS_DEF_PIX_WIDTH 640 | ||
24 | #define S5PCSIS_DEF_PIX_HEIGHT 480 | ||
25 | |||
26 | #endif | ||