diff options
author | Sylwester Nawrocki <sylvester.nawrocki@gmail.com> | 2012-08-22 19:09:18 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-11-28 06:43:34 -0500 |
commit | babde1c243b25592038dd7193867a49729e3d505 (patch) | |
tree | 30277f8be0979d4b9a3e32d50607e4c99df33ff4 /drivers/media | |
parent | d033a308d81ec83908760a15a841c2bd0d5e0ea3 (diff) |
[media] V4L: Add driver for S3C24XX/S3C64XX SoC series camera interface
This patch adds V4L2 driver for Samsung S3C24XX/S3C64XX SoC series
camera interface. The driver exposes a subdev device node for CAMIF
pixel resolution and crop control and two video capture nodes - for
the "codec" and "preview" data paths. It has been tested on Mini2440
(s3c2440) and Mini6410 (s3c6410) board with gstreamer and mplayer.
Signed-off-by: Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
Signed-off-by: Tomasz Figa <tomasz.figa@gmail.com>
Signed-off-by: Andrey Gusakov <dron0gus@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/platform/Kconfig | 12 | ||||
-rw-r--r-- | drivers/media/platform/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/platform/s3c-camif/Makefile | 5 | ||||
-rw-r--r-- | drivers/media/platform/s3c-camif/camif-capture.c | 1672 | ||||
-rw-r--r-- | drivers/media/platform/s3c-camif/camif-core.c | 662 | ||||
-rw-r--r-- | drivers/media/platform/s3c-camif/camif-core.h | 393 | ||||
-rw-r--r-- | drivers/media/platform/s3c-camif/camif-regs.c | 606 | ||||
-rw-r--r-- | drivers/media/platform/s3c-camif/camif-regs.h | 269 |
8 files changed, 3620 insertions, 0 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 181c7686e412..3dcfea612c42 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig | |||
@@ -109,6 +109,18 @@ config VIDEO_OMAP3_DEBUG | |||
109 | ---help--- | 109 | ---help--- |
110 | Enable debug messages on OMAP 3 camera controller driver. | 110 | Enable debug messages on OMAP 3 camera controller driver. |
111 | 111 | ||
112 | config VIDEO_S3C_CAMIF | ||
113 | tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver" | ||
114 | depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API | ||
115 | depends on (PLAT_S3C64XX || PLAT_S3C24XX) && PM_RUNTIME | ||
116 | select VIDEOBUF2_DMA_CONTIG | ||
117 | ---help--- | ||
118 | This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera | ||
119 | host interface (CAMIF). | ||
120 | |||
121 | To compile this driver as a module, choose M here: the module | ||
122 | will be called s3c-camif. | ||
123 | |||
112 | source "drivers/media/platform/soc_camera/Kconfig" | 124 | source "drivers/media/platform/soc_camera/Kconfig" |
113 | source "drivers/media/platform/s5p-fimc/Kconfig" | 125 | source "drivers/media/platform/s5p-fimc/Kconfig" |
114 | source "drivers/media/platform/s5p-tv/Kconfig" | 126 | source "drivers/media/platform/s5p-tv/Kconfig" |
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index baaa55026c8e..4817d2802171 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile | |||
@@ -27,6 +27,7 @@ obj-$(CONFIG_VIDEO_CODA) += coda.o | |||
27 | 27 | ||
28 | obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o | 28 | obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o |
29 | 29 | ||
30 | obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/ | ||
30 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/ | 31 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/ |
31 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/ | 32 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/ |
32 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/ | 33 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/ |
diff --git a/drivers/media/platform/s3c-camif/Makefile b/drivers/media/platform/s3c-camif/Makefile new file mode 100644 index 000000000000..50bf8c59b99c --- /dev/null +++ b/drivers/media/platform/s3c-camif/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | # Makefile for s3c244x/s3c64xx CAMIF driver | ||
2 | |||
3 | s3c-camif-objs := camif-core.o camif-capture.o camif-regs.o | ||
4 | |||
5 | obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif.o | ||
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c new file mode 100644 index 000000000000..a55793c3d811 --- /dev/null +++ b/drivers/media/platform/s3c-camif/camif-capture.c | |||
@@ -0,0 +1,1672 @@ | |||
1 | /* | ||
2 | * s3c24xx/s3c64xx SoC series Camera Interface (CAMIF) driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com> | ||
5 | * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.com> | ||
6 | * | ||
7 | * Based on drivers/media/platform/s5p-fimc, | ||
8 | * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | #define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ | ||
15 | |||
16 | #include <linux/bug.h> | ||
17 | #include <linux/clk.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/i2c.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/io.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/list.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/pm_runtime.h> | ||
28 | #include <linux/ratelimit.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/types.h> | ||
31 | #include <linux/videodev2.h> | ||
32 | |||
33 | #include <media/media-device.h> | ||
34 | #include <media/v4l2-ctrls.h> | ||
35 | #include <media/v4l2-event.h> | ||
36 | #include <media/v4l2-ioctl.h> | ||
37 | #include <media/videobuf2-core.h> | ||
38 | #include <media/videobuf2-dma-contig.h> | ||
39 | |||
40 | #include "camif-core.h" | ||
41 | #include "camif-regs.h" | ||
42 | |||
43 | static int debug; | ||
44 | module_param(debug, int, 0644); | ||
45 | |||
46 | /* Locking: called with vp->camif->slock spinlock held */ | ||
47 | static void camif_cfg_video_path(struct camif_vp *vp) | ||
48 | { | ||
49 | WARN_ON(s3c_camif_get_scaler_config(vp, &vp->scaler)); | ||
50 | camif_hw_set_scaler(vp); | ||
51 | camif_hw_set_flip(vp); | ||
52 | camif_hw_set_target_format(vp); | ||
53 | camif_hw_set_output_dma(vp); | ||
54 | } | ||
55 | |||
56 | static void camif_prepare_dma_offset(struct camif_vp *vp) | ||
57 | { | ||
58 | struct camif_frame *f = &vp->out_frame; | ||
59 | |||
60 | f->dma_offset.initial = f->rect.top * f->f_width + f->rect.left; | ||
61 | f->dma_offset.line = f->f_width - (f->rect.left + f->rect.width); | ||
62 | |||
63 | pr_debug("dma_offset: initial: %d, line: %d\n", | ||
64 | f->dma_offset.initial, f->dma_offset.line); | ||
65 | } | ||
66 | |||
67 | /* Locking: called with camif->slock spinlock held */ | ||
68 | static int s3c_camif_hw_init(struct camif_dev *camif, struct camif_vp *vp) | ||
69 | { | ||
70 | const struct s3c_camif_variant *variant = camif->variant; | ||
71 | |||
72 | if (camif->sensor.sd == NULL || vp->out_fmt == NULL) | ||
73 | return -EINVAL; | ||
74 | |||
75 | if (variant->ip_revision == S3C244X_CAMIF_IP_REV) | ||
76 | camif_hw_clear_fifo_overflow(vp); | ||
77 | camif_hw_set_camera_bus(camif); | ||
78 | camif_hw_set_source_format(camif); | ||
79 | camif_hw_set_camera_crop(camif); | ||
80 | camif_hw_set_test_pattern(camif, camif->test_pattern); | ||
81 | if (variant->has_img_effect) | ||
82 | camif_hw_set_effect(camif, camif->colorfx, | ||
83 | camif->colorfx_cb, camif->colorfx_cr); | ||
84 | if (variant->ip_revision == S3C6410_CAMIF_IP_REV) | ||
85 | camif_hw_set_input_path(vp); | ||
86 | camif_cfg_video_path(vp); | ||
87 | vp->state &= ~ST_VP_CONFIG; | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | /* | ||
93 | * Initialize the video path, only up from the scaler stage. The camera | ||
94 | * input interface set up is skipped. This is useful to enable one of the | ||
95 | * video paths when the other is already running. | ||
96 | * Locking: called with camif->slock spinlock held. | ||
97 | */ | ||
98 | static int s3c_camif_hw_vp_init(struct camif_dev *camif, struct camif_vp *vp) | ||
99 | { | ||
100 | unsigned int ip_rev = camif->variant->ip_revision; | ||
101 | |||
102 | if (vp->out_fmt == NULL) | ||
103 | return -EINVAL; | ||
104 | |||
105 | camif_prepare_dma_offset(vp); | ||
106 | if (ip_rev == S3C244X_CAMIF_IP_REV) | ||
107 | camif_hw_clear_fifo_overflow(vp); | ||
108 | camif_cfg_video_path(vp); | ||
109 | vp->state &= ~ST_VP_CONFIG; | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static int sensor_set_power(struct camif_dev *camif, int on) | ||
114 | { | ||
115 | struct cam_sensor *sensor = &camif->sensor; | ||
116 | int err = 0; | ||
117 | |||
118 | if (!on == camif->sensor.power_count) | ||
119 | err = v4l2_subdev_call(sensor->sd, core, s_power, on); | ||
120 | if (!err) | ||
121 | sensor->power_count += on ? 1 : -1; | ||
122 | |||
123 | pr_debug("on: %d, power_count: %d, err: %d\n", | ||
124 | on, sensor->power_count, err); | ||
125 | |||
126 | return err; | ||
127 | } | ||
128 | |||
129 | static int sensor_set_streaming(struct camif_dev *camif, int on) | ||
130 | { | ||
131 | struct cam_sensor *sensor = &camif->sensor; | ||
132 | int err = 0; | ||
133 | |||
134 | if (!on == camif->sensor.stream_count) | ||
135 | err = v4l2_subdev_call(sensor->sd, video, s_stream, on); | ||
136 | if (!err) | ||
137 | sensor->stream_count += on ? 1 : -1; | ||
138 | |||
139 | pr_debug("on: %d, stream_count: %d, err: %d\n", | ||
140 | on, sensor->stream_count, err); | ||
141 | |||
142 | return err; | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * Reinitialize the driver so it is ready to start streaming again. | ||
147 | * Return any buffers to vb2, perform CAMIF software reset and | ||
148 | * turn off streaming at the data pipeline (sensor) if required. | ||
149 | */ | ||
150 | static int camif_reinitialize(struct camif_vp *vp) | ||
151 | { | ||
152 | struct camif_dev *camif = vp->camif; | ||
153 | struct camif_buffer *buf; | ||
154 | unsigned long flags; | ||
155 | bool streaming; | ||
156 | |||
157 | spin_lock_irqsave(&camif->slock, flags); | ||
158 | streaming = vp->state & ST_VP_SENSOR_STREAMING; | ||
159 | |||
160 | vp->state &= ~(ST_VP_PENDING | ST_VP_RUNNING | ST_VP_OFF | | ||
161 | ST_VP_ABORTING | ST_VP_STREAMING | | ||
162 | ST_VP_SENSOR_STREAMING | ST_VP_LASTIRQ); | ||
163 | |||
164 | /* Release unused buffers */ | ||
165 | while (!list_empty(&vp->pending_buf_q)) { | ||
166 | buf = camif_pending_queue_pop(vp); | ||
167 | vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); | ||
168 | } | ||
169 | |||
170 | while (!list_empty(&vp->active_buf_q)) { | ||
171 | buf = camif_active_queue_pop(vp); | ||
172 | vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); | ||
173 | } | ||
174 | |||
175 | spin_unlock_irqrestore(&camif->slock, flags); | ||
176 | |||
177 | if (!streaming) | ||
178 | return 0; | ||
179 | |||
180 | return sensor_set_streaming(camif, 0); | ||
181 | } | ||
182 | |||
183 | static bool s3c_vp_active(struct camif_vp *vp) | ||
184 | { | ||
185 | struct camif_dev *camif = vp->camif; | ||
186 | unsigned long flags; | ||
187 | bool ret; | ||
188 | |||
189 | spin_lock_irqsave(&camif->slock, flags); | ||
190 | ret = (vp->state & ST_VP_RUNNING) || (vp->state & ST_VP_PENDING); | ||
191 | spin_unlock_irqrestore(&camif->slock, flags); | ||
192 | |||
193 | return ret; | ||
194 | } | ||
195 | |||
196 | static bool camif_is_streaming(struct camif_dev *camif) | ||
197 | { | ||
198 | unsigned long flags; | ||
199 | bool status; | ||
200 | |||
201 | spin_lock_irqsave(&camif->slock, flags); | ||
202 | status = camif->stream_count > 0; | ||
203 | spin_unlock_irqrestore(&camif->slock, flags); | ||
204 | |||
205 | return status; | ||
206 | } | ||
207 | |||
208 | static int camif_stop_capture(struct camif_vp *vp) | ||
209 | { | ||
210 | struct camif_dev *camif = vp->camif; | ||
211 | unsigned long flags; | ||
212 | int ret; | ||
213 | |||
214 | if (!s3c_vp_active(vp)) | ||
215 | return 0; | ||
216 | |||
217 | spin_lock_irqsave(&camif->slock, flags); | ||
218 | vp->state &= ~(ST_VP_OFF | ST_VP_LASTIRQ); | ||
219 | vp->state |= ST_VP_ABORTING; | ||
220 | spin_unlock_irqrestore(&camif->slock, flags); | ||
221 | |||
222 | ret = wait_event_timeout(vp->irq_queue, | ||
223 | !(vp->state & ST_VP_ABORTING), | ||
224 | msecs_to_jiffies(CAMIF_STOP_TIMEOUT)); | ||
225 | |||
226 | spin_lock_irqsave(&camif->slock, flags); | ||
227 | |||
228 | if (ret == 0 && !(vp->state & ST_VP_OFF)) { | ||
229 | /* Timed out, forcibly stop capture */ | ||
230 | vp->state &= ~(ST_VP_OFF | ST_VP_ABORTING | | ||
231 | ST_VP_LASTIRQ); | ||
232 | |||
233 | camif_hw_disable_capture(vp); | ||
234 | camif_hw_enable_scaler(vp, false); | ||
235 | } | ||
236 | |||
237 | spin_unlock_irqrestore(&camif->slock, flags); | ||
238 | |||
239 | return camif_reinitialize(vp); | ||
240 | } | ||
241 | |||
242 | static int camif_prepare_addr(struct camif_vp *vp, struct vb2_buffer *vb, | ||
243 | struct camif_addr *paddr) | ||
244 | { | ||
245 | struct camif_frame *frame = &vp->out_frame; | ||
246 | u32 pix_size; | ||
247 | |||
248 | if (vb == NULL || frame == NULL) | ||
249 | return -EINVAL; | ||
250 | |||
251 | pix_size = frame->rect.width * frame->rect.height; | ||
252 | |||
253 | pr_debug("colplanes: %d, pix_size: %u\n", | ||
254 | vp->out_fmt->colplanes, pix_size); | ||
255 | |||
256 | paddr->y = vb2_dma_contig_plane_dma_addr(vb, 0); | ||
257 | |||
258 | switch (vp->out_fmt->colplanes) { | ||
259 | case 1: | ||
260 | paddr->cb = 0; | ||
261 | paddr->cr = 0; | ||
262 | break; | ||
263 | case 2: | ||
264 | /* decompose Y into Y/Cb */ | ||
265 | paddr->cb = (u32)(paddr->y + pix_size); | ||
266 | paddr->cr = 0; | ||
267 | break; | ||
268 | case 3: | ||
269 | paddr->cb = (u32)(paddr->y + pix_size); | ||
270 | /* decompose Y into Y/Cb/Cr */ | ||
271 | if (vp->out_fmt->color == IMG_FMT_YCBCR422P) | ||
272 | paddr->cr = (u32)(paddr->cb + (pix_size >> 1)); | ||
273 | else /* 420 */ | ||
274 | paddr->cr = (u32)(paddr->cb + (pix_size >> 2)); | ||
275 | |||
276 | if (vp->out_fmt->color == IMG_FMT_YCRCB420) | ||
277 | swap(paddr->cb, paddr->cr); | ||
278 | break; | ||
279 | default: | ||
280 | return -EINVAL; | ||
281 | } | ||
282 | |||
283 | pr_debug("DMA address: y: %#x cb: %#x cr: %#x\n", | ||
284 | paddr->y, paddr->cb, paddr->cr); | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | irqreturn_t s3c_camif_irq_handler(int irq, void *priv) | ||
290 | { | ||
291 | struct camif_vp *vp = priv; | ||
292 | struct camif_dev *camif = vp->camif; | ||
293 | unsigned int ip_rev = camif->variant->ip_revision; | ||
294 | unsigned int status; | ||
295 | |||
296 | spin_lock(&camif->slock); | ||
297 | |||
298 | if (ip_rev == S3C6410_CAMIF_IP_REV) | ||
299 | camif_hw_clear_pending_irq(vp); | ||
300 | |||
301 | status = camif_hw_get_status(vp); | ||
302 | |||
303 | if (ip_rev == S3C244X_CAMIF_IP_REV && (status & CISTATUS_OVF_MASK)) { | ||
304 | camif_hw_clear_fifo_overflow(vp); | ||
305 | goto unlock; | ||
306 | } | ||
307 | |||
308 | if (vp->state & ST_VP_ABORTING) { | ||
309 | if (vp->state & ST_VP_OFF) { | ||
310 | /* Last IRQ */ | ||
311 | vp->state &= ~(ST_VP_OFF | ST_VP_ABORTING | | ||
312 | ST_VP_LASTIRQ); | ||
313 | wake_up(&vp->irq_queue); | ||
314 | goto unlock; | ||
315 | } else if (vp->state & ST_VP_LASTIRQ) { | ||
316 | camif_hw_disable_capture(vp); | ||
317 | camif_hw_enable_scaler(vp, false); | ||
318 | camif_hw_set_lastirq(vp, false); | ||
319 | vp->state |= ST_VP_OFF; | ||
320 | } else { | ||
321 | /* Disable capture, enable last IRQ */ | ||
322 | camif_hw_set_lastirq(vp, true); | ||
323 | vp->state |= ST_VP_LASTIRQ; | ||
324 | } | ||
325 | } | ||
326 | |||
327 | if (!list_empty(&vp->pending_buf_q) && (vp->state & ST_VP_RUNNING) && | ||
328 | !list_empty(&vp->active_buf_q)) { | ||
329 | unsigned int index; | ||
330 | struct camif_buffer *vbuf; | ||
331 | struct timeval *tv; | ||
332 | struct timespec ts; | ||
333 | /* | ||
334 | * Get previous DMA write buffer index: | ||
335 | * 0 => DMA buffer 0, 2; | ||
336 | * 1 => DMA buffer 1, 3. | ||
337 | */ | ||
338 | index = (CISTATUS_FRAMECNT(status) + 2) & 1; | ||
339 | |||
340 | ktime_get_ts(&ts); | ||
341 | vbuf = camif_active_queue_peek(vp, index); | ||
342 | |||
343 | if (!WARN_ON(vbuf == NULL)) { | ||
344 | /* Dequeue a filled buffer */ | ||
345 | tv = &vbuf->vb.v4l2_buf.timestamp; | ||
346 | tv->tv_sec = ts.tv_sec; | ||
347 | tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; | ||
348 | vbuf->vb.v4l2_buf.sequence = vp->frame_sequence++; | ||
349 | vb2_buffer_done(&vbuf->vb, VB2_BUF_STATE_DONE); | ||
350 | |||
351 | /* Set up an empty buffer at the DMA engine */ | ||
352 | vbuf = camif_pending_queue_pop(vp); | ||
353 | vbuf->index = index; | ||
354 | camif_hw_set_output_addr(vp, &vbuf->paddr, index); | ||
355 | camif_hw_set_output_addr(vp, &vbuf->paddr, index + 2); | ||
356 | |||
357 | /* Scheduled in H/W, add to the queue */ | ||
358 | camif_active_queue_add(vp, vbuf); | ||
359 | } | ||
360 | } else if (!(vp->state & ST_VP_ABORTING) && | ||
361 | (vp->state & ST_VP_PENDING)) { | ||
362 | vp->state |= ST_VP_RUNNING; | ||
363 | } | ||
364 | |||
365 | if (vp->state & ST_VP_CONFIG) { | ||
366 | camif_prepare_dma_offset(vp); | ||
367 | camif_hw_set_camera_crop(camif); | ||
368 | camif_hw_set_scaler(vp); | ||
369 | camif_hw_set_flip(vp); | ||
370 | camif_hw_set_test_pattern(camif, camif->test_pattern); | ||
371 | if (camif->variant->has_img_effect) | ||
372 | camif_hw_set_effect(camif, camif->colorfx, | ||
373 | camif->colorfx_cb, camif->colorfx_cr); | ||
374 | vp->state &= ~ST_VP_CONFIG; | ||
375 | } | ||
376 | unlock: | ||
377 | spin_unlock(&camif->slock); | ||
378 | return IRQ_HANDLED; | ||
379 | } | ||
380 | |||
381 | static int start_streaming(struct vb2_queue *vq, unsigned int count) | ||
382 | { | ||
383 | struct camif_vp *vp = vb2_get_drv_priv(vq); | ||
384 | struct camif_dev *camif = vp->camif; | ||
385 | unsigned long flags; | ||
386 | int ret; | ||
387 | |||
388 | /* | ||
389 | * We assume the codec capture path is always activated | ||
390 | * first, before the preview path starts streaming. | ||
391 | * This is required to avoid internal FIFO overflow and | ||
392 | * a need for CAMIF software reset. | ||
393 | */ | ||
394 | spin_lock_irqsave(&camif->slock, flags); | ||
395 | |||
396 | if (camif->stream_count == 0) { | ||
397 | camif_hw_reset(camif); | ||
398 | ret = s3c_camif_hw_init(camif, vp); | ||
399 | } else { | ||
400 | ret = s3c_camif_hw_vp_init(camif, vp); | ||
401 | } | ||
402 | spin_unlock_irqrestore(&camif->slock, flags); | ||
403 | |||
404 | if (ret < 0) { | ||
405 | camif_reinitialize(vp); | ||
406 | return ret; | ||
407 | } | ||
408 | |||
409 | spin_lock_irqsave(&camif->slock, flags); | ||
410 | vp->frame_sequence = 0; | ||
411 | vp->state |= ST_VP_PENDING; | ||
412 | |||
413 | if (!list_empty(&vp->pending_buf_q) && | ||
414 | (!(vp->state & ST_VP_STREAMING) || | ||
415 | !(vp->state & ST_VP_SENSOR_STREAMING))) { | ||
416 | |||
417 | camif_hw_enable_scaler(vp, vp->scaler.enable); | ||
418 | camif_hw_enable_capture(vp); | ||
419 | vp->state |= ST_VP_STREAMING; | ||
420 | |||
421 | if (!(vp->state & ST_VP_SENSOR_STREAMING)) { | ||
422 | vp->state |= ST_VP_SENSOR_STREAMING; | ||
423 | spin_unlock_irqrestore(&camif->slock, flags); | ||
424 | ret = sensor_set_streaming(camif, 1); | ||
425 | if (ret) | ||
426 | v4l2_err(&vp->vdev, "Sensor s_stream failed\n"); | ||
427 | if (debug) | ||
428 | camif_hw_dump_regs(camif, __func__); | ||
429 | |||
430 | return ret; | ||
431 | } | ||
432 | } | ||
433 | |||
434 | spin_unlock_irqrestore(&camif->slock, flags); | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | static int stop_streaming(struct vb2_queue *vq) | ||
439 | { | ||
440 | struct camif_vp *vp = vb2_get_drv_priv(vq); | ||
441 | return camif_stop_capture(vp); | ||
442 | } | ||
443 | |||
444 | static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, | ||
445 | unsigned int *num_buffers, unsigned int *num_planes, | ||
446 | unsigned int sizes[], void *allocators[]) | ||
447 | { | ||
448 | const struct v4l2_pix_format *pix = NULL; | ||
449 | struct camif_vp *vp = vb2_get_drv_priv(vq); | ||
450 | struct camif_dev *camif = vp->camif; | ||
451 | struct camif_frame *frame = &vp->out_frame; | ||
452 | const struct camif_fmt *fmt = vp->out_fmt; | ||
453 | unsigned int size; | ||
454 | |||
455 | if (pfmt) { | ||
456 | pix = &pfmt->fmt.pix; | ||
457 | fmt = s3c_camif_find_format(vp, &pix->pixelformat, -1); | ||
458 | size = (pix->width * pix->height * fmt->depth) / 8; | ||
459 | } else { | ||
460 | size = (frame->f_width * frame->f_height * fmt->depth) / 8; | ||
461 | } | ||
462 | |||
463 | if (fmt == NULL) | ||
464 | return -EINVAL; | ||
465 | *num_planes = 1; | ||
466 | |||
467 | if (pix) | ||
468 | sizes[0] = max(size, pix->sizeimage); | ||
469 | else | ||
470 | sizes[0] = size; | ||
471 | allocators[0] = camif->alloc_ctx; | ||
472 | |||
473 | pr_debug("size: %u\n", sizes[0]); | ||
474 | return 0; | ||
475 | } | ||
476 | |||
477 | static int buffer_prepare(struct vb2_buffer *vb) | ||
478 | { | ||
479 | struct camif_vp *vp = vb2_get_drv_priv(vb->vb2_queue); | ||
480 | |||
481 | if (vp->out_fmt == NULL) | ||
482 | return -EINVAL; | ||
483 | |||
484 | if (vb2_plane_size(vb, 0) < vp->payload) { | ||
485 | v4l2_err(&vp->vdev, "buffer too small: %lu, required: %u\n", | ||
486 | vb2_plane_size(vb, 0), vp->payload); | ||
487 | return -EINVAL; | ||
488 | } | ||
489 | vb2_set_plane_payload(vb, 0, vp->payload); | ||
490 | |||
491 | return 0; | ||
492 | } | ||
493 | |||
494 | static void buffer_queue(struct vb2_buffer *vb) | ||
495 | { | ||
496 | struct camif_buffer *buf = container_of(vb, struct camif_buffer, vb); | ||
497 | struct camif_vp *vp = vb2_get_drv_priv(vb->vb2_queue); | ||
498 | struct camif_dev *camif = vp->camif; | ||
499 | unsigned long flags; | ||
500 | |||
501 | spin_lock_irqsave(&camif->slock, flags); | ||
502 | WARN_ON(camif_prepare_addr(vp, &buf->vb, &buf->paddr)); | ||
503 | |||
504 | if (!(vp->state & ST_VP_STREAMING) && vp->active_buffers < 2) { | ||
505 | /* Schedule an empty buffer in H/W */ | ||
506 | buf->index = vp->buf_index; | ||
507 | |||
508 | camif_hw_set_output_addr(vp, &buf->paddr, buf->index); | ||
509 | camif_hw_set_output_addr(vp, &buf->paddr, buf->index + 2); | ||
510 | |||
511 | camif_active_queue_add(vp, buf); | ||
512 | vp->buf_index = !vp->buf_index; | ||
513 | } else { | ||
514 | camif_pending_queue_add(vp, buf); | ||
515 | } | ||
516 | |||
517 | if (vb2_is_streaming(&vp->vb_queue) && !list_empty(&vp->pending_buf_q) | ||
518 | && !(vp->state & ST_VP_STREAMING)) { | ||
519 | |||
520 | vp->state |= ST_VP_STREAMING; | ||
521 | camif_hw_enable_scaler(vp, vp->scaler.enable); | ||
522 | camif_hw_enable_capture(vp); | ||
523 | spin_unlock_irqrestore(&camif->slock, flags); | ||
524 | |||
525 | if (!(vp->state & ST_VP_SENSOR_STREAMING)) { | ||
526 | if (sensor_set_streaming(camif, 1) == 0) | ||
527 | vp->state |= ST_VP_SENSOR_STREAMING; | ||
528 | else | ||
529 | v4l2_err(&vp->vdev, "Sensor s_stream failed\n"); | ||
530 | |||
531 | if (debug) | ||
532 | camif_hw_dump_regs(camif, __func__); | ||
533 | } | ||
534 | return; | ||
535 | } | ||
536 | spin_unlock_irqrestore(&camif->slock, flags); | ||
537 | } | ||
538 | |||
539 | static void camif_lock(struct vb2_queue *vq) | ||
540 | { | ||
541 | struct camif_vp *vp = vb2_get_drv_priv(vq); | ||
542 | mutex_lock(&vp->camif->lock); | ||
543 | } | ||
544 | |||
545 | static void camif_unlock(struct vb2_queue *vq) | ||
546 | { | ||
547 | struct camif_vp *vp = vb2_get_drv_priv(vq); | ||
548 | mutex_unlock(&vp->camif->lock); | ||
549 | } | ||
550 | |||
551 | static const struct vb2_ops s3c_camif_qops = { | ||
552 | .queue_setup = queue_setup, | ||
553 | .buf_prepare = buffer_prepare, | ||
554 | .buf_queue = buffer_queue, | ||
555 | .wait_prepare = camif_unlock, | ||
556 | .wait_finish = camif_lock, | ||
557 | .start_streaming = start_streaming, | ||
558 | .stop_streaming = stop_streaming, | ||
559 | }; | ||
560 | |||
561 | static int s3c_camif_open(struct file *file) | ||
562 | { | ||
563 | struct camif_vp *vp = video_drvdata(file); | ||
564 | struct camif_dev *camif = vp->camif; | ||
565 | int ret; | ||
566 | |||
567 | pr_debug("[vp%d] state: %#x, owner: %p, pid: %d\n", vp->id, | ||
568 | vp->state, vp->owner, task_pid_nr(current)); | ||
569 | |||
570 | if (mutex_lock_interruptible(&camif->lock)) | ||
571 | return -ERESTARTSYS; | ||
572 | |||
573 | ret = v4l2_fh_open(file); | ||
574 | if (ret < 0) | ||
575 | goto unlock; | ||
576 | |||
577 | ret = pm_runtime_get_sync(camif->dev); | ||
578 | if (ret < 0) | ||
579 | goto err_pm; | ||
580 | |||
581 | ret = sensor_set_power(camif, 1); | ||
582 | if (!ret) | ||
583 | goto unlock; | ||
584 | |||
585 | pm_runtime_put(camif->dev); | ||
586 | err_pm: | ||
587 | v4l2_fh_release(file); | ||
588 | unlock: | ||
589 | mutex_unlock(&camif->lock); | ||
590 | return ret; | ||
591 | } | ||
592 | |||
593 | static int s3c_camif_close(struct file *file) | ||
594 | { | ||
595 | struct camif_vp *vp = video_drvdata(file); | ||
596 | struct camif_dev *camif = vp->camif; | ||
597 | int ret; | ||
598 | |||
599 | pr_debug("[vp%d] state: %#x, owner: %p, pid: %d\n", vp->id, | ||
600 | vp->state, vp->owner, task_pid_nr(current)); | ||
601 | |||
602 | mutex_lock(&camif->lock); | ||
603 | |||
604 | if (vp->owner == file->private_data) { | ||
605 | camif_stop_capture(vp); | ||
606 | vb2_queue_release(&vp->vb_queue); | ||
607 | vp->owner = NULL; | ||
608 | } | ||
609 | |||
610 | sensor_set_power(camif, 0); | ||
611 | |||
612 | pm_runtime_put(camif->dev); | ||
613 | ret = v4l2_fh_release(file); | ||
614 | |||
615 | mutex_unlock(&camif->lock); | ||
616 | return ret; | ||
617 | } | ||
618 | |||
619 | static unsigned int s3c_camif_poll(struct file *file, | ||
620 | struct poll_table_struct *wait) | ||
621 | { | ||
622 | struct camif_vp *vp = video_drvdata(file); | ||
623 | struct camif_dev *camif = vp->camif; | ||
624 | int ret; | ||
625 | |||
626 | mutex_lock(&camif->lock); | ||
627 | if (vp->owner && vp->owner != file->private_data) | ||
628 | ret = -EBUSY; | ||
629 | else | ||
630 | ret = vb2_poll(&vp->vb_queue, file, wait); | ||
631 | |||
632 | mutex_unlock(&camif->lock); | ||
633 | return ret; | ||
634 | } | ||
635 | |||
636 | static int s3c_camif_mmap(struct file *file, struct vm_area_struct *vma) | ||
637 | { | ||
638 | struct camif_vp *vp = video_drvdata(file); | ||
639 | int ret; | ||
640 | |||
641 | if (vp->owner && vp->owner != file->private_data) | ||
642 | ret = -EBUSY; | ||
643 | else | ||
644 | ret = vb2_mmap(&vp->vb_queue, vma); | ||
645 | |||
646 | return ret; | ||
647 | } | ||
648 | |||
649 | static const struct v4l2_file_operations s3c_camif_fops = { | ||
650 | .owner = THIS_MODULE, | ||
651 | .open = s3c_camif_open, | ||
652 | .release = s3c_camif_close, | ||
653 | .poll = s3c_camif_poll, | ||
654 | .unlocked_ioctl = video_ioctl2, | ||
655 | .mmap = s3c_camif_mmap, | ||
656 | }; | ||
657 | |||
658 | /* | ||
659 | * Video node IOCTLs | ||
660 | */ | ||
661 | |||
662 | static int s3c_camif_vidioc_querycap(struct file *file, void *priv, | ||
663 | struct v4l2_capability *cap) | ||
664 | { | ||
665 | struct camif_vp *vp = video_drvdata(file); | ||
666 | |||
667 | strlcpy(cap->driver, S3C_CAMIF_DRIVER_NAME, sizeof(cap->driver)); | ||
668 | strlcpy(cap->card, S3C_CAMIF_DRIVER_NAME, sizeof(cap->card)); | ||
669 | snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s.%d", | ||
670 | dev_name(vp->camif->dev), vp->id); | ||
671 | |||
672 | cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; | ||
673 | cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; | ||
674 | |||
675 | return 0; | ||
676 | } | ||
677 | |||
678 | static int s3c_camif_vidioc_enum_input(struct file *file, void *priv, | ||
679 | struct v4l2_input *input) | ||
680 | { | ||
681 | struct camif_vp *vp = video_drvdata(file); | ||
682 | struct v4l2_subdev *sensor = vp->camif->sensor.sd; | ||
683 | |||
684 | if (input->index || sensor == NULL) | ||
685 | return -EINVAL; | ||
686 | |||
687 | input->type = V4L2_INPUT_TYPE_CAMERA; | ||
688 | strlcpy(input->name, sensor->name, sizeof(input->name)); | ||
689 | return 0; | ||
690 | } | ||
691 | |||
692 | static int s3c_camif_vidioc_s_input(struct file *file, void *priv, | ||
693 | unsigned int i) | ||
694 | { | ||
695 | return i == 0 ? 0 : -EINVAL; | ||
696 | } | ||
697 | |||
698 | static int s3c_camif_vidioc_g_input(struct file *file, void *priv, | ||
699 | unsigned int *i) | ||
700 | { | ||
701 | *i = 0; | ||
702 | return 0; | ||
703 | } | ||
704 | |||
705 | static int s3c_camif_vidioc_enum_fmt(struct file *file, void *priv, | ||
706 | struct v4l2_fmtdesc *f) | ||
707 | { | ||
708 | struct camif_vp *vp = video_drvdata(file); | ||
709 | const struct camif_fmt *fmt; | ||
710 | |||
711 | fmt = s3c_camif_find_format(vp, NULL, f->index); | ||
712 | if (!fmt) | ||
713 | return -EINVAL; | ||
714 | |||
715 | strlcpy(f->description, fmt->name, sizeof(f->description)); | ||
716 | f->pixelformat = fmt->fourcc; | ||
717 | |||
718 | pr_debug("fmt(%d): %s\n", f->index, f->description); | ||
719 | return 0; | ||
720 | } | ||
721 | |||
722 | static int s3c_camif_vidioc_g_fmt(struct file *file, void *priv, | ||
723 | struct v4l2_format *f) | ||
724 | { | ||
725 | struct camif_vp *vp = video_drvdata(file); | ||
726 | struct v4l2_pix_format *pix = &f->fmt.pix; | ||
727 | struct camif_frame *frame = &vp->out_frame; | ||
728 | const struct camif_fmt *fmt = vp->out_fmt; | ||
729 | |||
730 | pix->bytesperline = frame->f_width * fmt->ybpp; | ||
731 | pix->sizeimage = vp->payload; | ||
732 | |||
733 | pix->pixelformat = fmt->fourcc; | ||
734 | pix->width = frame->f_width; | ||
735 | pix->height = frame->f_height; | ||
736 | pix->field = V4L2_FIELD_NONE; | ||
737 | pix->colorspace = V4L2_COLORSPACE_JPEG; | ||
738 | |||
739 | return 0; | ||
740 | } | ||
741 | |||
742 | static int __camif_video_try_format(struct camif_vp *vp, | ||
743 | struct v4l2_pix_format *pix, | ||
744 | const struct camif_fmt **ffmt) | ||
745 | { | ||
746 | struct camif_dev *camif = vp->camif; | ||
747 | struct v4l2_rect *crop = &camif->camif_crop; | ||
748 | unsigned int wmin, hmin, sc_hrmax, sc_vrmax; | ||
749 | const struct vp_pix_limits *pix_lim; | ||
750 | const struct camif_fmt *fmt; | ||
751 | |||
752 | fmt = s3c_camif_find_format(vp, &pix->pixelformat, 0); | ||
753 | |||
754 | if (WARN_ON(fmt == NULL)) | ||
755 | return -EINVAL; | ||
756 | |||
757 | if (ffmt) | ||
758 | *ffmt = fmt; | ||
759 | |||
760 | pix_lim = &camif->variant->vp_pix_limits[vp->id]; | ||
761 | |||
762 | pr_debug("fmt: %ux%u, crop: %ux%u, bytesperline: %u\n", | ||
763 | pix->width, pix->height, crop->width, crop->height, | ||
764 | pix->bytesperline); | ||
765 | /* | ||
766 | * Calculate minimum width and height according to the configured | ||
767 | * camera input interface crop rectangle and the resizer's capabilities. | ||
768 | */ | ||
769 | sc_hrmax = min(SCALER_MAX_RATIO, 1 << (ffs(crop->width) - 3)); | ||
770 | sc_vrmax = min(SCALER_MAX_RATIO, 1 << (ffs(crop->height) - 1)); | ||
771 | |||
772 | wmin = max_t(u32, pix_lim->min_out_width, crop->width / sc_hrmax); | ||
773 | wmin = round_up(wmin, pix_lim->out_width_align); | ||
774 | hmin = max_t(u32, 8, crop->height / sc_vrmax); | ||
775 | hmin = round_up(hmin, 8); | ||
776 | |||
777 | v4l_bound_align_image(&pix->width, wmin, pix_lim->max_sc_out_width, | ||
778 | ffs(pix_lim->out_width_align) - 1, | ||
779 | &pix->height, hmin, pix_lim->max_height, 0, 0); | ||
780 | |||
781 | pix->bytesperline = pix->width * fmt->ybpp; | ||
782 | pix->sizeimage = (pix->width * pix->height * fmt->depth) / 8; | ||
783 | pix->pixelformat = fmt->fourcc; | ||
784 | pix->colorspace = V4L2_COLORSPACE_JPEG; | ||
785 | pix->field = V4L2_FIELD_NONE; | ||
786 | |||
787 | pr_debug("%ux%u, wmin: %d, hmin: %d, sc_hrmax: %d, sc_vrmax: %d\n", | ||
788 | pix->width, pix->height, wmin, hmin, sc_hrmax, sc_vrmax); | ||
789 | |||
790 | return 0; | ||
791 | } | ||
792 | |||
793 | static int s3c_camif_vidioc_try_fmt(struct file *file, void *priv, | ||
794 | struct v4l2_format *f) | ||
795 | { | ||
796 | struct camif_vp *vp = video_drvdata(file); | ||
797 | return __camif_video_try_format(vp, &f->fmt.pix, NULL); | ||
798 | } | ||
799 | |||
800 | static int s3c_camif_vidioc_s_fmt(struct file *file, void *priv, | ||
801 | struct v4l2_format *f) | ||
802 | { | ||
803 | struct v4l2_pix_format *pix = &f->fmt.pix; | ||
804 | struct camif_vp *vp = video_drvdata(file); | ||
805 | struct camif_frame *out_frame = &vp->out_frame; | ||
806 | const struct camif_fmt *fmt = NULL; | ||
807 | int ret; | ||
808 | |||
809 | pr_debug("[vp%d]\n", vp->id); | ||
810 | |||
811 | if (vb2_is_busy(&vp->vb_queue)) | ||
812 | return -EBUSY; | ||
813 | |||
814 | ret = __camif_video_try_format(vp, &f->fmt.pix, &fmt); | ||
815 | if (ret < 0) | ||
816 | return ret; | ||
817 | |||
818 | vp->out_fmt = fmt; | ||
819 | vp->payload = pix->sizeimage; | ||
820 | out_frame->f_width = pix->width; | ||
821 | out_frame->f_height = pix->height; | ||
822 | |||
823 | /* Reset composition rectangle */ | ||
824 | out_frame->rect.width = pix->width; | ||
825 | out_frame->rect.height = pix->height; | ||
826 | out_frame->rect.left = 0; | ||
827 | out_frame->rect.top = 0; | ||
828 | |||
829 | if (vp->owner == NULL) | ||
830 | vp->owner = priv; | ||
831 | |||
832 | pr_debug("%ux%u. payload: %u. fmt: %s. %d %d. sizeimage: %d. bpl: %d\n", | ||
833 | out_frame->f_width, out_frame->f_height, vp->payload, fmt->name, | ||
834 | pix->width * pix->height * fmt->depth, fmt->depth, | ||
835 | pix->sizeimage, pix->bytesperline); | ||
836 | |||
837 | return 0; | ||
838 | } | ||
839 | |||
840 | /* Only check pixel formats at the sensor and the camif subdev pads */ | ||
841 | static int camif_pipeline_validate(struct camif_dev *camif) | ||
842 | { | ||
843 | struct v4l2_subdev_format src_fmt; | ||
844 | struct media_pad *pad; | ||
845 | int ret; | ||
846 | |||
847 | /* Retrieve format at the sensor subdev source pad */ | ||
848 | pad = media_entity_remote_source(&camif->pads[0]); | ||
849 | if (!pad || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
850 | return -EPIPE; | ||
851 | |||
852 | src_fmt.pad = pad->index; | ||
853 | src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
854 | ret = v4l2_subdev_call(camif->sensor.sd, pad, get_fmt, NULL, &src_fmt); | ||
855 | if (ret < 0 && ret != -ENOIOCTLCMD) | ||
856 | return -EPIPE; | ||
857 | |||
858 | if (src_fmt.format.width != camif->mbus_fmt.width || | ||
859 | src_fmt.format.height != camif->mbus_fmt.height || | ||
860 | src_fmt.format.code != camif->mbus_fmt.code) | ||
861 | return -EPIPE; | ||
862 | |||
863 | return 0; | ||
864 | } | ||
865 | |||
866 | static int s3c_camif_streamon(struct file *file, void *priv, | ||
867 | enum v4l2_buf_type type) | ||
868 | { | ||
869 | struct camif_vp *vp = video_drvdata(file); | ||
870 | struct camif_dev *camif = vp->camif; | ||
871 | struct media_entity *sensor = &camif->sensor.sd->entity; | ||
872 | int ret; | ||
873 | |||
874 | pr_debug("[vp%d]\n", vp->id); | ||
875 | |||
876 | if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
877 | return -EINVAL; | ||
878 | |||
879 | if (vp->owner && vp->owner != priv) | ||
880 | return -EBUSY; | ||
881 | |||
882 | if (s3c_vp_active(vp)) | ||
883 | return 0; | ||
884 | |||
885 | ret = media_entity_pipeline_start(sensor, camif->m_pipeline); | ||
886 | if (ret < 0) | ||
887 | return ret; | ||
888 | |||
889 | ret = camif_pipeline_validate(camif); | ||
890 | if (ret < 0) { | ||
891 | media_entity_pipeline_stop(sensor); | ||
892 | return ret; | ||
893 | } | ||
894 | |||
895 | return vb2_streamon(&vp->vb_queue, type); | ||
896 | } | ||
897 | |||
898 | static int s3c_camif_streamoff(struct file *file, void *priv, | ||
899 | enum v4l2_buf_type type) | ||
900 | { | ||
901 | struct camif_vp *vp = video_drvdata(file); | ||
902 | struct camif_dev *camif = vp->camif; | ||
903 | int ret; | ||
904 | |||
905 | pr_debug("[vp%d]\n", vp->id); | ||
906 | |||
907 | if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
908 | return -EINVAL; | ||
909 | |||
910 | if (vp->owner && vp->owner != priv) | ||
911 | return -EBUSY; | ||
912 | |||
913 | ret = vb2_streamoff(&vp->vb_queue, type); | ||
914 | if (ret == 0) | ||
915 | media_entity_pipeline_stop(&camif->sensor.sd->entity); | ||
916 | return ret; | ||
917 | } | ||
918 | |||
919 | static int s3c_camif_reqbufs(struct file *file, void *priv, | ||
920 | struct v4l2_requestbuffers *rb) | ||
921 | { | ||
922 | struct camif_vp *vp = video_drvdata(file); | ||
923 | int ret; | ||
924 | |||
925 | pr_debug("[vp%d] rb count: %d, owner: %p, priv: %p\n", | ||
926 | vp->id, rb->count, vp->owner, priv); | ||
927 | |||
928 | if (vp->owner && vp->owner != priv) | ||
929 | return -EBUSY; | ||
930 | |||
931 | if (rb->count) | ||
932 | rb->count = max_t(u32, CAMIF_REQ_BUFS_MIN, rb->count); | ||
933 | else | ||
934 | vp->owner = NULL; | ||
935 | |||
936 | ret = vb2_reqbufs(&vp->vb_queue, rb); | ||
937 | if (!ret) { | ||
938 | vp->reqbufs_count = rb->count; | ||
939 | if (vp->owner == NULL && rb->count > 0) | ||
940 | vp->owner = priv; | ||
941 | } | ||
942 | |||
943 | return ret; | ||
944 | } | ||
945 | |||
946 | static int s3c_camif_querybuf(struct file *file, void *priv, | ||
947 | struct v4l2_buffer *buf) | ||
948 | { | ||
949 | struct camif_vp *vp = video_drvdata(file); | ||
950 | return vb2_querybuf(&vp->vb_queue, buf); | ||
951 | } | ||
952 | |||
953 | static int s3c_camif_qbuf(struct file *file, void *priv, | ||
954 | struct v4l2_buffer *buf) | ||
955 | { | ||
956 | struct camif_vp *vp = video_drvdata(file); | ||
957 | |||
958 | pr_debug("[vp%d]\n", vp->id); | ||
959 | |||
960 | if (vp->owner && vp->owner != priv) | ||
961 | return -EBUSY; | ||
962 | |||
963 | return vb2_qbuf(&vp->vb_queue, buf); | ||
964 | } | ||
965 | |||
966 | static int s3c_camif_dqbuf(struct file *file, void *priv, | ||
967 | struct v4l2_buffer *buf) | ||
968 | { | ||
969 | struct camif_vp *vp = video_drvdata(file); | ||
970 | |||
971 | pr_debug("[vp%d] sequence: %d\n", vp->id, vp->frame_sequence); | ||
972 | |||
973 | if (vp->owner && vp->owner != priv) | ||
974 | return -EBUSY; | ||
975 | |||
976 | return vb2_dqbuf(&vp->vb_queue, buf, file->f_flags & O_NONBLOCK); | ||
977 | } | ||
978 | |||
979 | static int s3c_camif_create_bufs(struct file *file, void *priv, | ||
980 | struct v4l2_create_buffers *create) | ||
981 | { | ||
982 | struct camif_vp *vp = video_drvdata(file); | ||
983 | int ret; | ||
984 | |||
985 | if (vp->owner && vp->owner != priv) | ||
986 | return -EBUSY; | ||
987 | |||
988 | create->count = max_t(u32, 1, create->count); | ||
989 | ret = vb2_create_bufs(&vp->vb_queue, create); | ||
990 | |||
991 | if (!ret && vp->owner == NULL) | ||
992 | vp->owner = priv; | ||
993 | |||
994 | return ret; | ||
995 | } | ||
996 | |||
997 | static int s3c_camif_prepare_buf(struct file *file, void *priv, | ||
998 | struct v4l2_buffer *b) | ||
999 | { | ||
1000 | struct camif_vp *vp = video_drvdata(file); | ||
1001 | return vb2_prepare_buf(&vp->vb_queue, b); | ||
1002 | } | ||
1003 | |||
1004 | static int s3c_camif_g_selection(struct file *file, void *priv, | ||
1005 | struct v4l2_selection *sel) | ||
1006 | { | ||
1007 | struct camif_vp *vp = video_drvdata(file); | ||
1008 | |||
1009 | if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1010 | return -EINVAL; | ||
1011 | |||
1012 | switch (sel->target) { | ||
1013 | case V4L2_SEL_TGT_COMPOSE_BOUNDS: | ||
1014 | case V4L2_SEL_TGT_COMPOSE_DEFAULT: | ||
1015 | sel->r.left = 0; | ||
1016 | sel->r.top = 0; | ||
1017 | sel->r.width = vp->out_frame.f_width; | ||
1018 | sel->r.height = vp->out_frame.f_height; | ||
1019 | return 0; | ||
1020 | |||
1021 | case V4L2_SEL_TGT_COMPOSE: | ||
1022 | sel->r = vp->out_frame.rect; | ||
1023 | return 0; | ||
1024 | } | ||
1025 | |||
1026 | return -EINVAL; | ||
1027 | } | ||
1028 | |||
1029 | static void __camif_try_compose(struct camif_dev *camif, struct camif_vp *vp, | ||
1030 | struct v4l2_rect *r) | ||
1031 | { | ||
1032 | /* s3c244x doesn't support composition */ | ||
1033 | if (camif->variant->ip_revision == S3C244X_CAMIF_IP_REV) { | ||
1034 | *r = vp->out_frame.rect; | ||
1035 | return; | ||
1036 | } | ||
1037 | |||
1038 | /* TODO: s3c64xx */ | ||
1039 | } | ||
1040 | |||
1041 | static int s3c_camif_s_selection(struct file *file, void *priv, | ||
1042 | struct v4l2_selection *sel) | ||
1043 | { | ||
1044 | struct camif_vp *vp = video_drvdata(file); | ||
1045 | struct camif_dev *camif = vp->camif; | ||
1046 | struct v4l2_rect rect = sel->r; | ||
1047 | unsigned long flags; | ||
1048 | |||
1049 | if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || | ||
1050 | sel->target != V4L2_SEL_TGT_COMPOSE) | ||
1051 | return -EINVAL; | ||
1052 | |||
1053 | __camif_try_compose(camif, vp, &rect); | ||
1054 | |||
1055 | sel->r = rect; | ||
1056 | spin_lock_irqsave(&camif->slock, flags); | ||
1057 | vp->out_frame.rect = rect; | ||
1058 | vp->state |= ST_VP_CONFIG; | ||
1059 | spin_unlock_irqrestore(&camif->slock, flags); | ||
1060 | |||
1061 | pr_debug("type: %#x, target: %#x, flags: %#x, (%d,%d)/%dx%d\n", | ||
1062 | sel->type, sel->target, sel->flags, | ||
1063 | sel->r.left, sel->r.top, sel->r.width, sel->r.height); | ||
1064 | |||
1065 | return 0; | ||
1066 | } | ||
1067 | |||
1068 | static const struct v4l2_ioctl_ops s3c_camif_ioctl_ops = { | ||
1069 | .vidioc_querycap = s3c_camif_vidioc_querycap, | ||
1070 | .vidioc_enum_input = s3c_camif_vidioc_enum_input, | ||
1071 | .vidioc_g_input = s3c_camif_vidioc_g_input, | ||
1072 | .vidioc_s_input = s3c_camif_vidioc_s_input, | ||
1073 | .vidioc_enum_fmt_vid_cap = s3c_camif_vidioc_enum_fmt, | ||
1074 | .vidioc_try_fmt_vid_cap = s3c_camif_vidioc_try_fmt, | ||
1075 | .vidioc_s_fmt_vid_cap = s3c_camif_vidioc_s_fmt, | ||
1076 | .vidioc_g_fmt_vid_cap = s3c_camif_vidioc_g_fmt, | ||
1077 | .vidioc_g_selection = s3c_camif_g_selection, | ||
1078 | .vidioc_s_selection = s3c_camif_s_selection, | ||
1079 | .vidioc_reqbufs = s3c_camif_reqbufs, | ||
1080 | .vidioc_querybuf = s3c_camif_querybuf, | ||
1081 | .vidioc_prepare_buf = s3c_camif_prepare_buf, | ||
1082 | .vidioc_create_bufs = s3c_camif_create_bufs, | ||
1083 | .vidioc_qbuf = s3c_camif_qbuf, | ||
1084 | .vidioc_dqbuf = s3c_camif_dqbuf, | ||
1085 | .vidioc_streamon = s3c_camif_streamon, | ||
1086 | .vidioc_streamoff = s3c_camif_streamoff, | ||
1087 | .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, | ||
1088 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, | ||
1089 | .vidioc_log_status = v4l2_ctrl_log_status, | ||
1090 | }; | ||
1091 | |||
1092 | /* | ||
1093 | * Video node controls | ||
1094 | */ | ||
1095 | static int s3c_camif_video_s_ctrl(struct v4l2_ctrl *ctrl) | ||
1096 | { | ||
1097 | struct camif_vp *vp = ctrl->priv; | ||
1098 | struct camif_dev *camif = vp->camif; | ||
1099 | unsigned long flags; | ||
1100 | |||
1101 | pr_debug("[vp%d] ctrl: %s, value: %d\n", vp->id, | ||
1102 | ctrl->name, ctrl->val); | ||
1103 | |||
1104 | spin_lock_irqsave(&camif->slock, flags); | ||
1105 | |||
1106 | switch (ctrl->id) { | ||
1107 | case V4L2_CID_HFLIP: | ||
1108 | vp->hflip = ctrl->val; | ||
1109 | break; | ||
1110 | |||
1111 | case V4L2_CID_VFLIP: | ||
1112 | vp->vflip = ctrl->val; | ||
1113 | break; | ||
1114 | } | ||
1115 | |||
1116 | vp->state |= ST_VP_CONFIG; | ||
1117 | spin_unlock_irqrestore(&camif->slock, flags); | ||
1118 | return 0; | ||
1119 | } | ||
1120 | |||
1121 | /* Codec and preview video node control ops */ | ||
1122 | static const struct v4l2_ctrl_ops s3c_camif_video_ctrl_ops = { | ||
1123 | .s_ctrl = s3c_camif_video_s_ctrl, | ||
1124 | }; | ||
1125 | |||
1126 | int s3c_camif_register_video_node(struct camif_dev *camif, int idx) | ||
1127 | { | ||
1128 | struct camif_vp *vp = &camif->vp[idx]; | ||
1129 | struct vb2_queue *q = &vp->vb_queue; | ||
1130 | struct video_device *vfd = &vp->vdev; | ||
1131 | struct v4l2_ctrl *ctrl; | ||
1132 | int ret; | ||
1133 | |||
1134 | memset(vfd, 0, sizeof(*vfd)); | ||
1135 | snprintf(vfd->name, sizeof(vfd->name), "camif-%s", | ||
1136 | vp->id == 0 ? "codec" : "preview"); | ||
1137 | |||
1138 | vfd->fops = &s3c_camif_fops; | ||
1139 | vfd->ioctl_ops = &s3c_camif_ioctl_ops; | ||
1140 | vfd->v4l2_dev = &camif->v4l2_dev; | ||
1141 | vfd->minor = -1; | ||
1142 | vfd->release = video_device_release_empty; | ||
1143 | vfd->lock = &camif->lock; | ||
1144 | vp->reqbufs_count = 0; | ||
1145 | |||
1146 | INIT_LIST_HEAD(&vp->pending_buf_q); | ||
1147 | INIT_LIST_HEAD(&vp->active_buf_q); | ||
1148 | |||
1149 | memset(q, 0, sizeof(*q)); | ||
1150 | q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1151 | q->io_modes = VB2_MMAP | VB2_USERPTR; | ||
1152 | q->ops = &s3c_camif_qops; | ||
1153 | q->mem_ops = &vb2_dma_contig_memops; | ||
1154 | q->buf_struct_size = sizeof(struct camif_buffer); | ||
1155 | q->drv_priv = vp; | ||
1156 | |||
1157 | ret = vb2_queue_init(q); | ||
1158 | if (ret) | ||
1159 | goto err_vd_rel; | ||
1160 | |||
1161 | vp->pad.flags = MEDIA_PAD_FL_SINK; | ||
1162 | ret = media_entity_init(&vfd->entity, 1, &vp->pad, 0); | ||
1163 | if (ret) | ||
1164 | goto err_vd_rel; | ||
1165 | |||
1166 | video_set_drvdata(vfd, vp); | ||
1167 | set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); | ||
1168 | |||
1169 | v4l2_ctrl_handler_init(&vp->ctrl_handler, 1); | ||
1170 | ctrl = v4l2_ctrl_new_std(&vp->ctrl_handler, &s3c_camif_video_ctrl_ops, | ||
1171 | V4L2_CID_HFLIP, 0, 1, 1, 0); | ||
1172 | if (ctrl) | ||
1173 | ctrl->priv = vp; | ||
1174 | ctrl = v4l2_ctrl_new_std(&vp->ctrl_handler, &s3c_camif_video_ctrl_ops, | ||
1175 | V4L2_CID_VFLIP, 0, 1, 1, 0); | ||
1176 | if (ctrl) | ||
1177 | ctrl->priv = vp; | ||
1178 | |||
1179 | ret = vp->ctrl_handler.error; | ||
1180 | if (ret < 0) | ||
1181 | goto err_me_cleanup; | ||
1182 | |||
1183 | vfd->ctrl_handler = &vp->ctrl_handler; | ||
1184 | |||
1185 | ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); | ||
1186 | if (ret) | ||
1187 | goto err_ctrlh_free; | ||
1188 | |||
1189 | v4l2_info(&camif->v4l2_dev, "registered %s as /dev/%s\n", | ||
1190 | vfd->name, video_device_node_name(vfd)); | ||
1191 | return 0; | ||
1192 | |||
1193 | err_ctrlh_free: | ||
1194 | v4l2_ctrl_handler_free(&vp->ctrl_handler); | ||
1195 | err_me_cleanup: | ||
1196 | media_entity_cleanup(&vfd->entity); | ||
1197 | err_vd_rel: | ||
1198 | video_device_release(vfd); | ||
1199 | return ret; | ||
1200 | } | ||
1201 | |||
1202 | void s3c_camif_unregister_video_node(struct camif_dev *camif, int idx) | ||
1203 | { | ||
1204 | struct video_device *vfd = &camif->vp[idx].vdev; | ||
1205 | |||
1206 | if (video_is_registered(vfd)) { | ||
1207 | video_unregister_device(vfd); | ||
1208 | media_entity_cleanup(&vfd->entity); | ||
1209 | v4l2_ctrl_handler_free(vfd->ctrl_handler); | ||
1210 | } | ||
1211 | } | ||
1212 | |||
1213 | /* Media bus pixel formats supported at the camif input */ | ||
1214 | static const enum v4l2_mbus_pixelcode camif_mbus_formats[] = { | ||
1215 | V4L2_MBUS_FMT_YUYV8_2X8, | ||
1216 | V4L2_MBUS_FMT_YVYU8_2X8, | ||
1217 | V4L2_MBUS_FMT_UYVY8_2X8, | ||
1218 | V4L2_MBUS_FMT_VYUY8_2X8, | ||
1219 | }; | ||
1220 | |||
1221 | /* | ||
1222 | * Camera input interface subdev operations | ||
1223 | */ | ||
1224 | |||
1225 | static int s3c_camif_subdev_enum_mbus_code(struct v4l2_subdev *sd, | ||
1226 | struct v4l2_subdev_fh *fh, | ||
1227 | struct v4l2_subdev_mbus_code_enum *code) | ||
1228 | { | ||
1229 | if (code->index >= ARRAY_SIZE(camif_mbus_formats)) | ||
1230 | return -EINVAL; | ||
1231 | |||
1232 | code->code = camif_mbus_formats[code->index]; | ||
1233 | return 0; | ||
1234 | } | ||
1235 | |||
1236 | static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd, | ||
1237 | struct v4l2_subdev_fh *fh, | ||
1238 | struct v4l2_subdev_format *fmt) | ||
1239 | { | ||
1240 | struct camif_dev *camif = v4l2_get_subdevdata(sd); | ||
1241 | struct v4l2_mbus_framefmt *mf = &fmt->format; | ||
1242 | |||
1243 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1244 | mf = v4l2_subdev_get_try_format(fh, fmt->pad); | ||
1245 | fmt->format = *mf; | ||
1246 | return 0; | ||
1247 | } | ||
1248 | |||
1249 | mutex_lock(&camif->lock); | ||
1250 | |||
1251 | switch (fmt->pad) { | ||
1252 | case CAMIF_SD_PAD_SINK: | ||
1253 | /* full camera input pixel size */ | ||
1254 | *mf = camif->mbus_fmt; | ||
1255 | break; | ||
1256 | |||
1257 | case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P: | ||
1258 | /* crop rectangle at camera interface input */ | ||
1259 | mf->width = camif->camif_crop.width; | ||
1260 | mf->height = camif->camif_crop.height; | ||
1261 | mf->code = camif->mbus_fmt.code; | ||
1262 | break; | ||
1263 | } | ||
1264 | |||
1265 | mutex_unlock(&camif->lock); | ||
1266 | mf->colorspace = V4L2_COLORSPACE_JPEG; | ||
1267 | return 0; | ||
1268 | } | ||
1269 | |||
1270 | static void __camif_subdev_try_format(struct camif_dev *camif, | ||
1271 | struct v4l2_mbus_framefmt *mf, int pad) | ||
1272 | { | ||
1273 | const struct s3c_camif_variant *variant = camif->variant; | ||
1274 | const struct vp_pix_limits *pix_lim; | ||
1275 | int i = ARRAY_SIZE(camif_mbus_formats); | ||
1276 | |||
1277 | /* FIXME: constraints against codec or preview path ? */ | ||
1278 | pix_lim = &variant->vp_pix_limits[VP_CODEC]; | ||
1279 | |||
1280 | while (i-- >= 0) | ||
1281 | if (camif_mbus_formats[i] == mf->code) | ||
1282 | break; | ||
1283 | |||
1284 | mf->code = camif_mbus_formats[i]; | ||
1285 | |||
1286 | if (pad == CAMIF_SD_PAD_SINK) { | ||
1287 | v4l_bound_align_image(&mf->width, 8, CAMIF_MAX_PIX_WIDTH, | ||
1288 | ffs(pix_lim->out_width_align) - 1, | ||
1289 | &mf->height, 8, CAMIF_MAX_PIX_HEIGHT, 0, | ||
1290 | 0); | ||
1291 | } else { | ||
1292 | struct v4l2_rect *crop = &camif->camif_crop; | ||
1293 | v4l_bound_align_image(&mf->width, 8, crop->width, | ||
1294 | ffs(pix_lim->out_width_align) - 1, | ||
1295 | &mf->height, 8, crop->height, | ||
1296 | 0, 0); | ||
1297 | } | ||
1298 | |||
1299 | v4l2_dbg(1, debug, &camif->subdev, "%ux%u\n", mf->width, mf->height); | ||
1300 | } | ||
1301 | |||
1302 | static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, | ||
1303 | struct v4l2_subdev_fh *fh, | ||
1304 | struct v4l2_subdev_format *fmt) | ||
1305 | { | ||
1306 | struct camif_dev *camif = v4l2_get_subdevdata(sd); | ||
1307 | struct v4l2_mbus_framefmt *mf = &fmt->format; | ||
1308 | struct v4l2_rect *crop = &camif->camif_crop; | ||
1309 | int i; | ||
1310 | |||
1311 | v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %ux%u\n", | ||
1312 | fmt->pad, mf->code, mf->width, mf->height); | ||
1313 | |||
1314 | mf->colorspace = V4L2_COLORSPACE_JPEG; | ||
1315 | mutex_lock(&camif->lock); | ||
1316 | |||
1317 | /* | ||
1318 | * No pixel format change at the camera input is allowed | ||
1319 | * while streaming. | ||
1320 | */ | ||
1321 | if (vb2_is_busy(&camif->vp[VP_CODEC].vb_queue) || | ||
1322 | vb2_is_busy(&camif->vp[VP_PREVIEW].vb_queue)) { | ||
1323 | mutex_unlock(&camif->lock); | ||
1324 | return -EBUSY; | ||
1325 | } | ||
1326 | |||
1327 | __camif_subdev_try_format(camif, mf, fmt->pad); | ||
1328 | |||
1329 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1330 | mf = v4l2_subdev_get_try_format(fh, fmt->pad); | ||
1331 | *mf = fmt->format; | ||
1332 | mutex_unlock(&camif->lock); | ||
1333 | return 0; | ||
1334 | } | ||
1335 | |||
1336 | switch (fmt->pad) { | ||
1337 | case CAMIF_SD_PAD_SINK: | ||
1338 | camif->mbus_fmt = *mf; | ||
1339 | /* Reset sink crop rectangle. */ | ||
1340 | crop->width = mf->width; | ||
1341 | crop->height = mf->height; | ||
1342 | crop->left = 0; | ||
1343 | crop->top = 0; | ||
1344 | /* | ||
1345 | * Reset source format (the camif's crop rectangle) | ||
1346 | * and the video output resolution. | ||
1347 | */ | ||
1348 | for (i = 0; i < CAMIF_VP_NUM; i++) { | ||
1349 | struct camif_frame *frame = &camif->vp[i].out_frame; | ||
1350 | frame->rect = *crop; | ||
1351 | frame->f_width = mf->width; | ||
1352 | frame->f_height = mf->height; | ||
1353 | } | ||
1354 | break; | ||
1355 | |||
1356 | case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P: | ||
1357 | /* Pixel format can be only changed on the sink pad. */ | ||
1358 | mf->code = camif->mbus_fmt.code; | ||
1359 | mf->width = crop->width; | ||
1360 | mf->height = crop->height; | ||
1361 | break; | ||
1362 | } | ||
1363 | |||
1364 | mutex_unlock(&camif->lock); | ||
1365 | return 0; | ||
1366 | } | ||
1367 | |||
1368 | static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd, | ||
1369 | struct v4l2_subdev_fh *fh, | ||
1370 | struct v4l2_subdev_selection *sel) | ||
1371 | { | ||
1372 | struct camif_dev *camif = v4l2_get_subdevdata(sd); | ||
1373 | struct v4l2_rect *crop = &camif->camif_crop; | ||
1374 | struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt; | ||
1375 | |||
1376 | if ((sel->target != V4L2_SEL_TGT_CROP && | ||
1377 | sel->target != V4L2_SEL_TGT_CROP_BOUNDS) || | ||
1378 | sel->pad != CAMIF_SD_PAD_SINK) | ||
1379 | return -EINVAL; | ||
1380 | |||
1381 | if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1382 | sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad); | ||
1383 | return 0; | ||
1384 | } | ||
1385 | |||
1386 | mutex_lock(&camif->lock); | ||
1387 | |||
1388 | if (sel->target == V4L2_SEL_TGT_CROP) { | ||
1389 | sel->r = *crop; | ||
1390 | } else { /* crop bounds */ | ||
1391 | sel->r.width = mf->width; | ||
1392 | sel->r.height = mf->height; | ||
1393 | sel->r.left = 0; | ||
1394 | sel->r.top = 0; | ||
1395 | } | ||
1396 | |||
1397 | mutex_unlock(&camif->lock); | ||
1398 | |||
1399 | v4l2_dbg(1, debug, sd, "%s: crop: (%d,%d) %dx%d, size: %ux%u\n", | ||
1400 | __func__, crop->left, crop->top, crop->width, | ||
1401 | crop->height, mf->width, mf->height); | ||
1402 | |||
1403 | return 0; | ||
1404 | } | ||
1405 | |||
1406 | static void __camif_try_crop(struct camif_dev *camif, struct v4l2_rect *r) | ||
1407 | { | ||
1408 | struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt; | ||
1409 | const struct camif_pix_limits *pix_lim = &camif->variant->pix_limits; | ||
1410 | unsigned int left = 2 * r->left; | ||
1411 | unsigned int top = 2 * r->top; | ||
1412 | |||
1413 | /* | ||
1414 | * Following constraints must be met: | ||
1415 | * - r->width + 2 * r->left = mf->width; | ||
1416 | * - r->height + 2 * r->top = mf->height; | ||
1417 | * - crop rectangle size and position must be aligned | ||
1418 | * to 8 or 2 pixels, depending on SoC version. | ||
1419 | */ | ||
1420 | v4l_bound_align_image(&r->width, 0, mf->width, | ||
1421 | ffs(pix_lim->win_hor_offset_align) - 1, | ||
1422 | &r->height, 0, mf->height, 1, 0); | ||
1423 | |||
1424 | v4l_bound_align_image(&left, 0, mf->width - r->width, | ||
1425 | ffs(pix_lim->win_hor_offset_align), | ||
1426 | &top, 0, mf->height - r->height, 2, 0); | ||
1427 | |||
1428 | r->left = left / 2; | ||
1429 | r->top = top / 2; | ||
1430 | r->width = mf->width - left; | ||
1431 | r->height = mf->height - top; | ||
1432 | /* | ||
1433 | * Make sure we either downscale or upscale both the pixel | ||
1434 | * width and height. Just return current crop rectangle if | ||
1435 | * this scaler constraint is not met. | ||
1436 | */ | ||
1437 | if (camif->variant->ip_revision == S3C244X_CAMIF_IP_REV && | ||
1438 | camif_is_streaming(camif)) { | ||
1439 | unsigned int i; | ||
1440 | |||
1441 | for (i = 0; i < CAMIF_VP_NUM; i++) { | ||
1442 | struct v4l2_rect *or = &camif->vp[i].out_frame.rect; | ||
1443 | if ((or->width > r->width) == (or->height > r->height)) | ||
1444 | continue; | ||
1445 | *r = camif->camif_crop; | ||
1446 | pr_debug("Width/height scaling direction limitation\n"); | ||
1447 | break; | ||
1448 | } | ||
1449 | } | ||
1450 | |||
1451 | v4l2_dbg(1, debug, &camif->v4l2_dev, "crop: (%d,%d)/%dx%d, fmt: %ux%u\n", | ||
1452 | r->left, r->top, r->width, r->height, mf->width, mf->height); | ||
1453 | } | ||
1454 | |||
1455 | static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd, | ||
1456 | struct v4l2_subdev_fh *fh, | ||
1457 | struct v4l2_subdev_selection *sel) | ||
1458 | { | ||
1459 | struct camif_dev *camif = v4l2_get_subdevdata(sd); | ||
1460 | struct v4l2_rect *crop = &camif->camif_crop; | ||
1461 | struct camif_scaler scaler; | ||
1462 | |||
1463 | if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != CAMIF_SD_PAD_SINK) | ||
1464 | return -EINVAL; | ||
1465 | |||
1466 | mutex_lock(&camif->lock); | ||
1467 | __camif_try_crop(camif, &sel->r); | ||
1468 | |||
1469 | if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1470 | *v4l2_subdev_get_try_crop(fh, sel->pad) = sel->r; | ||
1471 | } else { | ||
1472 | unsigned long flags; | ||
1473 | unsigned int i; | ||
1474 | |||
1475 | spin_lock_irqsave(&camif->slock, flags); | ||
1476 | *crop = sel->r; | ||
1477 | |||
1478 | for (i = 0; i < CAMIF_VP_NUM; i++) { | ||
1479 | struct camif_vp *vp = &camif->vp[i]; | ||
1480 | scaler = vp->scaler; | ||
1481 | if (s3c_camif_get_scaler_config(vp, &scaler)) | ||
1482 | continue; | ||
1483 | vp->scaler = scaler; | ||
1484 | vp->state |= ST_VP_CONFIG; | ||
1485 | } | ||
1486 | |||
1487 | spin_unlock_irqrestore(&camif->slock, flags); | ||
1488 | } | ||
1489 | mutex_unlock(&camif->lock); | ||
1490 | |||
1491 | v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %u, f_h: %u\n", | ||
1492 | __func__, crop->left, crop->top, crop->width, crop->height, | ||
1493 | camif->mbus_fmt.width, camif->mbus_fmt.height); | ||
1494 | |||
1495 | return 0; | ||
1496 | } | ||
1497 | |||
1498 | static const struct v4l2_subdev_pad_ops s3c_camif_subdev_pad_ops = { | ||
1499 | .enum_mbus_code = s3c_camif_subdev_enum_mbus_code, | ||
1500 | .get_selection = s3c_camif_subdev_get_selection, | ||
1501 | .set_selection = s3c_camif_subdev_set_selection, | ||
1502 | .get_fmt = s3c_camif_subdev_get_fmt, | ||
1503 | .set_fmt = s3c_camif_subdev_set_fmt, | ||
1504 | }; | ||
1505 | |||
1506 | static struct v4l2_subdev_ops s3c_camif_subdev_ops = { | ||
1507 | .pad = &s3c_camif_subdev_pad_ops, | ||
1508 | }; | ||
1509 | |||
1510 | static int s3c_camif_subdev_s_ctrl(struct v4l2_ctrl *ctrl) | ||
1511 | { | ||
1512 | struct camif_dev *camif = container_of(ctrl->handler, struct camif_dev, | ||
1513 | ctrl_handler); | ||
1514 | unsigned long flags; | ||
1515 | |||
1516 | spin_lock_irqsave(&camif->slock, flags); | ||
1517 | |||
1518 | switch (ctrl->id) { | ||
1519 | case V4L2_CID_COLORFX: | ||
1520 | camif->colorfx = camif->ctrl_colorfx->val; | ||
1521 | /* Set Cb, Cr */ | ||
1522 | switch (ctrl->val) { | ||
1523 | case V4L2_COLORFX_SEPIA: | ||
1524 | camif->colorfx_cb = 115; | ||
1525 | camif->colorfx_cr = 145; | ||
1526 | break; | ||
1527 | case V4L2_COLORFX_SET_CBCR: | ||
1528 | camif->colorfx_cb = camif->ctrl_colorfx_cbcr->val >> 8; | ||
1529 | camif->colorfx_cr = camif->ctrl_colorfx_cbcr->val & 0xff; | ||
1530 | break; | ||
1531 | default: | ||
1532 | /* for V4L2_COLORFX_BW and others */ | ||
1533 | camif->colorfx_cb = 128; | ||
1534 | camif->colorfx_cr = 128; | ||
1535 | } | ||
1536 | break; | ||
1537 | case V4L2_CID_TEST_PATTERN: | ||
1538 | camif->test_pattern = camif->ctrl_test_pattern->val; | ||
1539 | break; | ||
1540 | default: | ||
1541 | WARN_ON(1); | ||
1542 | } | ||
1543 | |||
1544 | camif->vp[VP_CODEC].state |= ST_VP_CONFIG; | ||
1545 | camif->vp[VP_PREVIEW].state |= ST_VP_CONFIG; | ||
1546 | spin_unlock_irqrestore(&camif->slock, flags); | ||
1547 | |||
1548 | return 0; | ||
1549 | } | ||
1550 | |||
1551 | static const struct v4l2_ctrl_ops s3c_camif_subdev_ctrl_ops = { | ||
1552 | .s_ctrl = s3c_camif_subdev_s_ctrl, | ||
1553 | }; | ||
1554 | |||
1555 | static const char * const s3c_camif_test_pattern_menu[] = { | ||
1556 | "Disabled", | ||
1557 | "Color bars", | ||
1558 | "Horizontal increment", | ||
1559 | "Vertical increment", | ||
1560 | }; | ||
1561 | |||
1562 | int s3c_camif_create_subdev(struct camif_dev *camif) | ||
1563 | { | ||
1564 | struct v4l2_ctrl_handler *handler = &camif->ctrl_handler; | ||
1565 | struct v4l2_subdev *sd = &camif->subdev; | ||
1566 | int ret; | ||
1567 | |||
1568 | v4l2_subdev_init(sd, &s3c_camif_subdev_ops); | ||
1569 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1570 | strlcpy(sd->name, "S3C-CAMIF", sizeof(sd->name)); | ||
1571 | |||
1572 | camif->pads[CAMIF_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
1573 | camif->pads[CAMIF_SD_PAD_SOURCE_C].flags = MEDIA_PAD_FL_SOURCE; | ||
1574 | camif->pads[CAMIF_SD_PAD_SOURCE_P].flags = MEDIA_PAD_FL_SOURCE; | ||
1575 | |||
1576 | ret = media_entity_init(&sd->entity, CAMIF_SD_PADS_NUM, | ||
1577 | camif->pads, 0); | ||
1578 | if (ret) | ||
1579 | return ret; | ||
1580 | |||
1581 | v4l2_ctrl_handler_init(handler, 3); | ||
1582 | camif->ctrl_test_pattern = v4l2_ctrl_new_std_menu_items(handler, | ||
1583 | &s3c_camif_subdev_ctrl_ops, V4L2_CID_TEST_PATTERN, | ||
1584 | ARRAY_SIZE(s3c_camif_test_pattern_menu) - 1, 0, 0, | ||
1585 | s3c_camif_test_pattern_menu); | ||
1586 | |||
1587 | camif->ctrl_colorfx = v4l2_ctrl_new_std_menu(handler, | ||
1588 | &s3c_camif_subdev_ctrl_ops, | ||
1589 | V4L2_CID_COLORFX, V4L2_COLORFX_SET_CBCR, | ||
1590 | ~0x981f, V4L2_COLORFX_NONE); | ||
1591 | |||
1592 | camif->ctrl_colorfx_cbcr = v4l2_ctrl_new_std(handler, | ||
1593 | &s3c_camif_subdev_ctrl_ops, | ||
1594 | V4L2_CID_COLORFX_CBCR, 0, 0xffff, 1, 0); | ||
1595 | if (handler->error) { | ||
1596 | v4l2_ctrl_handler_free(handler); | ||
1597 | media_entity_cleanup(&sd->entity); | ||
1598 | return handler->error; | ||
1599 | } | ||
1600 | |||
1601 | v4l2_ctrl_auto_cluster(2, &camif->ctrl_colorfx, | ||
1602 | V4L2_COLORFX_SET_CBCR, false); | ||
1603 | if (!camif->variant->has_img_effect) { | ||
1604 | camif->ctrl_colorfx->flags |= V4L2_CTRL_FLAG_DISABLED; | ||
1605 | camif->ctrl_colorfx_cbcr->flags |= V4L2_CTRL_FLAG_DISABLED; | ||
1606 | } | ||
1607 | sd->ctrl_handler = handler; | ||
1608 | v4l2_set_subdevdata(sd, camif); | ||
1609 | |||
1610 | return 0; | ||
1611 | } | ||
1612 | |||
1613 | void s3c_camif_unregister_subdev(struct camif_dev *camif) | ||
1614 | { | ||
1615 | struct v4l2_subdev *sd = &camif->subdev; | ||
1616 | |||
1617 | /* Return if not registered */ | ||
1618 | if (v4l2_get_subdevdata(sd) == NULL) | ||
1619 | return; | ||
1620 | |||
1621 | v4l2_device_unregister_subdev(sd); | ||
1622 | media_entity_cleanup(&sd->entity); | ||
1623 | v4l2_ctrl_handler_free(&camif->ctrl_handler); | ||
1624 | v4l2_set_subdevdata(sd, NULL); | ||
1625 | } | ||
1626 | |||
1627 | int s3c_camif_set_defaults(struct camif_dev *camif) | ||
1628 | { | ||
1629 | unsigned int ip_rev = camif->variant->ip_revision; | ||
1630 | int i; | ||
1631 | |||
1632 | for (i = 0; i < CAMIF_VP_NUM; i++) { | ||
1633 | struct camif_vp *vp = &camif->vp[i]; | ||
1634 | struct camif_frame *f = &vp->out_frame; | ||
1635 | |||
1636 | vp->camif = camif; | ||
1637 | vp->id = i; | ||
1638 | vp->offset = camif->variant->vp_offset; | ||
1639 | |||
1640 | if (ip_rev == S3C244X_CAMIF_IP_REV) | ||
1641 | vp->fmt_flags = i ? FMT_FL_S3C24XX_PREVIEW : | ||
1642 | FMT_FL_S3C24XX_CODEC; | ||
1643 | else | ||
1644 | vp->fmt_flags = FMT_FL_S3C64XX; | ||
1645 | |||
1646 | vp->out_fmt = s3c_camif_find_format(vp, NULL, 0); | ||
1647 | BUG_ON(vp->out_fmt == NULL); | ||
1648 | |||
1649 | memset(f, 0, sizeof(*f)); | ||
1650 | f->f_width = CAMIF_DEF_WIDTH; | ||
1651 | f->f_height = CAMIF_DEF_HEIGHT; | ||
1652 | f->rect.width = CAMIF_DEF_WIDTH; | ||
1653 | f->rect.height = CAMIF_DEF_HEIGHT; | ||
1654 | |||
1655 | /* Scaler is always enabled */ | ||
1656 | vp->scaler.enable = 1; | ||
1657 | |||
1658 | vp->payload = (f->f_width * f->f_height * | ||
1659 | vp->out_fmt->depth) / 8; | ||
1660 | } | ||
1661 | |||
1662 | memset(&camif->mbus_fmt, 0, sizeof(camif->mbus_fmt)); | ||
1663 | camif->mbus_fmt.width = CAMIF_DEF_WIDTH; | ||
1664 | camif->mbus_fmt.height = CAMIF_DEF_HEIGHT; | ||
1665 | camif->mbus_fmt.code = camif_mbus_formats[0]; | ||
1666 | |||
1667 | memset(&camif->camif_crop, 0, sizeof(camif->camif_crop)); | ||
1668 | camif->camif_crop.width = CAMIF_DEF_WIDTH; | ||
1669 | camif->camif_crop.height = CAMIF_DEF_HEIGHT; | ||
1670 | |||
1671 | return 0; | ||
1672 | } | ||
diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c new file mode 100644 index 000000000000..0dd65376c067 --- /dev/null +++ b/drivers/media/platform/s3c-camif/camif-core.c | |||
@@ -0,0 +1,662 @@ | |||
1 | /* | ||
2 | * s3c24xx/s3c64xx SoC series Camera Interface (CAMIF) driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com> | ||
5 | * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.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 | #define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ | ||
13 | |||
14 | #include <linux/bug.h> | ||
15 | #include <linux/clk.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/errno.h> | ||
19 | #include <linux/gpio.h> | ||
20 | #include <linux/i2c.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/io.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/list.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/pm_runtime.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/types.h> | ||
30 | |||
31 | #include <media/media-device.h> | ||
32 | #include <media/v4l2-ctrls.h> | ||
33 | #include <media/v4l2-ioctl.h> | ||
34 | #include <media/videobuf2-core.h> | ||
35 | #include <media/videobuf2-dma-contig.h> | ||
36 | |||
37 | #include "camif-core.h" | ||
38 | |||
39 | static char *camif_clocks[CLK_MAX_NUM] = { | ||
40 | /* HCLK CAMIF clock */ | ||
41 | [CLK_GATE] = "camif", | ||
42 | /* CAMIF / external camera sensor master clock */ | ||
43 | [CLK_CAM] = "camera", | ||
44 | }; | ||
45 | |||
46 | static const struct camif_fmt camif_formats[] = { | ||
47 | { | ||
48 | .name = "YUV 4:2:2 planar, Y/Cb/Cr", | ||
49 | .fourcc = V4L2_PIX_FMT_YUV422P, | ||
50 | .depth = 16, | ||
51 | .ybpp = 1, | ||
52 | .color = IMG_FMT_YCBCR422P, | ||
53 | .colplanes = 3, | ||
54 | .flags = FMT_FL_S3C24XX_CODEC | | ||
55 | FMT_FL_S3C64XX, | ||
56 | }, { | ||
57 | .name = "YUV 4:2:0 planar, Y/Cb/Cr", | ||
58 | .fourcc = V4L2_PIX_FMT_YUV420, | ||
59 | .depth = 12, | ||
60 | .ybpp = 1, | ||
61 | .color = IMG_FMT_YCBCR420, | ||
62 | .colplanes = 3, | ||
63 | .flags = FMT_FL_S3C24XX_CODEC | | ||
64 | FMT_FL_S3C64XX, | ||
65 | }, { | ||
66 | .name = "YVU 4:2:0 planar, Y/Cr/Cb", | ||
67 | .fourcc = V4L2_PIX_FMT_YVU420, | ||
68 | .depth = 12, | ||
69 | .ybpp = 1, | ||
70 | .color = IMG_FMT_YCRCB420, | ||
71 | .colplanes = 3, | ||
72 | .flags = FMT_FL_S3C24XX_CODEC | | ||
73 | FMT_FL_S3C64XX, | ||
74 | }, { | ||
75 | .name = "RGB565, 16 bpp", | ||
76 | .fourcc = V4L2_PIX_FMT_RGB565X, | ||
77 | .depth = 16, | ||
78 | .ybpp = 2, | ||
79 | .color = IMG_FMT_RGB565, | ||
80 | .colplanes = 1, | ||
81 | .flags = FMT_FL_S3C24XX_PREVIEW | | ||
82 | FMT_FL_S3C64XX, | ||
83 | }, { | ||
84 | .name = "XRGB8888, 32 bpp", | ||
85 | .fourcc = V4L2_PIX_FMT_RGB32, | ||
86 | .depth = 32, | ||
87 | .ybpp = 4, | ||
88 | .color = IMG_FMT_XRGB8888, | ||
89 | .colplanes = 1, | ||
90 | .flags = FMT_FL_S3C24XX_PREVIEW | | ||
91 | FMT_FL_S3C64XX, | ||
92 | }, { | ||
93 | .name = "BGR666", | ||
94 | .fourcc = V4L2_PIX_FMT_BGR666, | ||
95 | .depth = 32, | ||
96 | .ybpp = 4, | ||
97 | .color = IMG_FMT_RGB666, | ||
98 | .colplanes = 1, | ||
99 | .flags = FMT_FL_S3C64XX, | ||
100 | } | ||
101 | }; | ||
102 | |||
103 | /** | ||
104 | * s3c_camif_find_format() - lookup camif color format by fourcc or an index | ||
105 | * @pixelformat: fourcc to match, ignored if null | ||
106 | * @index: index to the camif_formats array, ignored if negative | ||
107 | */ | ||
108 | const struct camif_fmt *s3c_camif_find_format(struct camif_vp *vp, | ||
109 | const u32 *pixelformat, | ||
110 | int index) | ||
111 | { | ||
112 | const struct camif_fmt *fmt, *def_fmt = NULL; | ||
113 | unsigned int i; | ||
114 | int id = 0; | ||
115 | |||
116 | if (index >= (int)ARRAY_SIZE(camif_formats)) | ||
117 | return NULL; | ||
118 | |||
119 | for (i = 0; i < ARRAY_SIZE(camif_formats); ++i) { | ||
120 | fmt = &camif_formats[i]; | ||
121 | if (vp && !(vp->fmt_flags & fmt->flags)) | ||
122 | continue; | ||
123 | if (pixelformat && fmt->fourcc == *pixelformat) | ||
124 | return fmt; | ||
125 | if (index == id) | ||
126 | def_fmt = fmt; | ||
127 | id++; | ||
128 | } | ||
129 | return def_fmt; | ||
130 | } | ||
131 | |||
132 | static int camif_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift) | ||
133 | { | ||
134 | unsigned int sh = 6; | ||
135 | |||
136 | if (src >= 64 * tar) | ||
137 | return -EINVAL; | ||
138 | |||
139 | while (sh--) { | ||
140 | unsigned int tmp = 1 << sh; | ||
141 | if (src >= tar * tmp) { | ||
142 | *shift = sh, *ratio = tmp; | ||
143 | return 0; | ||
144 | } | ||
145 | } | ||
146 | *shift = 0, *ratio = 1; | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | int s3c_camif_get_scaler_config(struct camif_vp *vp, | ||
151 | struct camif_scaler *scaler) | ||
152 | { | ||
153 | struct v4l2_rect *camif_crop = &vp->camif->camif_crop; | ||
154 | int source_x = camif_crop->width; | ||
155 | int source_y = camif_crop->height; | ||
156 | int target_x = vp->out_frame.rect.width; | ||
157 | int target_y = vp->out_frame.rect.height; | ||
158 | int ret; | ||
159 | |||
160 | if (vp->rotation == 90 || vp->rotation == 270) | ||
161 | swap(target_x, target_y); | ||
162 | |||
163 | ret = camif_get_scaler_factor(source_x, target_x, &scaler->pre_h_ratio, | ||
164 | &scaler->h_shift); | ||
165 | if (ret < 0) | ||
166 | return ret; | ||
167 | |||
168 | ret = camif_get_scaler_factor(source_y, target_y, &scaler->pre_v_ratio, | ||
169 | &scaler->v_shift); | ||
170 | if (ret < 0) | ||
171 | return ret; | ||
172 | |||
173 | scaler->pre_dst_width = source_x / scaler->pre_h_ratio; | ||
174 | scaler->pre_dst_height = source_y / scaler->pre_v_ratio; | ||
175 | |||
176 | scaler->main_h_ratio = (source_x << 8) / (target_x << scaler->h_shift); | ||
177 | scaler->main_v_ratio = (source_y << 8) / (target_y << scaler->v_shift); | ||
178 | |||
179 | scaler->scaleup_h = (target_x >= source_x); | ||
180 | scaler->scaleup_v = (target_y >= source_y); | ||
181 | |||
182 | scaler->copy = 0; | ||
183 | |||
184 | pr_debug("H: ratio: %u, shift: %u. V: ratio: %u, shift: %u.\n", | ||
185 | scaler->pre_h_ratio, scaler->h_shift, | ||
186 | scaler->pre_v_ratio, scaler->v_shift); | ||
187 | |||
188 | pr_debug("Source: %dx%d, Target: %dx%d, scaleup_h/v: %d/%d\n", | ||
189 | source_x, source_y, target_x, target_y, | ||
190 | scaler->scaleup_h, scaler->scaleup_v); | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static int camif_register_sensor(struct camif_dev *camif) | ||
196 | { | ||
197 | struct s3c_camif_sensor_info *sensor = &camif->pdata.sensor; | ||
198 | struct v4l2_device *v4l2_dev = &camif->v4l2_dev; | ||
199 | struct i2c_adapter *adapter; | ||
200 | struct v4l2_subdev_format format; | ||
201 | struct v4l2_subdev *sd; | ||
202 | int ret; | ||
203 | |||
204 | camif->sensor.sd = NULL; | ||
205 | |||
206 | if (sensor->i2c_board_info.addr == 0) | ||
207 | return -EINVAL; | ||
208 | |||
209 | adapter = i2c_get_adapter(sensor->i2c_bus_num); | ||
210 | if (adapter == NULL) { | ||
211 | v4l2_warn(v4l2_dev, "failed to get I2C adapter %d\n", | ||
212 | sensor->i2c_bus_num); | ||
213 | return -EPROBE_DEFER; | ||
214 | } | ||
215 | |||
216 | sd = v4l2_i2c_new_subdev_board(v4l2_dev, adapter, | ||
217 | &sensor->i2c_board_info, NULL); | ||
218 | if (sd == NULL) { | ||
219 | i2c_put_adapter(adapter); | ||
220 | v4l2_warn(v4l2_dev, "failed to acquire subdev %s\n", | ||
221 | sensor->i2c_board_info.type); | ||
222 | return -EPROBE_DEFER; | ||
223 | } | ||
224 | camif->sensor.sd = sd; | ||
225 | |||
226 | v4l2_info(v4l2_dev, "registered sensor subdevice %s\n", sd->name); | ||
227 | |||
228 | /* Get initial pixel format and set it at the camif sink pad */ | ||
229 | format.pad = 0; | ||
230 | format.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
231 | ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &format); | ||
232 | |||
233 | if (ret < 0) | ||
234 | return 0; | ||
235 | |||
236 | format.pad = CAMIF_SD_PAD_SINK; | ||
237 | v4l2_subdev_call(&camif->subdev, pad, set_fmt, NULL, &format); | ||
238 | |||
239 | v4l2_info(sd, "Initial format from sensor: %dx%d, %#x\n", | ||
240 | format.format.width, format.format.height, | ||
241 | format.format.code); | ||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | static void camif_unregister_sensor(struct camif_dev *camif) | ||
246 | { | ||
247 | struct v4l2_subdev *sd = camif->sensor.sd; | ||
248 | struct i2c_client *client = sd ? v4l2_get_subdevdata(sd) : NULL; | ||
249 | struct i2c_adapter *adapter; | ||
250 | |||
251 | if (client == NULL) | ||
252 | return; | ||
253 | |||
254 | adapter = client->adapter; | ||
255 | v4l2_device_unregister_subdev(sd); | ||
256 | camif->sensor.sd = NULL; | ||
257 | i2c_unregister_device(client); | ||
258 | if (adapter) | ||
259 | i2c_put_adapter(adapter); | ||
260 | } | ||
261 | |||
262 | static int camif_create_media_links(struct camif_dev *camif) | ||
263 | { | ||
264 | int i, ret; | ||
265 | |||
266 | ret = media_entity_create_link(&camif->sensor.sd->entity, 0, | ||
267 | &camif->subdev.entity, CAMIF_SD_PAD_SINK, | ||
268 | MEDIA_LNK_FL_IMMUTABLE | | ||
269 | MEDIA_LNK_FL_ENABLED); | ||
270 | if (ret) | ||
271 | return ret; | ||
272 | |||
273 | for (i = 1; i < CAMIF_SD_PADS_NUM && !ret; i++) { | ||
274 | ret = media_entity_create_link(&camif->subdev.entity, i, | ||
275 | &camif->vp[i - 1].vdev.entity, 0, | ||
276 | MEDIA_LNK_FL_IMMUTABLE | | ||
277 | MEDIA_LNK_FL_ENABLED); | ||
278 | } | ||
279 | |||
280 | return ret; | ||
281 | } | ||
282 | |||
283 | static int camif_register_video_nodes(struct camif_dev *camif) | ||
284 | { | ||
285 | int ret = s3c_camif_register_video_node(camif, VP_CODEC); | ||
286 | if (ret < 0) | ||
287 | return ret; | ||
288 | |||
289 | return s3c_camif_register_video_node(camif, VP_PREVIEW); | ||
290 | } | ||
291 | |||
292 | static void camif_unregister_video_nodes(struct camif_dev *camif) | ||
293 | { | ||
294 | s3c_camif_unregister_video_node(camif, VP_CODEC); | ||
295 | s3c_camif_unregister_video_node(camif, VP_PREVIEW); | ||
296 | } | ||
297 | |||
298 | static void camif_unregister_media_entities(struct camif_dev *camif) | ||
299 | { | ||
300 | camif_unregister_video_nodes(camif); | ||
301 | camif_unregister_sensor(camif); | ||
302 | s3c_camif_unregister_subdev(camif); | ||
303 | } | ||
304 | |||
305 | /* | ||
306 | * Media device | ||
307 | */ | ||
308 | static int camif_media_dev_register(struct camif_dev *camif) | ||
309 | { | ||
310 | struct media_device *md = &camif->media_dev; | ||
311 | struct v4l2_device *v4l2_dev = &camif->v4l2_dev; | ||
312 | unsigned int ip_rev = camif->variant->ip_revision; | ||
313 | int ret; | ||
314 | |||
315 | memset(md, 0, sizeof(*md)); | ||
316 | snprintf(md->model, sizeof(md->model), "SAMSUNG S3C%s CAMIF", | ||
317 | ip_rev == S3C6410_CAMIF_IP_REV ? "6410" : "244X"); | ||
318 | strlcpy(md->bus_info, "platform", sizeof(md->bus_info)); | ||
319 | md->hw_revision = ip_rev; | ||
320 | md->driver_version = KERNEL_VERSION(1, 0, 0); | ||
321 | |||
322 | md->dev = camif->dev; | ||
323 | |||
324 | strlcpy(v4l2_dev->name, "s3c-camif", sizeof(v4l2_dev->name)); | ||
325 | v4l2_dev->mdev = md; | ||
326 | |||
327 | ret = v4l2_device_register(camif->dev, v4l2_dev); | ||
328 | if (ret < 0) | ||
329 | return ret; | ||
330 | |||
331 | ret = media_device_register(md); | ||
332 | if (ret < 0) | ||
333 | v4l2_device_unregister(v4l2_dev); | ||
334 | |||
335 | return ret; | ||
336 | } | ||
337 | |||
338 | static void camif_clk_put(struct camif_dev *camif) | ||
339 | { | ||
340 | int i; | ||
341 | |||
342 | for (i = 0; i < CLK_MAX_NUM; i++) { | ||
343 | if (IS_ERR_OR_NULL(camif->clock[i])) | ||
344 | continue; | ||
345 | clk_unprepare(camif->clock[i]); | ||
346 | clk_put(camif->clock[i]); | ||
347 | } | ||
348 | } | ||
349 | |||
350 | static int camif_clk_get(struct camif_dev *camif) | ||
351 | { | ||
352 | int ret, i; | ||
353 | |||
354 | for (i = 0; i < CLK_MAX_NUM; i++) { | ||
355 | camif->clock[i] = clk_get(camif->dev, camif_clocks[i]); | ||
356 | if (IS_ERR(camif->clock[i])) { | ||
357 | ret = PTR_ERR(camif->clock[i]); | ||
358 | goto err; | ||
359 | } | ||
360 | ret = clk_prepare(camif->clock[i]); | ||
361 | if (ret < 0) { | ||
362 | clk_put(camif->clock[i]); | ||
363 | camif->clock[i] = NULL; | ||
364 | goto err; | ||
365 | } | ||
366 | } | ||
367 | return 0; | ||
368 | err: | ||
369 | camif_clk_put(camif); | ||
370 | dev_err(camif->dev, "failed to get clock: %s\n", | ||
371 | camif_clocks[i]); | ||
372 | return ret; | ||
373 | } | ||
374 | |||
375 | /* | ||
376 | * The CAMIF device has two relatively independent data processing paths | ||
377 | * that can source data from memory or the common camera input frontend. | ||
378 | * Register interrupts for each data processing path (camif_vp). | ||
379 | */ | ||
380 | static int camif_request_irqs(struct platform_device *pdev, | ||
381 | struct camif_dev *camif) | ||
382 | { | ||
383 | int irq, ret, i; | ||
384 | |||
385 | for (i = 0; i < CAMIF_VP_NUM; i++) { | ||
386 | struct camif_vp *vp = &camif->vp[i]; | ||
387 | |||
388 | init_waitqueue_head(&vp->irq_queue); | ||
389 | |||
390 | irq = platform_get_irq(pdev, i); | ||
391 | if (irq <= 0) { | ||
392 | dev_err(&pdev->dev, "failed to get IRQ %d\n", i); | ||
393 | return -ENXIO; | ||
394 | } | ||
395 | |||
396 | ret = devm_request_irq(&pdev->dev, irq, s3c_camif_irq_handler, | ||
397 | 0, dev_name(&pdev->dev), vp); | ||
398 | if (ret < 0) { | ||
399 | dev_err(&pdev->dev, "failed to install IRQ: %d\n", ret); | ||
400 | break; | ||
401 | } | ||
402 | } | ||
403 | |||
404 | return ret; | ||
405 | } | ||
406 | |||
407 | static int s3c_camif_probe(struct platform_device *pdev) | ||
408 | { | ||
409 | struct device *dev = &pdev->dev; | ||
410 | struct s3c_camif_plat_data *pdata = dev->platform_data; | ||
411 | struct s3c_camif_drvdata *drvdata; | ||
412 | struct camif_dev *camif; | ||
413 | struct resource *mres; | ||
414 | int ret = 0; | ||
415 | |||
416 | camif = devm_kzalloc(dev, sizeof(*camif), GFP_KERNEL); | ||
417 | if (!camif) | ||
418 | return -ENOMEM; | ||
419 | |||
420 | spin_lock_init(&camif->slock); | ||
421 | mutex_init(&camif->lock); | ||
422 | |||
423 | camif->dev = dev; | ||
424 | |||
425 | if (!pdata || !pdata->gpio_get || !pdata->gpio_put) { | ||
426 | dev_err(dev, "wrong platform data\n"); | ||
427 | return -EINVAL; | ||
428 | } | ||
429 | |||
430 | camif->pdata = *pdata; | ||
431 | drvdata = (void *)platform_get_device_id(pdev)->driver_data; | ||
432 | camif->variant = drvdata->variant; | ||
433 | |||
434 | mres = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
435 | |||
436 | camif->io_base = devm_request_and_ioremap(dev, mres); | ||
437 | if (!camif->io_base) { | ||
438 | dev_err(dev, "failed to obtain I/O memory\n"); | ||
439 | return -ENOENT; | ||
440 | } | ||
441 | |||
442 | ret = camif_request_irqs(pdev, camif); | ||
443 | if (ret < 0) | ||
444 | return ret; | ||
445 | |||
446 | ret = pdata->gpio_get(); | ||
447 | if (ret < 0) | ||
448 | return ret; | ||
449 | |||
450 | ret = s3c_camif_create_subdev(camif); | ||
451 | if (ret < 0) | ||
452 | goto err_sd; | ||
453 | |||
454 | ret = camif_clk_get(camif); | ||
455 | if (ret < 0) | ||
456 | goto err_clk; | ||
457 | |||
458 | platform_set_drvdata(pdev, camif); | ||
459 | clk_set_rate(camif->clock[CLK_CAM], | ||
460 | camif->pdata.sensor.clock_frequency); | ||
461 | |||
462 | dev_info(dev, "sensor clock frequency: %lu\n", | ||
463 | clk_get_rate(camif->clock[CLK_CAM])); | ||
464 | /* | ||
465 | * Set initial pixel format, resolution and crop rectangle. | ||
466 | * Must be done before a sensor subdev is registered as some | ||
467 | * settings are overrode with values from sensor subdev. | ||
468 | */ | ||
469 | s3c_camif_set_defaults(camif); | ||
470 | |||
471 | pm_runtime_enable(dev); | ||
472 | |||
473 | ret = pm_runtime_get_sync(dev); | ||
474 | if (ret < 0) | ||
475 | goto err_pm; | ||
476 | |||
477 | /* Initialize contiguous memory allocator */ | ||
478 | camif->alloc_ctx = vb2_dma_contig_init_ctx(dev); | ||
479 | if (IS_ERR(camif->alloc_ctx)) { | ||
480 | ret = PTR_ERR(camif->alloc_ctx); | ||
481 | goto err_alloc; | ||
482 | } | ||
483 | |||
484 | ret = camif_media_dev_register(camif); | ||
485 | if (ret < 0) | ||
486 | goto err_mdev; | ||
487 | |||
488 | ret = camif_register_sensor(camif); | ||
489 | if (ret < 0) | ||
490 | goto err_sens; | ||
491 | |||
492 | ret = v4l2_device_register_subdev(&camif->v4l2_dev, &camif->subdev); | ||
493 | if (ret < 0) | ||
494 | goto err_sens; | ||
495 | |||
496 | mutex_lock(&camif->media_dev.graph_mutex); | ||
497 | |||
498 | ret = v4l2_device_register_subdev_nodes(&camif->v4l2_dev); | ||
499 | if (ret < 0) | ||
500 | goto err_unlock; | ||
501 | |||
502 | ret = camif_register_video_nodes(camif); | ||
503 | if (ret < 0) | ||
504 | goto err_unlock; | ||
505 | |||
506 | ret = camif_create_media_links(camif); | ||
507 | if (ret < 0) | ||
508 | goto err_unlock; | ||
509 | |||
510 | mutex_unlock(&camif->media_dev.graph_mutex); | ||
511 | pm_runtime_put(dev); | ||
512 | return 0; | ||
513 | |||
514 | err_unlock: | ||
515 | mutex_unlock(&camif->media_dev.graph_mutex); | ||
516 | err_sens: | ||
517 | v4l2_device_unregister(&camif->v4l2_dev); | ||
518 | media_device_unregister(&camif->media_dev); | ||
519 | camif_unregister_media_entities(camif); | ||
520 | err_mdev: | ||
521 | vb2_dma_contig_cleanup_ctx(camif->alloc_ctx); | ||
522 | err_alloc: | ||
523 | pm_runtime_put(dev); | ||
524 | pm_runtime_disable(dev); | ||
525 | err_pm: | ||
526 | camif_clk_put(camif); | ||
527 | err_clk: | ||
528 | s3c_camif_unregister_subdev(camif); | ||
529 | err_sd: | ||
530 | pdata->gpio_put(); | ||
531 | return ret; | ||
532 | } | ||
533 | |||
534 | static int __devexit s3c_camif_remove(struct platform_device *pdev) | ||
535 | { | ||
536 | struct camif_dev *camif = platform_get_drvdata(pdev); | ||
537 | struct s3c_camif_plat_data *pdata = &camif->pdata; | ||
538 | |||
539 | media_device_unregister(&camif->media_dev); | ||
540 | camif_unregister_media_entities(camif); | ||
541 | v4l2_device_unregister(&camif->v4l2_dev); | ||
542 | |||
543 | pm_runtime_disable(&pdev->dev); | ||
544 | camif_clk_put(camif); | ||
545 | pdata->gpio_put(); | ||
546 | |||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | static int s3c_camif_runtime_resume(struct device *dev) | ||
551 | { | ||
552 | struct camif_dev *camif = dev_get_drvdata(dev); | ||
553 | |||
554 | clk_enable(camif->clock[CLK_GATE]); | ||
555 | /* null op on s3c244x */ | ||
556 | clk_enable(camif->clock[CLK_CAM]); | ||
557 | return 0; | ||
558 | } | ||
559 | |||
560 | static int s3c_camif_runtime_suspend(struct device *dev) | ||
561 | { | ||
562 | struct camif_dev *camif = dev_get_drvdata(dev); | ||
563 | |||
564 | /* null op on s3c244x */ | ||
565 | clk_disable(camif->clock[CLK_CAM]); | ||
566 | |||
567 | clk_disable(camif->clock[CLK_GATE]); | ||
568 | return 0; | ||
569 | } | ||
570 | |||
571 | static const struct s3c_camif_variant s3c244x_camif_variant = { | ||
572 | .vp_pix_limits = { | ||
573 | [VP_CODEC] = { | ||
574 | .max_out_width = 4096, | ||
575 | .max_sc_out_width = 2048, | ||
576 | .out_width_align = 16, | ||
577 | .min_out_width = 16, | ||
578 | .max_height = 4096, | ||
579 | }, | ||
580 | [VP_PREVIEW] = { | ||
581 | .max_out_width = 640, | ||
582 | .max_sc_out_width = 640, | ||
583 | .out_width_align = 16, | ||
584 | .min_out_width = 16, | ||
585 | .max_height = 480, | ||
586 | } | ||
587 | }, | ||
588 | .pix_limits = { | ||
589 | .win_hor_offset_align = 8, | ||
590 | }, | ||
591 | .ip_revision = S3C244X_CAMIF_IP_REV, | ||
592 | }; | ||
593 | |||
594 | static struct s3c_camif_drvdata s3c244x_camif_drvdata = { | ||
595 | .variant = &s3c244x_camif_variant, | ||
596 | .bus_clk_freq = 24000000UL, | ||
597 | }; | ||
598 | |||
599 | static const struct s3c_camif_variant s3c6410_camif_variant = { | ||
600 | .vp_pix_limits = { | ||
601 | [VP_CODEC] = { | ||
602 | .max_out_width = 4096, | ||
603 | .max_sc_out_width = 2048, | ||
604 | .out_width_align = 16, | ||
605 | .min_out_width = 16, | ||
606 | .max_height = 4096, | ||
607 | }, | ||
608 | [VP_PREVIEW] = { | ||
609 | .max_out_width = 4096, | ||
610 | .max_sc_out_width = 720, | ||
611 | .out_width_align = 16, | ||
612 | .min_out_width = 16, | ||
613 | .max_height = 4096, | ||
614 | } | ||
615 | }, | ||
616 | .pix_limits = { | ||
617 | .win_hor_offset_align = 8, | ||
618 | }, | ||
619 | .ip_revision = S3C6410_CAMIF_IP_REV, | ||
620 | .has_img_effect = 1, | ||
621 | .vp_offset = 0x20, | ||
622 | }; | ||
623 | |||
624 | static struct s3c_camif_drvdata s3c6410_camif_drvdata = { | ||
625 | .variant = &s3c6410_camif_variant, | ||
626 | .bus_clk_freq = 133000000UL, | ||
627 | }; | ||
628 | |||
629 | static struct platform_device_id s3c_camif_driver_ids[] = { | ||
630 | { | ||
631 | .name = "s3c2440-camif", | ||
632 | .driver_data = (unsigned long)&s3c244x_camif_drvdata, | ||
633 | }, { | ||
634 | .name = "s3c6410-camif", | ||
635 | .driver_data = (unsigned long)&s3c6410_camif_drvdata, | ||
636 | }, | ||
637 | { /* sentinel */ }, | ||
638 | }; | ||
639 | MODULE_DEVICE_TABLE(platform, s3c_camif_driver_ids); | ||
640 | |||
641 | static const struct dev_pm_ops s3c_camif_pm_ops = { | ||
642 | .runtime_suspend = s3c_camif_runtime_suspend, | ||
643 | .runtime_resume = s3c_camif_runtime_resume, | ||
644 | }; | ||
645 | |||
646 | static struct platform_driver s3c_camif_driver = { | ||
647 | .probe = s3c_camif_probe, | ||
648 | .remove = __devexit_p(s3c_camif_remove), | ||
649 | .id_table = s3c_camif_driver_ids, | ||
650 | .driver = { | ||
651 | .name = S3C_CAMIF_DRIVER_NAME, | ||
652 | .owner = THIS_MODULE, | ||
653 | .pm = &s3c_camif_pm_ops, | ||
654 | } | ||
655 | }; | ||
656 | |||
657 | module_platform_driver(s3c_camif_driver); | ||
658 | |||
659 | MODULE_AUTHOR("Sylwester Nawrocki <sylvester.nawrocki@gmail.com>"); | ||
660 | MODULE_AUTHOR("Tomasz Figa <tomasz.figa@gmail.com>"); | ||
661 | MODULE_DESCRIPTION("S3C24XX/S3C64XX SoC camera interface driver"); | ||
662 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/platform/s3c-camif/camif-core.h b/drivers/media/platform/s3c-camif/camif-core.h new file mode 100644 index 000000000000..261134baa655 --- /dev/null +++ b/drivers/media/platform/s3c-camif/camif-core.h | |||
@@ -0,0 +1,393 @@ | |||
1 | /* | ||
2 | * s3c24xx/s3c64xx SoC series Camera Interface (CAMIF) driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com> | ||
5 | * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.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 | #ifndef CAMIF_CORE_H_ | ||
13 | #define CAMIF_CORE_H_ | ||
14 | |||
15 | #include <linux/io.h> | ||
16 | #include <linux/irq.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/v4l2-ctrls.h> | ||
25 | #include <media/v4l2-dev.h> | ||
26 | #include <media/v4l2-device.h> | ||
27 | #include <media/v4l2-mediabus.h> | ||
28 | #include <media/videobuf2-core.h> | ||
29 | #include <media/s3c_camif.h> | ||
30 | |||
31 | #define S3C_CAMIF_DRIVER_NAME "s3c-camif" | ||
32 | #define CAMIF_REQ_BUFS_MIN 3 | ||
33 | #define CAMIF_MAX_OUT_BUFS 4 | ||
34 | #define CAMIF_MAX_PIX_WIDTH 4096 | ||
35 | #define CAMIF_MAX_PIX_HEIGHT 4096 | ||
36 | #define SCALER_MAX_RATIO 64 | ||
37 | #define CAMIF_DEF_WIDTH 640 | ||
38 | #define CAMIF_DEF_HEIGHT 480 | ||
39 | #define CAMIF_STOP_TIMEOUT 1500 /* ms */ | ||
40 | |||
41 | #define S3C244X_CAMIF_IP_REV 0x20 /* 2.0 */ | ||
42 | #define S3C2450_CAMIF_IP_REV 0x30 /* 3.0 - not implemented, not tested */ | ||
43 | #define S3C6400_CAMIF_IP_REV 0x31 /* 3.1 - not implemented, not tested */ | ||
44 | #define S3C6410_CAMIF_IP_REV 0x32 /* 3.2 */ | ||
45 | |||
46 | /* struct camif_vp::state */ | ||
47 | |||
48 | #define ST_VP_PENDING (1 << 0) | ||
49 | #define ST_VP_RUNNING (1 << 1) | ||
50 | #define ST_VP_STREAMING (1 << 2) | ||
51 | #define ST_VP_SENSOR_STREAMING (1 << 3) | ||
52 | |||
53 | #define ST_VP_ABORTING (1 << 4) | ||
54 | #define ST_VP_OFF (1 << 5) | ||
55 | #define ST_VP_LASTIRQ (1 << 6) | ||
56 | |||
57 | #define ST_VP_CONFIG (1 << 8) | ||
58 | |||
59 | #define CAMIF_SD_PAD_SINK 0 | ||
60 | #define CAMIF_SD_PAD_SOURCE_C 1 | ||
61 | #define CAMIF_SD_PAD_SOURCE_P 2 | ||
62 | #define CAMIF_SD_PADS_NUM 3 | ||
63 | |||
64 | enum img_fmt { | ||
65 | IMG_FMT_RGB565 = 0x0010, | ||
66 | IMG_FMT_RGB666, | ||
67 | IMG_FMT_XRGB8888, | ||
68 | IMG_FMT_YCBCR420 = 0x0020, | ||
69 | IMG_FMT_YCRCB420, | ||
70 | IMG_FMT_YCBCR422P, | ||
71 | IMG_FMT_YCBYCR422 = 0x0040, | ||
72 | IMG_FMT_YCRYCB422, | ||
73 | IMG_FMT_CBYCRY422, | ||
74 | IMG_FMT_CRYCBY422, | ||
75 | }; | ||
76 | |||
77 | #define img_fmt_is_rgb(x) ((x) & 0x10) | ||
78 | #define img_fmt_is_ycbcr(x) ((x) & 0x60) | ||
79 | |||
80 | /* Possible values for struct camif_fmt::flags */ | ||
81 | #define FMT_FL_S3C24XX_CODEC (1 << 0) | ||
82 | #define FMT_FL_S3C24XX_PREVIEW (1 << 1) | ||
83 | #define FMT_FL_S3C64XX (1 << 2) | ||
84 | |||
85 | /** | ||
86 | * struct camif_fmt - pixel format description | ||
87 | * @fourcc: fourcc code for this format, 0 if not applicable | ||
88 | * @color: a corresponding enum img_fmt | ||
89 | * @colplanes: number of physically contiguous data planes | ||
90 | * @flags: indicate for which SoCs revisions this format is valid | ||
91 | * @depth: bits per pixel (total) | ||
92 | * @ybpp: number of luminance bytes per pixel | ||
93 | */ | ||
94 | struct camif_fmt { | ||
95 | char *name; | ||
96 | u32 fourcc; | ||
97 | u32 color; | ||
98 | u16 colplanes; | ||
99 | u16 flags; | ||
100 | u8 depth; | ||
101 | u8 ybpp; | ||
102 | }; | ||
103 | |||
104 | /** | ||
105 | * struct camif_dma_offset - pixel offset information for DMA | ||
106 | * @initial: offset (in pixels) to first pixel | ||
107 | * @line: offset (in pixels) from end of line to start of next line | ||
108 | */ | ||
109 | struct camif_dma_offset { | ||
110 | int initial; | ||
111 | int line; | ||
112 | }; | ||
113 | |||
114 | /** | ||
115 | * struct camif_frame - source/target frame properties | ||
116 | * @f_width: full pixel width | ||
117 | * @f_height: full pixel height | ||
118 | * @rect: crop/composition rectangle | ||
119 | * @dma_offset: DMA offset configuration | ||
120 | */ | ||
121 | struct camif_frame { | ||
122 | u16 f_width; | ||
123 | u16 f_height; | ||
124 | struct v4l2_rect rect; | ||
125 | struct camif_dma_offset dma_offset; | ||
126 | }; | ||
127 | |||
128 | /* CAMIF clocks enumeration */ | ||
129 | enum { | ||
130 | CLK_GATE, | ||
131 | CLK_CAM, | ||
132 | CLK_MAX_NUM, | ||
133 | }; | ||
134 | |||
135 | struct vp_pix_limits { | ||
136 | u16 max_out_width; | ||
137 | u16 max_sc_out_width; | ||
138 | u16 out_width_align; | ||
139 | u16 max_height; | ||
140 | u8 min_out_width; | ||
141 | u16 out_hor_offset_align; | ||
142 | }; | ||
143 | |||
144 | struct camif_pix_limits { | ||
145 | u16 win_hor_offset_align; | ||
146 | }; | ||
147 | |||
148 | /** | ||
149 | * struct s3c_camif_variant - CAMIF variant structure | ||
150 | * @vp_pix_limits: pixel limits for the codec and preview paths | ||
151 | * @camif_pix_limits: pixel limits for the camera input interface | ||
152 | * @ip_revision: the CAMIF IP revision: 0x20 for s3c244x, 0x32 for s3c6410 | ||
153 | */ | ||
154 | struct s3c_camif_variant { | ||
155 | struct vp_pix_limits vp_pix_limits[2]; | ||
156 | struct camif_pix_limits pix_limits; | ||
157 | u8 ip_revision; | ||
158 | u8 has_img_effect; | ||
159 | unsigned int vp_offset; | ||
160 | }; | ||
161 | |||
162 | struct s3c_camif_drvdata { | ||
163 | const struct s3c_camif_variant *variant; | ||
164 | unsigned long bus_clk_freq; | ||
165 | }; | ||
166 | |||
167 | struct camif_scaler { | ||
168 | u8 scaleup_h; | ||
169 | u8 scaleup_v; | ||
170 | u8 copy; | ||
171 | u8 enable; | ||
172 | u32 h_shift; | ||
173 | u32 v_shift; | ||
174 | u32 pre_h_ratio; | ||
175 | u32 pre_v_ratio; | ||
176 | u32 pre_dst_width; | ||
177 | u32 pre_dst_height; | ||
178 | u32 main_h_ratio; | ||
179 | u32 main_v_ratio; | ||
180 | }; | ||
181 | |||
182 | struct camif_dev; | ||
183 | |||
184 | /** | ||
185 | * struct camif_vp - CAMIF data processing path structure (codec/preview) | ||
186 | * @irq_queue: interrupt handling waitqueue | ||
187 | * @irq: interrupt number for this data path | ||
188 | * @camif: pointer to the camif structure | ||
189 | * @pad: media pad for the video node | ||
190 | * @vdev video device | ||
191 | * @ctrl_handler: video node controls handler | ||
192 | * @owner: file handle that own the streaming | ||
193 | * @pending_buf_q: pending (empty) buffers queue head | ||
194 | * @active_buf_q: active (being written) buffers queue head | ||
195 | * @active_buffers: counter of buffer set up at the DMA engine | ||
196 | * @buf_index: identifier of a last empty buffer set up in H/W | ||
197 | * @frame_sequence: image frame sequence counter | ||
198 | * @reqbufs_count: the number of buffers requested | ||
199 | * @scaler: the scaler structure | ||
200 | * @out_fmt: pixel format at this video path output | ||
201 | * @payload: the output data frame payload size | ||
202 | * @out_frame: the output pixel resolution | ||
203 | * @state: the video path's state | ||
204 | * @fmt_flags: flags determining supported pixel formats | ||
205 | * @id: CAMIF id, 0 - codec, 1 - preview | ||
206 | * @rotation: current image rotation value | ||
207 | * @hflip: apply horizontal flip if set | ||
208 | * @vflip: apply vertical flip if set | ||
209 | */ | ||
210 | struct camif_vp { | ||
211 | wait_queue_head_t irq_queue; | ||
212 | int irq; | ||
213 | struct camif_dev *camif; | ||
214 | struct media_pad pad; | ||
215 | struct video_device vdev; | ||
216 | struct v4l2_ctrl_handler ctrl_handler; | ||
217 | struct v4l2_fh *owner; | ||
218 | struct vb2_queue vb_queue; | ||
219 | struct list_head pending_buf_q; | ||
220 | struct list_head active_buf_q; | ||
221 | unsigned int active_buffers; | ||
222 | unsigned int buf_index; | ||
223 | unsigned int frame_sequence; | ||
224 | unsigned int reqbufs_count; | ||
225 | struct camif_scaler scaler; | ||
226 | const struct camif_fmt *out_fmt; | ||
227 | unsigned int payload; | ||
228 | struct camif_frame out_frame; | ||
229 | unsigned int state; | ||
230 | u16 fmt_flags; | ||
231 | u8 id; | ||
232 | u8 rotation; | ||
233 | u8 hflip; | ||
234 | u8 vflip; | ||
235 | unsigned int offset; | ||
236 | }; | ||
237 | |||
238 | /* Video processing path enumeration */ | ||
239 | #define VP_CODEC 0 | ||
240 | #define VP_PREVIEW 1 | ||
241 | #define CAMIF_VP_NUM 2 | ||
242 | |||
243 | /** | ||
244 | * struct camif_dev - the CAMIF driver private data structure | ||
245 | * @media_dev: top-level media device structure | ||
246 | * @v4l2_dev: root v4l2_device | ||
247 | * @subdev: camera interface ("catchcam") subdev | ||
248 | * @mbus_fmt: camera input media bus format | ||
249 | * @camif_crop: camera input interface crop rectangle | ||
250 | * @pads: the camif subdev's media pads | ||
251 | * @stream_count: the camera interface streaming reference counter | ||
252 | * @sensor: image sensor data structure | ||
253 | * @m_pipeline: video entity pipeline description | ||
254 | * @ctrl_handler: v4l2 control handler (owned by @subdev) | ||
255 | * @test_pattern: test pattern controls | ||
256 | * @vp: video path (DMA) description (codec/preview) | ||
257 | * @alloc_ctx: memory buffer allocator context | ||
258 | * @variant: variant information for this device | ||
259 | * @dev: pointer to the CAMIF device struct | ||
260 | * @pdata: a copy of the driver's platform data | ||
261 | * @clock: clocks required for the CAMIF operation | ||
262 | * @lock: mutex protecting this data structure | ||
263 | * @slock: spinlock protecting CAMIF registers | ||
264 | * @io_base: start address of the mmaped CAMIF registers | ||
265 | */ | ||
266 | struct camif_dev { | ||
267 | struct media_device media_dev; | ||
268 | struct v4l2_device v4l2_dev; | ||
269 | struct v4l2_subdev subdev; | ||
270 | struct v4l2_mbus_framefmt mbus_fmt; | ||
271 | struct v4l2_rect camif_crop; | ||
272 | struct media_pad pads[CAMIF_SD_PADS_NUM]; | ||
273 | int stream_count; | ||
274 | |||
275 | struct cam_sensor { | ||
276 | struct v4l2_subdev *sd; | ||
277 | short power_count; | ||
278 | short stream_count; | ||
279 | } sensor; | ||
280 | struct media_pipeline *m_pipeline; | ||
281 | |||
282 | struct v4l2_ctrl_handler ctrl_handler; | ||
283 | struct v4l2_ctrl *ctrl_test_pattern; | ||
284 | struct { | ||
285 | struct v4l2_ctrl *ctrl_colorfx; | ||
286 | struct v4l2_ctrl *ctrl_colorfx_cbcr; | ||
287 | }; | ||
288 | u8 test_pattern; | ||
289 | u8 colorfx; | ||
290 | u8 colorfx_cb; | ||
291 | u8 colorfx_cr; | ||
292 | |||
293 | struct camif_vp vp[CAMIF_VP_NUM]; | ||
294 | struct vb2_alloc_ctx *alloc_ctx; | ||
295 | |||
296 | const struct s3c_camif_variant *variant; | ||
297 | struct device *dev; | ||
298 | struct s3c_camif_plat_data pdata; | ||
299 | struct clk *clock[CLK_MAX_NUM]; | ||
300 | struct mutex lock; | ||
301 | spinlock_t slock; | ||
302 | void __iomem *io_base; | ||
303 | }; | ||
304 | |||
305 | /** | ||
306 | * struct camif_addr - Y/Cb/Cr DMA start address structure | ||
307 | * @y: luminance plane dma address | ||
308 | * @cb: Cb plane dma address | ||
309 | * @cr: Cr plane dma address | ||
310 | */ | ||
311 | struct camif_addr { | ||
312 | dma_addr_t y; | ||
313 | dma_addr_t cb; | ||
314 | dma_addr_t cr; | ||
315 | }; | ||
316 | |||
317 | /** | ||
318 | * struct camif_buffer - the camif video buffer structure | ||
319 | * @vb: vb2 buffer | ||
320 | * @list: list head for the buffers queue | ||
321 | * @paddr: DMA start addresses | ||
322 | * @index: an identifier of this buffer at the DMA engine | ||
323 | */ | ||
324 | struct camif_buffer { | ||
325 | struct vb2_buffer vb; | ||
326 | struct list_head list; | ||
327 | struct camif_addr paddr; | ||
328 | unsigned int index; | ||
329 | }; | ||
330 | |||
331 | const struct camif_fmt *s3c_camif_find_format(struct camif_vp *vp, | ||
332 | const u32 *pixelformat, int index); | ||
333 | int s3c_camif_register_video_node(struct camif_dev *camif, int idx); | ||
334 | void s3c_camif_unregister_video_node(struct camif_dev *camif, int idx); | ||
335 | irqreturn_t s3c_camif_irq_handler(int irq, void *priv); | ||
336 | int s3c_camif_create_subdev(struct camif_dev *camif); | ||
337 | void s3c_camif_unregister_subdev(struct camif_dev *camif); | ||
338 | int s3c_camif_set_defaults(struct camif_dev *camif); | ||
339 | int s3c_camif_get_scaler_config(struct camif_vp *vp, | ||
340 | struct camif_scaler *scaler); | ||
341 | |||
342 | static inline void camif_active_queue_add(struct camif_vp *vp, | ||
343 | struct camif_buffer *buf) | ||
344 | { | ||
345 | list_add_tail(&buf->list, &vp->active_buf_q); | ||
346 | vp->active_buffers++; | ||
347 | } | ||
348 | |||
349 | static inline struct camif_buffer *camif_active_queue_pop( | ||
350 | struct camif_vp *vp) | ||
351 | { | ||
352 | struct camif_buffer *buf = list_first_entry(&vp->active_buf_q, | ||
353 | struct camif_buffer, list); | ||
354 | list_del(&buf->list); | ||
355 | vp->active_buffers--; | ||
356 | return buf; | ||
357 | } | ||
358 | |||
359 | static inline struct camif_buffer *camif_active_queue_peek( | ||
360 | struct camif_vp *vp, int index) | ||
361 | { | ||
362 | struct camif_buffer *tmp, *buf; | ||
363 | |||
364 | if (WARN_ON(list_empty(&vp->active_buf_q))) | ||
365 | return NULL; | ||
366 | |||
367 | list_for_each_entry_safe(buf, tmp, &vp->active_buf_q, list) { | ||
368 | if (buf->index == index) { | ||
369 | list_del(&buf->list); | ||
370 | vp->active_buffers--; | ||
371 | return buf; | ||
372 | } | ||
373 | } | ||
374 | |||
375 | return NULL; | ||
376 | } | ||
377 | |||
378 | static inline void camif_pending_queue_add(struct camif_vp *vp, | ||
379 | struct camif_buffer *buf) | ||
380 | { | ||
381 | list_add_tail(&buf->list, &vp->pending_buf_q); | ||
382 | } | ||
383 | |||
384 | static inline struct camif_buffer *camif_pending_queue_pop( | ||
385 | struct camif_vp *vp) | ||
386 | { | ||
387 | struct camif_buffer *buf = list_first_entry(&vp->pending_buf_q, | ||
388 | struct camif_buffer, list); | ||
389 | list_del(&buf->list); | ||
390 | return buf; | ||
391 | } | ||
392 | |||
393 | #endif /* CAMIF_CORE_H_ */ | ||
diff --git a/drivers/media/platform/s3c-camif/camif-regs.c b/drivers/media/platform/s3c-camif/camif-regs.c new file mode 100644 index 000000000000..1a3b4fc05ec6 --- /dev/null +++ b/drivers/media/platform/s3c-camif/camif-regs.c | |||
@@ -0,0 +1,606 @@ | |||
1 | /* | ||
2 | * Samsung s3c24xx/s3c64xx SoC CAMIF driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com> | ||
5 | * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.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/delay.h> | ||
14 | #include "camif-regs.h" | ||
15 | |||
16 | #define camif_write(_camif, _off, _val) writel(_val, (_camif)->io_base + (_off)) | ||
17 | #define camif_read(_camif, _off) readl((_camif)->io_base + (_off)) | ||
18 | |||
19 | void camif_hw_reset(struct camif_dev *camif) | ||
20 | { | ||
21 | u32 cfg; | ||
22 | |||
23 | cfg = camif_read(camif, S3C_CAMIF_REG_CISRCFMT); | ||
24 | cfg |= CISRCFMT_ITU601_8BIT; | ||
25 | camif_write(camif, S3C_CAMIF_REG_CISRCFMT, cfg); | ||
26 | |||
27 | /* S/W reset */ | ||
28 | cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL); | ||
29 | cfg |= CIGCTRL_SWRST; | ||
30 | if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) | ||
31 | cfg |= CIGCTRL_IRQ_LEVEL; | ||
32 | camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg); | ||
33 | udelay(10); | ||
34 | |||
35 | cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL); | ||
36 | cfg &= ~CIGCTRL_SWRST; | ||
37 | camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg); | ||
38 | udelay(10); | ||
39 | } | ||
40 | |||
41 | void camif_hw_clear_pending_irq(struct camif_vp *vp) | ||
42 | { | ||
43 | u32 cfg = camif_read(vp->camif, S3C_CAMIF_REG_CIGCTRL); | ||
44 | cfg |= CIGCTRL_IRQ_CLR(vp->id); | ||
45 | camif_write(vp->camif, S3C_CAMIF_REG_CIGCTRL, cfg); | ||
46 | } | ||
47 | |||
48 | /* | ||
49 | * Sets video test pattern (off, color bar, horizontal or vertical gradient). | ||
50 | * External sensor pixel clock must be active for the test pattern to work. | ||
51 | */ | ||
52 | void camif_hw_set_test_pattern(struct camif_dev *camif, unsigned int pattern) | ||
53 | { | ||
54 | u32 cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL); | ||
55 | cfg &= ~CIGCTRL_TESTPATTERN_MASK; | ||
56 | cfg |= (pattern << 27); | ||
57 | camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg); | ||
58 | } | ||
59 | |||
60 | void camif_hw_set_effect(struct camif_dev *camif, unsigned int effect, | ||
61 | unsigned int cr, unsigned int cb) | ||
62 | { | ||
63 | static const struct v4l2_control colorfx[] = { | ||
64 | { V4L2_COLORFX_NONE, CIIMGEFF_FIN_BYPASS }, | ||
65 | { V4L2_COLORFX_BW, CIIMGEFF_FIN_ARBITRARY }, | ||
66 | { V4L2_COLORFX_SEPIA, CIIMGEFF_FIN_ARBITRARY }, | ||
67 | { V4L2_COLORFX_NEGATIVE, CIIMGEFF_FIN_NEGATIVE }, | ||
68 | { V4L2_COLORFX_ART_FREEZE, CIIMGEFF_FIN_ARTFREEZE }, | ||
69 | { V4L2_COLORFX_EMBOSS, CIIMGEFF_FIN_EMBOSSING }, | ||
70 | { V4L2_COLORFX_SILHOUETTE, CIIMGEFF_FIN_SILHOUETTE }, | ||
71 | { V4L2_COLORFX_SET_CBCR, CIIMGEFF_FIN_ARBITRARY }, | ||
72 | }; | ||
73 | unsigned int i, cfg; | ||
74 | |||
75 | for (i = 0; i < ARRAY_SIZE(colorfx); i++) | ||
76 | if (colorfx[i].id == effect) | ||
77 | break; | ||
78 | |||
79 | if (i == ARRAY_SIZE(colorfx)) | ||
80 | return; | ||
81 | |||
82 | cfg = camif_read(camif, S3C_CAMIF_REG_CIIMGEFF(camif->vp->offset)); | ||
83 | /* Set effect */ | ||
84 | cfg &= ~CIIMGEFF_FIN_MASK; | ||
85 | cfg |= colorfx[i].value; | ||
86 | /* Set both paths */ | ||
87 | if (camif->variant->ip_revision >= S3C6400_CAMIF_IP_REV) { | ||
88 | if (effect == V4L2_COLORFX_NONE) | ||
89 | cfg &= ~CIIMGEFF_IE_ENABLE_MASK; | ||
90 | else | ||
91 | cfg |= CIIMGEFF_IE_ENABLE_MASK; | ||
92 | } | ||
93 | cfg &= ~CIIMGEFF_PAT_CBCR_MASK; | ||
94 | cfg |= cr | (cb << 13); | ||
95 | camif_write(camif, S3C_CAMIF_REG_CIIMGEFF(camif->vp->offset), cfg); | ||
96 | } | ||
97 | |||
98 | static const u32 src_pixfmt_map[8][2] = { | ||
99 | { V4L2_MBUS_FMT_YUYV8_2X8, CISRCFMT_ORDER422_YCBYCR }, | ||
100 | { V4L2_MBUS_FMT_YVYU8_2X8, CISRCFMT_ORDER422_YCRYCB }, | ||
101 | { V4L2_MBUS_FMT_UYVY8_2X8, CISRCFMT_ORDER422_CBYCRY }, | ||
102 | { V4L2_MBUS_FMT_VYUY8_2X8, CISRCFMT_ORDER422_CRYCBY }, | ||
103 | }; | ||
104 | |||
105 | /* Set camera input pixel format and resolution */ | ||
106 | void camif_hw_set_source_format(struct camif_dev *camif) | ||
107 | { | ||
108 | struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt; | ||
109 | unsigned int i = ARRAY_SIZE(src_pixfmt_map); | ||
110 | u32 cfg; | ||
111 | |||
112 | while (i-- >= 0) { | ||
113 | if (src_pixfmt_map[i][0] == mf->code) | ||
114 | break; | ||
115 | } | ||
116 | |||
117 | if (i == 0 && src_pixfmt_map[i][0] != mf->code) { | ||
118 | dev_err(camif->dev, | ||
119 | "Unsupported pixel code, falling back to %#08x\n", | ||
120 | src_pixfmt_map[i][0]); | ||
121 | } | ||
122 | |||
123 | cfg = camif_read(camif, S3C_CAMIF_REG_CISRCFMT); | ||
124 | cfg &= ~(CISRCFMT_ORDER422_MASK | CISRCFMT_SIZE_CAM_MASK); | ||
125 | cfg |= (mf->width << 16) | mf->height; | ||
126 | cfg |= src_pixfmt_map[i][1]; | ||
127 | camif_write(camif, S3C_CAMIF_REG_CISRCFMT, cfg); | ||
128 | } | ||
129 | |||
130 | /* Set the camera host input window offsets (cropping) */ | ||
131 | void camif_hw_set_camera_crop(struct camif_dev *camif) | ||
132 | { | ||
133 | struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt; | ||
134 | struct v4l2_rect *crop = &camif->camif_crop; | ||
135 | u32 hoff2, voff2; | ||
136 | u32 cfg; | ||
137 | |||
138 | /* Note: s3c244x requirement: left = f_width - rect.width / 2 */ | ||
139 | cfg = camif_read(camif, S3C_CAMIF_REG_CIWDOFST); | ||
140 | cfg &= ~(CIWDOFST_OFST_MASK | CIWDOFST_WINOFSEN); | ||
141 | cfg |= (crop->left << 16) | crop->top; | ||
142 | if (crop->left != 0 || crop->top != 0) | ||
143 | cfg |= CIWDOFST_WINOFSEN; | ||
144 | camif_write(camif, S3C_CAMIF_REG_CIWDOFST, cfg); | ||
145 | |||
146 | if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) { | ||
147 | hoff2 = mf->width - crop->width - crop->left; | ||
148 | voff2 = mf->height - crop->height - crop->top; | ||
149 | cfg = (hoff2 << 16) | voff2; | ||
150 | camif_write(camif, S3C_CAMIF_REG_CIWDOFST2, cfg); | ||
151 | } | ||
152 | } | ||
153 | |||
154 | void camif_hw_clear_fifo_overflow(struct camif_vp *vp) | ||
155 | { | ||
156 | struct camif_dev *camif = vp->camif; | ||
157 | u32 cfg; | ||
158 | |||
159 | cfg = camif_read(camif, S3C_CAMIF_REG_CIWDOFST); | ||
160 | if (vp->id == 0) | ||
161 | cfg |= (CIWDOFST_CLROVCOFIY | CIWDOFST_CLROVCOFICB | | ||
162 | CIWDOFST_CLROVCOFICR); | ||
163 | else | ||
164 | cfg |= (/* CIWDOFST_CLROVPRFIY | */ CIWDOFST_CLROVPRFICB | | ||
165 | CIWDOFST_CLROVPRFICR); | ||
166 | camif_write(camif, S3C_CAMIF_REG_CIWDOFST, cfg); | ||
167 | } | ||
168 | |||
169 | /* Set video bus signals polarity */ | ||
170 | void camif_hw_set_camera_bus(struct camif_dev *camif) | ||
171 | { | ||
172 | unsigned int flags = camif->pdata.sensor.flags; | ||
173 | |||
174 | u32 cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL); | ||
175 | |||
176 | cfg &= ~(CIGCTRL_INVPOLPCLK | CIGCTRL_INVPOLVSYNC | | ||
177 | CIGCTRL_INVPOLHREF | CIGCTRL_INVPOLFIELD); | ||
178 | |||
179 | if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) | ||
180 | cfg |= CIGCTRL_INVPOLPCLK; | ||
181 | |||
182 | if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) | ||
183 | cfg |= CIGCTRL_INVPOLVSYNC; | ||
184 | /* | ||
185 | * HREF is normally high during frame active data | ||
186 | * transmission and low during horizontal synchronization | ||
187 | * period. Thus HREF active high means HSYNC active low. | ||
188 | */ | ||
189 | if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) | ||
190 | cfg |= CIGCTRL_INVPOLHREF; /* HREF active low */ | ||
191 | |||
192 | if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) { | ||
193 | if (flags & V4L2_MBUS_FIELD_EVEN_LOW) | ||
194 | cfg |= CIGCTRL_INVPOLFIELD; | ||
195 | cfg |= CIGCTRL_FIELDMODE; | ||
196 | } | ||
197 | |||
198 | pr_debug("Setting CIGCTRL to: %#x\n", cfg); | ||
199 | |||
200 | camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg); | ||
201 | } | ||
202 | |||
203 | void camif_hw_set_output_addr(struct camif_vp *vp, | ||
204 | struct camif_addr *paddr, int i) | ||
205 | { | ||
206 | struct camif_dev *camif = vp->camif; | ||
207 | |||
208 | camif_write(camif, S3C_CAMIF_REG_CIYSA(vp->id, i), paddr->y); | ||
209 | if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV | ||
210 | || vp->id == VP_CODEC) { | ||
211 | camif_write(camif, S3C_CAMIF_REG_CICBSA(vp->id, i), | ||
212 | paddr->cb); | ||
213 | camif_write(camif, S3C_CAMIF_REG_CICRSA(vp->id, i), | ||
214 | paddr->cr); | ||
215 | } | ||
216 | |||
217 | pr_debug("dst_buf[%d]: %#X, cb: %#X, cr: %#X\n", | ||
218 | i, paddr->y, paddr->cb, paddr->cr); | ||
219 | } | ||
220 | |||
221 | static void camif_hw_set_out_dma_size(struct camif_vp *vp) | ||
222 | { | ||
223 | struct camif_frame *frame = &vp->out_frame; | ||
224 | u32 cfg; | ||
225 | |||
226 | cfg = camif_read(vp->camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset)); | ||
227 | cfg &= ~CITRGFMT_TARGETSIZE_MASK; | ||
228 | cfg |= (frame->f_width << 16) | frame->f_height; | ||
229 | camif_write(vp->camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset), cfg); | ||
230 | } | ||
231 | |||
232 | static void camif_get_dma_burst(u32 width, u32 ybpp, u32 *mburst, u32 *rburst) | ||
233 | { | ||
234 | unsigned int nwords = width * ybpp / 4; | ||
235 | unsigned int div, rem; | ||
236 | |||
237 | if (WARN_ON(width < 8 || (width * ybpp) & 7)) | ||
238 | return; | ||
239 | |||
240 | for (div = 16; div >= 2; div /= 2) { | ||
241 | if (nwords < div) | ||
242 | continue; | ||
243 | |||
244 | rem = nwords & (div - 1); | ||
245 | if (rem == 0) { | ||
246 | *mburst = div; | ||
247 | *rburst = div; | ||
248 | break; | ||
249 | } | ||
250 | if (rem == div / 2 || rem == div / 4) { | ||
251 | *mburst = div; | ||
252 | *rburst = rem; | ||
253 | break; | ||
254 | } | ||
255 | } | ||
256 | } | ||
257 | |||
258 | void camif_hw_set_output_dma(struct camif_vp *vp) | ||
259 | { | ||
260 | struct camif_dev *camif = vp->camif; | ||
261 | struct camif_frame *frame = &vp->out_frame; | ||
262 | const struct camif_fmt *fmt = vp->out_fmt; | ||
263 | unsigned int ymburst = 0, yrburst = 0; | ||
264 | u32 cfg; | ||
265 | |||
266 | camif_hw_set_out_dma_size(vp); | ||
267 | |||
268 | if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) { | ||
269 | struct camif_dma_offset *offset = &frame->dma_offset; | ||
270 | /* Set the input dma offsets. */ | ||
271 | cfg = S3C_CISS_OFFS_INITIAL(offset->initial); | ||
272 | cfg |= S3C_CISS_OFFS_LINE(offset->line); | ||
273 | camif_write(camif, S3C_CAMIF_REG_CISSY(vp->id), cfg); | ||
274 | camif_write(camif, S3C_CAMIF_REG_CISSCB(vp->id), cfg); | ||
275 | camif_write(camif, S3C_CAMIF_REG_CISSCR(vp->id), cfg); | ||
276 | } | ||
277 | |||
278 | /* Configure DMA burst values */ | ||
279 | camif_get_dma_burst(frame->rect.width, fmt->ybpp, &ymburst, &yrburst); | ||
280 | |||
281 | cfg = camif_read(camif, S3C_CAMIF_REG_CICTRL(vp->id, vp->offset)); | ||
282 | cfg &= ~CICTRL_BURST_MASK; | ||
283 | |||
284 | cfg |= CICTRL_YBURST1(ymburst) | CICTRL_YBURST2(yrburst); | ||
285 | cfg |= CICTRL_CBURST1(ymburst / 2) | CICTRL_CBURST2(yrburst / 2); | ||
286 | |||
287 | camif_write(camif, S3C_CAMIF_REG_CICTRL(vp->id, vp->offset), cfg); | ||
288 | |||
289 | pr_debug("ymburst: %u, yrburst: %u\n", ymburst, yrburst); | ||
290 | } | ||
291 | |||
292 | void camif_hw_set_input_path(struct camif_vp *vp) | ||
293 | { | ||
294 | u32 cfg = camif_read(vp->camif, S3C_CAMIF_REG_MSCTRL(vp->id)); | ||
295 | cfg &= ~MSCTRL_SEL_DMA_CAM; | ||
296 | camif_write(vp->camif, S3C_CAMIF_REG_MSCTRL(vp->id), cfg); | ||
297 | } | ||
298 | |||
299 | void camif_hw_set_target_format(struct camif_vp *vp) | ||
300 | { | ||
301 | struct camif_dev *camif = vp->camif; | ||
302 | struct camif_frame *frame = &vp->out_frame; | ||
303 | u32 cfg; | ||
304 | |||
305 | pr_debug("fw: %d, fh: %d color: %d\n", frame->f_width, | ||
306 | frame->f_height, vp->out_fmt->color); | ||
307 | |||
308 | cfg = camif_read(camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset)); | ||
309 | cfg &= ~CITRGFMT_TARGETSIZE_MASK; | ||
310 | |||
311 | if (camif->variant->ip_revision == S3C244X_CAMIF_IP_REV) { | ||
312 | /* We currently support only YCbCr 4:2:2 at the camera input */ | ||
313 | cfg |= CITRGFMT_IN422; | ||
314 | cfg &= ~CITRGFMT_OUT422; | ||
315 | if (vp->out_fmt->color == IMG_FMT_YCBCR422P) | ||
316 | cfg |= CITRGFMT_OUT422; | ||
317 | } else { | ||
318 | cfg &= ~CITRGFMT_OUTFORMAT_MASK; | ||
319 | switch (vp->out_fmt->color) { | ||
320 | case IMG_FMT_RGB565...IMG_FMT_XRGB8888: | ||
321 | cfg |= CITRGFMT_OUTFORMAT_RGB; | ||
322 | break; | ||
323 | case IMG_FMT_YCBCR420...IMG_FMT_YCRCB420: | ||
324 | cfg |= CITRGFMT_OUTFORMAT_YCBCR420; | ||
325 | break; | ||
326 | case IMG_FMT_YCBCR422P: | ||
327 | cfg |= CITRGFMT_OUTFORMAT_YCBCR422; | ||
328 | break; | ||
329 | case IMG_FMT_YCBYCR422...IMG_FMT_CRYCBY422: | ||
330 | cfg |= CITRGFMT_OUTFORMAT_YCBCR422I; | ||
331 | break; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | /* Rotation is only supported by s3c64xx */ | ||
336 | if (vp->rotation == 90 || vp->rotation == 270) | ||
337 | cfg |= (frame->f_height << 16) | frame->f_width; | ||
338 | else | ||
339 | cfg |= (frame->f_width << 16) | frame->f_height; | ||
340 | camif_write(camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset), cfg); | ||
341 | |||
342 | /* Target area, output pixel width * height */ | ||
343 | cfg = camif_read(camif, S3C_CAMIF_REG_CITAREA(vp->id, vp->offset)); | ||
344 | cfg &= ~CITAREA_MASK; | ||
345 | cfg |= (frame->f_width * frame->f_height); | ||
346 | camif_write(camif, S3C_CAMIF_REG_CITAREA(vp->id, vp->offset), cfg); | ||
347 | } | ||
348 | |||
349 | void camif_hw_set_flip(struct camif_vp *vp) | ||
350 | { | ||
351 | u32 cfg = camif_read(vp->camif, | ||
352 | S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset)); | ||
353 | |||
354 | cfg &= ~CITRGFMT_FLIP_MASK; | ||
355 | |||
356 | if (vp->hflip) | ||
357 | cfg |= CITRGFMT_FLIP_Y_MIRROR; | ||
358 | if (vp->vflip) | ||
359 | cfg |= CITRGFMT_FLIP_X_MIRROR; | ||
360 | |||
361 | camif_write(vp->camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset), cfg); | ||
362 | } | ||
363 | |||
364 | static void camif_hw_set_prescaler(struct camif_vp *vp) | ||
365 | { | ||
366 | struct camif_dev *camif = vp->camif; | ||
367 | struct camif_scaler *sc = &vp->scaler; | ||
368 | u32 cfg, shfactor, addr; | ||
369 | |||
370 | addr = S3C_CAMIF_REG_CISCPRERATIO(vp->id, vp->offset); | ||
371 | |||
372 | shfactor = 10 - (sc->h_shift + sc->v_shift); | ||
373 | cfg = shfactor << 28; | ||
374 | |||
375 | cfg |= (sc->pre_h_ratio << 16) | sc->pre_v_ratio; | ||
376 | camif_write(camif, addr, cfg); | ||
377 | |||
378 | cfg = (sc->pre_dst_width << 16) | sc->pre_dst_height; | ||
379 | camif_write(camif, S3C_CAMIF_REG_CISCPREDST(vp->id, vp->offset), cfg); | ||
380 | } | ||
381 | |||
382 | void camif_s3c244x_hw_set_scaler(struct camif_vp *vp) | ||
383 | { | ||
384 | struct camif_dev *camif = vp->camif; | ||
385 | struct camif_scaler *scaler = &vp->scaler; | ||
386 | unsigned int color = vp->out_fmt->color; | ||
387 | u32 cfg; | ||
388 | |||
389 | camif_hw_set_prescaler(vp); | ||
390 | |||
391 | cfg = camif_read(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset)); | ||
392 | |||
393 | cfg &= ~(CISCCTRL_SCALEUP_MASK | CISCCTRL_SCALERBYPASS | | ||
394 | CISCCTRL_MAIN_RATIO_MASK | CIPRSCCTRL_RGB_FORMAT_24BIT); | ||
395 | |||
396 | if (scaler->enable) { | ||
397 | if (scaler->scaleup_h) { | ||
398 | if (vp->id == VP_CODEC) | ||
399 | cfg |= CISCCTRL_SCALEUP_H; | ||
400 | else | ||
401 | cfg |= CIPRSCCTRL_SCALEUP_H; | ||
402 | } | ||
403 | if (scaler->scaleup_v) { | ||
404 | if (vp->id == VP_CODEC) | ||
405 | cfg |= CISCCTRL_SCALEUP_V; | ||
406 | else | ||
407 | cfg |= CIPRSCCTRL_SCALEUP_V; | ||
408 | } | ||
409 | } else { | ||
410 | if (vp->id == VP_CODEC) | ||
411 | cfg |= CISCCTRL_SCALERBYPASS; | ||
412 | } | ||
413 | |||
414 | cfg |= ((scaler->main_h_ratio & 0x1ff) << 16); | ||
415 | cfg |= scaler->main_v_ratio & 0x1ff; | ||
416 | |||
417 | if (vp->id == VP_PREVIEW) { | ||
418 | if (color == IMG_FMT_XRGB8888) | ||
419 | cfg |= CIPRSCCTRL_RGB_FORMAT_24BIT; | ||
420 | cfg |= CIPRSCCTRL_SAMPLE; | ||
421 | } | ||
422 | |||
423 | camif_write(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset), cfg); | ||
424 | |||
425 | pr_debug("main: h_ratio: %#x, v_ratio: %#x", | ||
426 | scaler->main_h_ratio, scaler->main_v_ratio); | ||
427 | } | ||
428 | |||
429 | void camif_s3c64xx_hw_set_scaler(struct camif_vp *vp) | ||
430 | { | ||
431 | struct camif_dev *camif = vp->camif; | ||
432 | struct camif_scaler *scaler = &vp->scaler; | ||
433 | unsigned int color = vp->out_fmt->color; | ||
434 | u32 cfg; | ||
435 | |||
436 | camif_hw_set_prescaler(vp); | ||
437 | |||
438 | cfg = camif_read(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset)); | ||
439 | |||
440 | cfg &= ~(CISCCTRL_CSCR2Y_WIDE | CISCCTRL_CSCY2R_WIDE | ||
441 | | CISCCTRL_SCALEUP_H | CISCCTRL_SCALEUP_V | ||
442 | | CISCCTRL_SCALERBYPASS | CISCCTRL_ONE2ONE | ||
443 | | CISCCTRL_INRGB_FMT_MASK | CISCCTRL_OUTRGB_FMT_MASK | ||
444 | | CISCCTRL_INTERLACE | CISCCTRL_EXTRGB_EXTENSION | ||
445 | | CISCCTRL_MAIN_RATIO_MASK); | ||
446 | |||
447 | cfg |= (CISCCTRL_CSCR2Y_WIDE | CISCCTRL_CSCY2R_WIDE); | ||
448 | |||
449 | if (!scaler->enable) { | ||
450 | cfg |= CISCCTRL_SCALERBYPASS; | ||
451 | } else { | ||
452 | if (scaler->scaleup_h) | ||
453 | cfg |= CISCCTRL_SCALEUP_H; | ||
454 | if (scaler->scaleup_v) | ||
455 | cfg |= CISCCTRL_SCALEUP_V; | ||
456 | if (scaler->copy) | ||
457 | cfg |= CISCCTRL_ONE2ONE; | ||
458 | } | ||
459 | |||
460 | switch (color) { | ||
461 | case IMG_FMT_RGB666: | ||
462 | cfg |= CISCCTRL_OUTRGB_FMT_RGB666; | ||
463 | break; | ||
464 | case IMG_FMT_XRGB8888: | ||
465 | cfg |= CISCCTRL_OUTRGB_FMT_RGB888; | ||
466 | break; | ||
467 | } | ||
468 | |||
469 | cfg |= (scaler->main_h_ratio & 0x1ff) << 16; | ||
470 | cfg |= scaler->main_v_ratio & 0x1ff; | ||
471 | |||
472 | camif_write(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset), cfg); | ||
473 | |||
474 | pr_debug("main: h_ratio: %#x, v_ratio: %#x", | ||
475 | scaler->main_h_ratio, scaler->main_v_ratio); | ||
476 | } | ||
477 | |||
478 | void camif_hw_set_scaler(struct camif_vp *vp) | ||
479 | { | ||
480 | unsigned int ip_rev = vp->camif->variant->ip_revision; | ||
481 | |||
482 | if (ip_rev == S3C244X_CAMIF_IP_REV) | ||
483 | camif_s3c244x_hw_set_scaler(vp); | ||
484 | else | ||
485 | camif_s3c64xx_hw_set_scaler(vp); | ||
486 | } | ||
487 | |||
488 | void camif_hw_enable_scaler(struct camif_vp *vp, bool on) | ||
489 | { | ||
490 | u32 addr = S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset); | ||
491 | u32 cfg; | ||
492 | |||
493 | cfg = camif_read(vp->camif, addr); | ||
494 | if (on) | ||
495 | cfg |= CISCCTRL_SCALERSTART; | ||
496 | else | ||
497 | cfg &= ~CISCCTRL_SCALERSTART; | ||
498 | camif_write(vp->camif, addr, cfg); | ||
499 | } | ||
500 | |||
501 | void camif_hw_set_lastirq(struct camif_vp *vp, int enable) | ||
502 | { | ||
503 | u32 addr = S3C_CAMIF_REG_CICTRL(vp->id, vp->offset); | ||
504 | u32 cfg; | ||
505 | |||
506 | cfg = camif_read(vp->camif, addr); | ||
507 | if (enable) | ||
508 | cfg |= CICTRL_LASTIRQ_ENABLE; | ||
509 | else | ||
510 | cfg &= ~CICTRL_LASTIRQ_ENABLE; | ||
511 | camif_write(vp->camif, addr, cfg); | ||
512 | } | ||
513 | |||
514 | void camif_hw_enable_capture(struct camif_vp *vp) | ||
515 | { | ||
516 | struct camif_dev *camif = vp->camif; | ||
517 | u32 cfg; | ||
518 | |||
519 | cfg = camif_read(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset)); | ||
520 | camif->stream_count++; | ||
521 | |||
522 | if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) | ||
523 | cfg |= CIIMGCPT_CPT_FREN_ENABLE(vp->id); | ||
524 | |||
525 | if (vp->scaler.enable) | ||
526 | cfg |= CIIMGCPT_IMGCPTEN_SC(vp->id); | ||
527 | |||
528 | if (camif->stream_count == 1) | ||
529 | cfg |= CIIMGCPT_IMGCPTEN; | ||
530 | |||
531 | camif_write(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset), cfg); | ||
532 | |||
533 | pr_debug("CIIMGCPT: %#x, camif->stream_count: %d\n", | ||
534 | cfg, camif->stream_count); | ||
535 | } | ||
536 | |||
537 | void camif_hw_disable_capture(struct camif_vp *vp) | ||
538 | { | ||
539 | struct camif_dev *camif = vp->camif; | ||
540 | u32 cfg; | ||
541 | |||
542 | cfg = camif_read(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset)); | ||
543 | cfg &= ~CIIMGCPT_IMGCPTEN_SC(vp->id); | ||
544 | |||
545 | if (WARN_ON(--(camif->stream_count) < 0)) | ||
546 | camif->stream_count = 0; | ||
547 | |||
548 | if (camif->stream_count == 0) | ||
549 | cfg &= ~CIIMGCPT_IMGCPTEN; | ||
550 | |||
551 | pr_debug("CIIMGCPT: %#x, camif->stream_count: %d\n", | ||
552 | cfg, camif->stream_count); | ||
553 | |||
554 | camif_write(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset), cfg); | ||
555 | } | ||
556 | |||
557 | void camif_hw_dump_regs(struct camif_dev *camif, const char *label) | ||
558 | { | ||
559 | struct { | ||
560 | u32 offset; | ||
561 | const char * const name; | ||
562 | } registers[] = { | ||
563 | { S3C_CAMIF_REG_CISRCFMT, "CISRCFMT" }, | ||
564 | { S3C_CAMIF_REG_CIWDOFST, "CIWDOFST" }, | ||
565 | { S3C_CAMIF_REG_CIGCTRL, "CIGCTRL" }, | ||
566 | { S3C_CAMIF_REG_CIWDOFST2, "CIWDOFST2" }, | ||
567 | { S3C_CAMIF_REG_CIYSA(0, 0), "CICOYSA0" }, | ||
568 | { S3C_CAMIF_REG_CICBSA(0, 0), "CICOCBSA0" }, | ||
569 | { S3C_CAMIF_REG_CICRSA(0, 0), "CICOCRSA0" }, | ||
570 | { S3C_CAMIF_REG_CIYSA(0, 1), "CICOYSA1" }, | ||
571 | { S3C_CAMIF_REG_CICBSA(0, 1), "CICOCBSA1" }, | ||
572 | { S3C_CAMIF_REG_CICRSA(0, 1), "CICOCRSA1" }, | ||
573 | { S3C_CAMIF_REG_CIYSA(0, 2), "CICOYSA2" }, | ||
574 | { S3C_CAMIF_REG_CICBSA(0, 2), "CICOCBSA2" }, | ||
575 | { S3C_CAMIF_REG_CICRSA(0, 2), "CICOCRSA2" }, | ||
576 | { S3C_CAMIF_REG_CIYSA(0, 3), "CICOYSA3" }, | ||
577 | { S3C_CAMIF_REG_CICBSA(0, 3), "CICOCBSA3" }, | ||
578 | { S3C_CAMIF_REG_CICRSA(0, 3), "CICOCRSA3" }, | ||
579 | { S3C_CAMIF_REG_CIYSA(1, 0), "CIPRYSA0" }, | ||
580 | { S3C_CAMIF_REG_CIYSA(1, 1), "CIPRYSA1" }, | ||
581 | { S3C_CAMIF_REG_CIYSA(1, 2), "CIPRYSA2" }, | ||
582 | { S3C_CAMIF_REG_CIYSA(1, 3), "CIPRYSA3" }, | ||
583 | { S3C_CAMIF_REG_CITRGFMT(0, 0), "CICOTRGFMT" }, | ||
584 | { S3C_CAMIF_REG_CITRGFMT(1, 0), "CIPRTRGFMT" }, | ||
585 | { S3C_CAMIF_REG_CICTRL(0, 0), "CICOCTRL" }, | ||
586 | { S3C_CAMIF_REG_CICTRL(1, 0), "CIPRCTRL" }, | ||
587 | { S3C_CAMIF_REG_CISCPREDST(0, 0), "CICOSCPREDST" }, | ||
588 | { S3C_CAMIF_REG_CISCPREDST(1, 0), "CIPRSCPREDST" }, | ||
589 | { S3C_CAMIF_REG_CISCPRERATIO(0, 0), "CICOSCPRERATIO" }, | ||
590 | { S3C_CAMIF_REG_CISCPRERATIO(1, 0), "CIPRSCPRERATIO" }, | ||
591 | { S3C_CAMIF_REG_CISCCTRL(0, 0), "CICOSCCTRL" }, | ||
592 | { S3C_CAMIF_REG_CISCCTRL(1, 0), "CIPRSCCTRL" }, | ||
593 | { S3C_CAMIF_REG_CITAREA(0, 0), "CICOTAREA" }, | ||
594 | { S3C_CAMIF_REG_CITAREA(1, 0), "CIPRTAREA" }, | ||
595 | { S3C_CAMIF_REG_CISTATUS(0, 0), "CICOSTATUS" }, | ||
596 | { S3C_CAMIF_REG_CISTATUS(1, 0), "CIPRSTATUS" }, | ||
597 | { S3C_CAMIF_REG_CIIMGCPT(0), "CIIMGCPT" }, | ||
598 | }; | ||
599 | u32 i; | ||
600 | |||
601 | pr_info("--- %s ---\n", label); | ||
602 | for (i = 0; i < ARRAY_SIZE(registers); i++) { | ||
603 | u32 cfg = readl(camif->io_base + registers[i].offset); | ||
604 | printk(KERN_INFO "%s:\t0x%08x\n", registers[i].name, cfg); | ||
605 | } | ||
606 | } | ||
diff --git a/drivers/media/platform/s3c-camif/camif-regs.h b/drivers/media/platform/s3c-camif/camif-regs.h new file mode 100644 index 000000000000..af2d472ea1dd --- /dev/null +++ b/drivers/media/platform/s3c-camif/camif-regs.h | |||
@@ -0,0 +1,269 @@ | |||
1 | /* | ||
2 | * Register definition file for s3c24xx/s3c64xx SoC CAMIF driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com> | ||
5 | * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.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 | #ifndef CAMIF_REGS_H_ | ||
13 | #define CAMIF_REGS_H_ | ||
14 | |||
15 | #include "camif-core.h" | ||
16 | #include <media/s3c_camif.h> | ||
17 | |||
18 | /* | ||
19 | * The id argument indicates the processing path: | ||
20 | * id = 0 - codec (FIMC C), 1 - preview (FIMC P). | ||
21 | */ | ||
22 | |||
23 | /* Camera input format */ | ||
24 | #define S3C_CAMIF_REG_CISRCFMT 0x00 | ||
25 | #define CISRCFMT_ITU601_8BIT (1 << 31) | ||
26 | #define CISRCFMT_ITU656_8BIT (0 << 31) | ||
27 | #define CISRCFMT_ORDER422_YCBYCR (0 << 14) | ||
28 | #define CISRCFMT_ORDER422_YCRYCB (1 << 14) | ||
29 | #define CISRCFMT_ORDER422_CBYCRY (2 << 14) | ||
30 | #define CISRCFMT_ORDER422_CRYCBY (3 << 14) | ||
31 | #define CISRCFMT_ORDER422_MASK (3 << 14) | ||
32 | #define CISRCFMT_SIZE_CAM_MASK (0x1fff << 16 | 0x1fff) | ||
33 | |||
34 | /* Window offset */ | ||
35 | #define S3C_CAMIF_REG_CIWDOFST 0x04 | ||
36 | #define CIWDOFST_WINOFSEN (1 << 31) | ||
37 | #define CIWDOFST_CLROVCOFIY (1 << 30) | ||
38 | #define CIWDOFST_CLROVRLB_PR (1 << 28) | ||
39 | /* #define CIWDOFST_CLROVPRFIY (1 << 27) */ | ||
40 | #define CIWDOFST_CLROVCOFICB (1 << 15) | ||
41 | #define CIWDOFST_CLROVCOFICR (1 << 14) | ||
42 | #define CIWDOFST_CLROVPRFICB (1 << 13) | ||
43 | #define CIWDOFST_CLROVPRFICR (1 << 12) | ||
44 | #define CIWDOFST_OFST_MASK (0x7ff << 16 | 0x7ff) | ||
45 | |||
46 | /* Window offset 2 */ | ||
47 | #define S3C_CAMIF_REG_CIWDOFST2 0x14 | ||
48 | #define CIWDOFST2_OFST2_MASK (0xfff << 16 | 0xfff) | ||
49 | |||
50 | /* Global control */ | ||
51 | #define S3C_CAMIF_REG_CIGCTRL 0x08 | ||
52 | #define CIGCTRL_SWRST (1 << 31) | ||
53 | #define CIGCTRL_CAMRST (1 << 30) | ||
54 | #define CIGCTRL_TESTPATTERN_NORMAL (0 << 27) | ||
55 | #define CIGCTRL_TESTPATTERN_COLOR_BAR (1 << 27) | ||
56 | #define CIGCTRL_TESTPATTERN_HOR_INC (2 << 27) | ||
57 | #define CIGCTRL_TESTPATTERN_VER_INC (3 << 27) | ||
58 | #define CIGCTRL_TESTPATTERN_MASK (3 << 27) | ||
59 | #define CIGCTRL_INVPOLPCLK (1 << 26) | ||
60 | #define CIGCTRL_INVPOLVSYNC (1 << 25) | ||
61 | #define CIGCTRL_INVPOLHREF (1 << 24) | ||
62 | #define CIGCTRL_IRQ_OVFEN (1 << 22) | ||
63 | #define CIGCTRL_HREF_MASK (1 << 21) | ||
64 | #define CIGCTRL_IRQ_LEVEL (1 << 20) | ||
65 | /* IRQ_CLR_C, IRQ_CLR_P */ | ||
66 | #define CIGCTRL_IRQ_CLR(id) (1 << (19 - (id))) | ||
67 | #define CIGCTRL_FIELDMODE (1 << 2) | ||
68 | #define CIGCTRL_INVPOLFIELD (1 << 1) | ||
69 | #define CIGCTRL_CAM_INTERLACE (1 << 0) | ||
70 | |||
71 | /* Y DMA output frame start address. n = 0..3. */ | ||
72 | #define S3C_CAMIF_REG_CIYSA(id, n) (0x18 + (id) * 0x54 + (n) * 4) | ||
73 | /* Cb plane output DMA start address. n = 0..3. Only codec path. */ | ||
74 | #define S3C_CAMIF_REG_CICBSA(id, n) (0x28 + (id) * 0x54 + (n) * 4) | ||
75 | /* Cr plane output DMA start address. n = 0..3. Only codec path. */ | ||
76 | #define S3C_CAMIF_REG_CICRSA(id, n) (0x38 + (id) * 0x54 + (n) * 4) | ||
77 | |||
78 | /* CICOTRGFMT, CIPRTRGFMT - Target format */ | ||
79 | #define S3C_CAMIF_REG_CITRGFMT(id, _offs) (0x48 + (id) * (0x34 + (_offs))) | ||
80 | #define CITRGFMT_IN422 (1 << 31) /* only for s3c24xx */ | ||
81 | #define CITRGFMT_OUT422 (1 << 30) /* only for s3c24xx */ | ||
82 | #define CITRGFMT_OUTFORMAT_YCBCR420 (0 << 29) /* only for s3c6410 */ | ||
83 | #define CITRGFMT_OUTFORMAT_YCBCR422 (1 << 29) /* only for s3c6410 */ | ||
84 | #define CITRGFMT_OUTFORMAT_YCBCR422I (2 << 29) /* only for s3c6410 */ | ||
85 | #define CITRGFMT_OUTFORMAT_RGB (3 << 29) /* only for s3c6410 */ | ||
86 | #define CITRGFMT_OUTFORMAT_MASK (3 << 29) /* only for s3c6410 */ | ||
87 | #define CITRGFMT_TARGETHSIZE(x) ((x) << 16) | ||
88 | #define CITRGFMT_FLIP_NORMAL (0 << 14) | ||
89 | #define CITRGFMT_FLIP_X_MIRROR (1 << 14) | ||
90 | #define CITRGFMT_FLIP_Y_MIRROR (2 << 14) | ||
91 | #define CITRGFMT_FLIP_180 (3 << 14) | ||
92 | #define CITRGFMT_FLIP_MASK (3 << 14) | ||
93 | /* Preview path only */ | ||
94 | #define CITRGFMT_ROT90_PR (1 << 13) | ||
95 | #define CITRGFMT_TARGETVSIZE(x) ((x) << 0) | ||
96 | #define CITRGFMT_TARGETSIZE_MASK ((0x1fff << 16) | 0x1fff) | ||
97 | |||
98 | /* CICOCTRL, CIPRCTRL. Output DMA control. */ | ||
99 | #define S3C_CAMIF_REG_CICTRL(id, _offs) (0x4c + (id) * (0x34 + (_offs))) | ||
100 | #define CICTRL_BURST_MASK (0xfffff << 4) | ||
101 | /* xBURSTn - 5-bits width */ | ||
102 | #define CICTRL_YBURST1(x) ((x) << 19) | ||
103 | #define CICTRL_YBURST2(x) ((x) << 14) | ||
104 | #define CICTRL_RGBBURST1(x) ((x) << 19) | ||
105 | #define CICTRL_RGBBURST2(x) ((x) << 14) | ||
106 | #define CICTRL_CBURST1(x) ((x) << 9) | ||
107 | #define CICTRL_CBURST2(x) ((x) << 4) | ||
108 | #define CICTRL_LASTIRQ_ENABLE (1 << 2) | ||
109 | #define CICTRL_ORDER422_MASK (3 << 0) | ||
110 | |||
111 | /* CICOSCPRERATIO, CIPRSCPRERATIO. Pre-scaler control 1. */ | ||
112 | #define S3C_CAMIF_REG_CISCPRERATIO(id, _offs) (0x50 + (id) * (0x34 + (_offs))) | ||
113 | |||
114 | /* CICOSCPREDST, CIPRSCPREDST. Pre-scaler control 2. */ | ||
115 | #define S3C_CAMIF_REG_CISCPREDST(id, _offs) (0x54 + (id) * (0x34 + (_offs))) | ||
116 | |||
117 | /* CICOSCCTRL, CIPRSCCTRL. Main scaler control. */ | ||
118 | #define S3C_CAMIF_REG_CISCCTRL(id, _offs) (0x58 + (id) * (0x34 + (_offs))) | ||
119 | #define CISCCTRL_SCALERBYPASS (1 << 31) | ||
120 | /* s3c244x preview path only, s3c64xx both */ | ||
121 | #define CIPRSCCTRL_SAMPLE (1 << 31) | ||
122 | /* 0 - 16-bit RGB, 1 - 24-bit RGB */ | ||
123 | #define CIPRSCCTRL_RGB_FORMAT_24BIT (1 << 30) /* only for s3c244x */ | ||
124 | #define CIPRSCCTRL_SCALEUP_H (1 << 29) /* only for s3c244x */ | ||
125 | #define CIPRSCCTRL_SCALEUP_V (1 << 28) /* only for s3c244x */ | ||
126 | /* s3c64xx */ | ||
127 | #define CISCCTRL_SCALEUP_H (1 << 30) | ||
128 | #define CISCCTRL_SCALEUP_V (1 << 29) | ||
129 | #define CISCCTRL_SCALEUP_MASK (0x3 << 29) | ||
130 | #define CISCCTRL_CSCR2Y_WIDE (1 << 28) | ||
131 | #define CISCCTRL_CSCY2R_WIDE (1 << 27) | ||
132 | #define CISCCTRL_LCDPATHEN_FIFO (1 << 26) | ||
133 | #define CISCCTRL_INTERLACE (1 << 25) | ||
134 | #define CISCCTRL_SCALERSTART (1 << 15) | ||
135 | #define CISCCTRL_INRGB_FMT_RGB565 (0 << 13) | ||
136 | #define CISCCTRL_INRGB_FMT_RGB666 (1 << 13) | ||
137 | #define CISCCTRL_INRGB_FMT_RGB888 (2 << 13) | ||
138 | #define CISCCTRL_INRGB_FMT_MASK (3 << 13) | ||
139 | #define CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11) | ||
140 | #define CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11) | ||
141 | #define CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11) | ||
142 | #define CISCCTRL_OUTRGB_FMT_MASK (3 << 11) | ||
143 | #define CISCCTRL_EXTRGB_EXTENSION (1 << 10) | ||
144 | #define CISCCTRL_ONE2ONE (1 << 9) | ||
145 | #define CISCCTRL_MAIN_RATIO_MASK (0x1ff << 16 | 0x1ff) | ||
146 | |||
147 | /* CICOTAREA, CIPRTAREA. Target area for DMA (Hsize x Vsize). */ | ||
148 | #define S3C_CAMIF_REG_CITAREA(id, _offs) (0x5c + (id) * (0x34 + (_offs))) | ||
149 | #define CITAREA_MASK 0xfffffff | ||
150 | |||
151 | /* Codec (id = 0) or preview (id = 1) path status. */ | ||
152 | #define S3C_CAMIF_REG_CISTATUS(id, _offs) (0x64 + (id) * (0x34 + (_offs))) | ||
153 | #define CISTATUS_OVFIY_STATUS (1 << 31) | ||
154 | #define CISTATUS_OVFICB_STATUS (1 << 30) | ||
155 | #define CISTATUS_OVFICR_STATUS (1 << 29) | ||
156 | #define CISTATUS_OVF_MASK (0x7 << 29) | ||
157 | #define CIPRSTATUS_OVF_MASK (0x3 << 30) | ||
158 | #define CISTATUS_VSYNC_STATUS (1 << 28) | ||
159 | #define CISTATUS_FRAMECNT_MASK (3 << 26) | ||
160 | #define CISTATUS_FRAMECNT(__reg) (((__reg) >> 26) & 0x3) | ||
161 | #define CISTATUS_WINOFSTEN_STATUS (1 << 25) | ||
162 | #define CISTATUS_IMGCPTEN_STATUS (1 << 22) | ||
163 | #define CISTATUS_IMGCPTENSC_STATUS (1 << 21) | ||
164 | #define CISTATUS_VSYNC_A_STATUS (1 << 20) | ||
165 | #define CISTATUS_FRAMEEND_STATUS (1 << 19) /* 17 on s3c64xx */ | ||
166 | |||
167 | /* Image capture enable */ | ||
168 | #define S3C_CAMIF_REG_CIIMGCPT(_offs) (0xa0 + (_offs)) | ||
169 | #define CIIMGCPT_IMGCPTEN (1 << 31) | ||
170 | #define CIIMGCPT_IMGCPTEN_SC(id) (1 << (30 - (id))) | ||
171 | /* Frame control: 1 - one-shot, 0 - free run */ | ||
172 | #define CIIMGCPT_CPT_FREN_ENABLE(id) (1 << (25 - (id))) | ||
173 | #define CIIMGCPT_CPT_FRMOD_ENABLE (0 << 18) | ||
174 | #define CIIMGCPT_CPT_FRMOD_CNT (1 << 18) | ||
175 | |||
176 | /* Capture sequence */ | ||
177 | #define S3C_CAMIF_REG_CICPTSEQ 0xc4 | ||
178 | |||
179 | /* Image effects */ | ||
180 | #define S3C_CAMIF_REG_CIIMGEFF(_offs) (0xb0 + (_offs)) | ||
181 | #define CIIMGEFF_IE_ENABLE(id) (1 << (30 + (id))) | ||
182 | #define CIIMGEFF_IE_ENABLE_MASK (3 << 30) | ||
183 | /* Image effect: 1 - after scaler, 0 - before scaler */ | ||
184 | #define CIIMGEFF_IE_AFTER_SC (1 << 29) | ||
185 | #define CIIMGEFF_FIN_MASK (7 << 26) | ||
186 | #define CIIMGEFF_FIN_BYPASS (0 << 26) | ||
187 | #define CIIMGEFF_FIN_ARBITRARY (1 << 26) | ||
188 | #define CIIMGEFF_FIN_NEGATIVE (2 << 26) | ||
189 | #define CIIMGEFF_FIN_ARTFREEZE (3 << 26) | ||
190 | #define CIIMGEFF_FIN_EMBOSSING (4 << 26) | ||
191 | #define CIIMGEFF_FIN_SILHOUETTE (5 << 26) | ||
192 | #define CIIMGEFF_PAT_CBCR_MASK ((0xff << 13) | 0xff) | ||
193 | #define CIIMGEFF_PAT_CB(x) ((x) << 13) | ||
194 | #define CIIMGEFF_PAT_CR(x) (x) | ||
195 | |||
196 | /* MSCOY0SA, MSPRY0SA. Y/Cb/Cr frame start address for input DMA. */ | ||
197 | #define S3C_CAMIF_REG_MSY0SA(id) (0xd4 + ((id) * 0x2c)) | ||
198 | #define S3C_CAMIF_REG_MSCB0SA(id) (0xd8 + ((id) * 0x2c)) | ||
199 | #define S3C_CAMIF_REG_MSCR0SA(id) (0xdc + ((id) * 0x2c)) | ||
200 | |||
201 | /* MSCOY0END, MSCOY0END. Y/Cb/Cr frame end address for input DMA. */ | ||
202 | #define S3C_CAMIF_REG_MSY0END(id) (0xe0 + ((id) * 0x2c)) | ||
203 | #define S3C_CAMIF_REG_MSCB0END(id) (0xe4 + ((id) * 0x2c)) | ||
204 | #define S3C_CAMIF_REG_MSCR0END(id) (0xe8 + ((id) * 0x2c)) | ||
205 | |||
206 | /* MSPRYOFF, MSPRYOFF. Y/Cb/Cr offset. n: 0 - codec, 1 - preview. */ | ||
207 | #define S3C_CAMIF_REG_MSYOFF(id) (0x118 + ((id) * 0x2c)) | ||
208 | #define S3C_CAMIF_REG_MSCBOFF(id) (0x11c + ((id) * 0x2c)) | ||
209 | #define S3C_CAMIF_REG_MSCROFF(id) (0x120 + ((id) * 0x2c)) | ||
210 | |||
211 | /* Real input DMA data size. n = 0 - codec, 1 - preview. */ | ||
212 | #define S3C_CAMIF_REG_MSWIDTH(id) (0xf8 + (id) * 0x2c) | ||
213 | #define AUTOLOAD_ENABLE (1 << 31) | ||
214 | #define ADDR_CH_DIS (1 << 30) | ||
215 | #define MSHEIGHT(x) (((x) & 0x3ff) << 16) | ||
216 | #define MSWIDTH(x) ((x) & 0x3ff) | ||
217 | |||
218 | /* Input DMA control. n = 0 - codec, 1 - preview */ | ||
219 | #define S3C_CAMIF_REG_MSCTRL(id) (0xfc + (id) * 0x2c) | ||
220 | #define MSCTRL_ORDER422_M_YCBYCR (0 << 4) | ||
221 | #define MSCTRL_ORDER422_M_YCRYCB (1 << 4) | ||
222 | #define MSCTRL_ORDER422_M_CBYCRY (2 << 4) | ||
223 | #define MSCTRL_ORDER422_M_CRYCBY (3 << 4) | ||
224 | /* 0 - camera, 1 - DMA */ | ||
225 | #define MSCTRL_SEL_DMA_CAM (1 << 3) | ||
226 | #define MSCTRL_INFORMAT_M_YCBCR420 (0 << 1) | ||
227 | #define MSCTRL_INFORMAT_M_YCBCR422 (1 << 1) | ||
228 | #define MSCTRL_INFORMAT_M_YCBCR422I (2 << 1) | ||
229 | #define MSCTRL_INFORMAT_M_RGB (3 << 1) | ||
230 | #define MSCTRL_ENVID_M (1 << 0) | ||
231 | |||
232 | /* CICOSCOSY, CIPRSCOSY. Scan line Y/Cb/Cr offset. */ | ||
233 | #define S3C_CAMIF_REG_CISSY(id) (0x12c + (id) * 0x0c) | ||
234 | #define S3C_CAMIF_REG_CISSCB(id) (0x130 + (id) * 0x0c) | ||
235 | #define S3C_CAMIF_REG_CISSCR(id) (0x134 + (id) * 0x0c) | ||
236 | #define S3C_CISS_OFFS_INITIAL(x) ((x) << 16) | ||
237 | #define S3C_CISS_OFFS_LINE(x) ((x) << 0) | ||
238 | |||
239 | /* ------------------------------------------------------------------ */ | ||
240 | |||
241 | void camif_hw_reset(struct camif_dev *camif); | ||
242 | void camif_hw_clear_pending_irq(struct camif_vp *vp); | ||
243 | void camif_hw_clear_fifo_overflow(struct camif_vp *vp); | ||
244 | void camif_hw_set_lastirq(struct camif_vp *vp, int enable); | ||
245 | void camif_hw_set_input_path(struct camif_vp *vp); | ||
246 | void camif_hw_enable_scaler(struct camif_vp *vp, bool on); | ||
247 | void camif_hw_enable_capture(struct camif_vp *vp); | ||
248 | void camif_hw_disable_capture(struct camif_vp *vp); | ||
249 | void camif_hw_set_camera_bus(struct camif_dev *camif); | ||
250 | void camif_hw_set_source_format(struct camif_dev *camif); | ||
251 | void camif_hw_set_camera_crop(struct camif_dev *camif); | ||
252 | void camif_hw_set_scaler(struct camif_vp *vp); | ||
253 | void camif_hw_set_flip(struct camif_vp *vp); | ||
254 | void camif_hw_set_output_dma(struct camif_vp *vp); | ||
255 | void camif_hw_set_target_format(struct camif_vp *vp); | ||
256 | void camif_hw_set_test_pattern(struct camif_dev *camif, unsigned int pattern); | ||
257 | void camif_hw_set_effect(struct camif_dev *camif, unsigned int effect, | ||
258 | unsigned int cr, unsigned int cb); | ||
259 | void camif_hw_set_output_addr(struct camif_vp *vp, struct camif_addr *paddr, | ||
260 | int index); | ||
261 | void camif_hw_dump_regs(struct camif_dev *camif, const char *label); | ||
262 | |||
263 | static inline u32 camif_hw_get_status(struct camif_vp *vp) | ||
264 | { | ||
265 | return readl(vp->camif->io_base + S3C_CAMIF_REG_CISTATUS(vp->id, | ||
266 | vp->offset)); | ||
267 | } | ||
268 | |||
269 | #endif /* CAMIF_REGS_H_ */ | ||