diff options
-rw-r--r-- | drivers/media/video/s5p-fimc/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/video/s5p-fimc/fimc-capture.c | 819 | ||||
-rw-r--r-- | drivers/media/video/s5p-fimc/fimc-core.c | 563 | ||||
-rw-r--r-- | drivers/media/video/s5p-fimc/fimc-core.h | 205 | ||||
-rw-r--r-- | drivers/media/video/s5p-fimc/fimc-reg.c | 173 | ||||
-rw-r--r-- | include/media/s3c_fimc.h | 60 |
6 files changed, 1630 insertions, 192 deletions
diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile index 0d9d54132ecc..7ea1b1403b1e 100644 --- a/drivers/media/video/s5p-fimc/Makefile +++ b/drivers/media/video/s5p-fimc/Makefile | |||
@@ -1,3 +1,3 @@ | |||
1 | 1 | ||
2 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) := s5p-fimc.o | 2 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) := s5p-fimc.o |
3 | s5p-fimc-y := fimc-core.o fimc-reg.o | 3 | s5p-fimc-y := fimc-core.o fimc-reg.o fimc-capture.o |
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c new file mode 100644 index 000000000000..e8f13d3e2df1 --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-capture.c | |||
@@ -0,0 +1,819 @@ | |||
1 | /* | ||
2 | * Samsung S5P SoC series camera interface (camera capture) driver | ||
3 | * | ||
4 | * Copyright (c) 2010 Samsung Electronics Co., Ltd | ||
5 | * Author: Sylwester Nawrocki, <s.nawrocki@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/version.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/bug.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/device.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/list.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/clk.h> | ||
24 | #include <linux/i2c.h> | ||
25 | |||
26 | #include <linux/videodev2.h> | ||
27 | #include <media/v4l2-device.h> | ||
28 | #include <media/v4l2-ioctl.h> | ||
29 | #include <media/v4l2-mem2mem.h> | ||
30 | #include <media/videobuf-core.h> | ||
31 | #include <media/videobuf-dma-contig.h> | ||
32 | |||
33 | #include "fimc-core.h" | ||
34 | |||
35 | static struct v4l2_subdev *fimc_subdev_register(struct fimc_dev *fimc, | ||
36 | struct s3c_fimc_isp_info *isp_info) | ||
37 | { | ||
38 | struct i2c_adapter *i2c_adap; | ||
39 | struct fimc_vid_cap *vid_cap = &fimc->vid_cap; | ||
40 | struct v4l2_subdev *sd = NULL; | ||
41 | |||
42 | i2c_adap = i2c_get_adapter(isp_info->i2c_bus_num); | ||
43 | if (!i2c_adap) | ||
44 | return ERR_PTR(-ENOMEM); | ||
45 | |||
46 | sd = v4l2_i2c_new_subdev_board(&vid_cap->v4l2_dev, i2c_adap, | ||
47 | MODULE_NAME, isp_info->board_info, NULL); | ||
48 | if (!sd) { | ||
49 | v4l2_err(&vid_cap->v4l2_dev, "failed to acquire subdev\n"); | ||
50 | return NULL; | ||
51 | } | ||
52 | |||
53 | v4l2_info(&vid_cap->v4l2_dev, "subdevice %s registered successfuly\n", | ||
54 | isp_info->board_info->type); | ||
55 | |||
56 | return sd; | ||
57 | } | ||
58 | |||
59 | static void fimc_subdev_unregister(struct fimc_dev *fimc) | ||
60 | { | ||
61 | struct fimc_vid_cap *vid_cap = &fimc->vid_cap; | ||
62 | struct i2c_client *client; | ||
63 | |||
64 | if (vid_cap->input_index < 0) | ||
65 | return; /* Subdevice already released or not registered. */ | ||
66 | |||
67 | if (vid_cap->sd) { | ||
68 | v4l2_device_unregister_subdev(vid_cap->sd); | ||
69 | client = v4l2_get_subdevdata(vid_cap->sd); | ||
70 | i2c_unregister_device(client); | ||
71 | i2c_put_adapter(client->adapter); | ||
72 | vid_cap->sd = NULL; | ||
73 | } | ||
74 | |||
75 | vid_cap->input_index = -1; | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * fimc_subdev_attach - attach v4l2_subdev to camera host interface | ||
80 | * | ||
81 | * @fimc: FIMC device information | ||
82 | * @index: index to the array of available subdevices, | ||
83 | * -1 for full array search or non negative value | ||
84 | * to select specific subdevice | ||
85 | */ | ||
86 | static int fimc_subdev_attach(struct fimc_dev *fimc, int index) | ||
87 | { | ||
88 | struct fimc_vid_cap *vid_cap = &fimc->vid_cap; | ||
89 | struct s3c_platform_fimc *pdata = fimc->pdata; | ||
90 | struct s3c_fimc_isp_info *isp_info; | ||
91 | struct v4l2_subdev *sd; | ||
92 | int i; | ||
93 | |||
94 | for (i = 0; i < FIMC_MAX_CAMIF_CLIENTS; ++i) { | ||
95 | isp_info = pdata->isp_info[i]; | ||
96 | |||
97 | if (!isp_info || (index >= 0 && i != index)) | ||
98 | continue; | ||
99 | |||
100 | sd = fimc_subdev_register(fimc, isp_info); | ||
101 | if (sd) { | ||
102 | vid_cap->sd = sd; | ||
103 | vid_cap->input_index = i; | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | vid_cap->input_index = -1; | ||
110 | vid_cap->sd = NULL; | ||
111 | v4l2_err(&vid_cap->v4l2_dev, "fimc%d: sensor attach failed\n", | ||
112 | fimc->id); | ||
113 | return -ENODEV; | ||
114 | } | ||
115 | |||
116 | static int fimc_isp_subdev_init(struct fimc_dev *fimc, int index) | ||
117 | { | ||
118 | struct s3c_fimc_isp_info *isp_info; | ||
119 | int ret; | ||
120 | |||
121 | ret = fimc_subdev_attach(fimc, index); | ||
122 | if (ret) | ||
123 | return ret; | ||
124 | |||
125 | isp_info = fimc->pdata->isp_info[fimc->vid_cap.input_index]; | ||
126 | ret = fimc_hw_set_camera_polarity(fimc, isp_info); | ||
127 | if (!ret) { | ||
128 | ret = v4l2_subdev_call(fimc->vid_cap.sd, core, | ||
129 | s_power, 1); | ||
130 | if (!ret) | ||
131 | return ret; | ||
132 | } | ||
133 | |||
134 | fimc_subdev_unregister(fimc); | ||
135 | err("ISP initialization failed: %d", ret); | ||
136 | return ret; | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * At least one buffer on the pending_buf_q queue is required. | ||
141 | * Locking: The caller holds fimc->slock spinlock. | ||
142 | */ | ||
143 | int fimc_vid_cap_buf_queue(struct fimc_dev *fimc, | ||
144 | struct fimc_vid_buffer *fimc_vb) | ||
145 | { | ||
146 | struct fimc_vid_cap *cap = &fimc->vid_cap; | ||
147 | struct fimc_ctx *ctx = cap->ctx; | ||
148 | int ret = 0; | ||
149 | |||
150 | BUG_ON(!fimc || !fimc_vb); | ||
151 | |||
152 | ret = fimc_prepare_addr(ctx, fimc_vb, &ctx->d_frame, | ||
153 | &fimc_vb->paddr); | ||
154 | if (ret) | ||
155 | return ret; | ||
156 | |||
157 | if (test_bit(ST_CAPT_STREAM, &fimc->state)) { | ||
158 | fimc_pending_queue_add(cap, fimc_vb); | ||
159 | } else { | ||
160 | /* Setup the buffer directly for processing. */ | ||
161 | int buf_id = (cap->reqbufs_count == 1) ? -1 : cap->buf_index; | ||
162 | fimc_hw_set_output_addr(fimc, &fimc_vb->paddr, buf_id); | ||
163 | |||
164 | fimc_vb->index = cap->buf_index; | ||
165 | active_queue_add(cap, fimc_vb); | ||
166 | |||
167 | if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) | ||
168 | cap->buf_index = 0; | ||
169 | } | ||
170 | return ret; | ||
171 | } | ||
172 | |||
173 | static int fimc_stop_capture(struct fimc_dev *fimc) | ||
174 | { | ||
175 | unsigned long flags; | ||
176 | struct fimc_vid_cap *cap; | ||
177 | int ret; | ||
178 | |||
179 | cap = &fimc->vid_cap; | ||
180 | |||
181 | if (!fimc_capture_active(fimc)) | ||
182 | return 0; | ||
183 | |||
184 | spin_lock_irqsave(&fimc->slock, flags); | ||
185 | set_bit(ST_CAPT_SHUT, &fimc->state); | ||
186 | fimc_deactivate_capture(fimc); | ||
187 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
188 | |||
189 | wait_event_timeout(fimc->irq_queue, | ||
190 | test_bit(ST_CAPT_SHUT, &fimc->state), | ||
191 | FIMC_SHUTDOWN_TIMEOUT); | ||
192 | |||
193 | ret = v4l2_subdev_call(cap->sd, video, s_stream, 0); | ||
194 | if (ret) | ||
195 | v4l2_err(&fimc->vid_cap.v4l2_dev, "s_stream(0) failed\n"); | ||
196 | |||
197 | spin_lock_irqsave(&fimc->slock, flags); | ||
198 | fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_PEND | | ||
199 | 1 << ST_CAPT_STREAM); | ||
200 | |||
201 | fimc->vid_cap.active_buf_cnt = 0; | ||
202 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
203 | |||
204 | dbg("state: 0x%lx", fimc->state); | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static int fimc_capture_open(struct file *file) | ||
209 | { | ||
210 | struct fimc_dev *fimc = video_drvdata(file); | ||
211 | int ret = 0; | ||
212 | |||
213 | dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); | ||
214 | |||
215 | /* Return if the corresponding video mem2mem node is already opened. */ | ||
216 | if (fimc_m2m_active(fimc)) | ||
217 | return -EBUSY; | ||
218 | |||
219 | if (mutex_lock_interruptible(&fimc->lock)) | ||
220 | return -ERESTARTSYS; | ||
221 | |||
222 | if (++fimc->vid_cap.refcnt == 1) { | ||
223 | ret = fimc_isp_subdev_init(fimc, -1); | ||
224 | if (ret) { | ||
225 | fimc->vid_cap.refcnt--; | ||
226 | ret = -EIO; | ||
227 | } | ||
228 | } | ||
229 | |||
230 | file->private_data = fimc->vid_cap.ctx; | ||
231 | |||
232 | mutex_unlock(&fimc->lock); | ||
233 | return ret; | ||
234 | } | ||
235 | |||
236 | static int fimc_capture_close(struct file *file) | ||
237 | { | ||
238 | struct fimc_dev *fimc = video_drvdata(file); | ||
239 | |||
240 | if (mutex_lock_interruptible(&fimc->lock)) | ||
241 | return -ERESTARTSYS; | ||
242 | |||
243 | dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); | ||
244 | |||
245 | if (--fimc->vid_cap.refcnt == 0) { | ||
246 | fimc_stop_capture(fimc); | ||
247 | |||
248 | videobuf_stop(&fimc->vid_cap.vbq); | ||
249 | videobuf_mmap_free(&fimc->vid_cap.vbq); | ||
250 | |||
251 | v4l2_err(&fimc->vid_cap.v4l2_dev, "releasing ISP\n"); | ||
252 | v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0); | ||
253 | fimc_subdev_unregister(fimc); | ||
254 | } | ||
255 | |||
256 | mutex_unlock(&fimc->lock); | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static unsigned int fimc_capture_poll(struct file *file, | ||
261 | struct poll_table_struct *wait) | ||
262 | { | ||
263 | struct fimc_ctx *ctx = file->private_data; | ||
264 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
265 | struct fimc_vid_cap *cap = &fimc->vid_cap; | ||
266 | int ret; | ||
267 | |||
268 | if (mutex_lock_interruptible(&fimc->lock)) | ||
269 | return POLLERR; | ||
270 | |||
271 | ret = videobuf_poll_stream(file, &cap->vbq, wait); | ||
272 | mutex_unlock(&fimc->lock); | ||
273 | |||
274 | return ret; | ||
275 | } | ||
276 | |||
277 | static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma) | ||
278 | { | ||
279 | struct fimc_ctx *ctx = file->private_data; | ||
280 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
281 | struct fimc_vid_cap *cap = &fimc->vid_cap; | ||
282 | int ret; | ||
283 | |||
284 | if (mutex_lock_interruptible(&fimc->lock)) | ||
285 | return -ERESTARTSYS; | ||
286 | |||
287 | ret = videobuf_mmap_mapper(&cap->vbq, vma); | ||
288 | mutex_unlock(&fimc->lock); | ||
289 | |||
290 | return ret; | ||
291 | } | ||
292 | |||
293 | /* video device file operations */ | ||
294 | static const struct v4l2_file_operations fimc_capture_fops = { | ||
295 | .owner = THIS_MODULE, | ||
296 | .open = fimc_capture_open, | ||
297 | .release = fimc_capture_close, | ||
298 | .poll = fimc_capture_poll, | ||
299 | .unlocked_ioctl = video_ioctl2, | ||
300 | .mmap = fimc_capture_mmap, | ||
301 | }; | ||
302 | |||
303 | static int fimc_vidioc_querycap_capture(struct file *file, void *priv, | ||
304 | struct v4l2_capability *cap) | ||
305 | { | ||
306 | struct fimc_ctx *ctx = file->private_data; | ||
307 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
308 | |||
309 | strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); | ||
310 | strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); | ||
311 | cap->bus_info[0] = 0; | ||
312 | cap->version = KERNEL_VERSION(1, 0, 0); | ||
313 | cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; | ||
314 | |||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | /* Synchronize formats of the camera interface input and attached sensor. */ | ||
319 | static int sync_capture_fmt(struct fimc_ctx *ctx) | ||
320 | { | ||
321 | struct fimc_frame *frame = &ctx->s_frame; | ||
322 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
323 | struct v4l2_mbus_framefmt *fmt = &fimc->vid_cap.fmt; | ||
324 | int ret; | ||
325 | |||
326 | fmt->width = ctx->d_frame.o_width; | ||
327 | fmt->height = ctx->d_frame.o_height; | ||
328 | |||
329 | ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_mbus_fmt, fmt); | ||
330 | if (ret == -ENOIOCTLCMD) { | ||
331 | err("s_mbus_fmt failed"); | ||
332 | return ret; | ||
333 | } | ||
334 | dbg("w: %d, h: %d, code= %d", fmt->width, fmt->height, fmt->code); | ||
335 | |||
336 | frame->fmt = find_mbus_format(fmt, FMT_FLAGS_CAM); | ||
337 | if (!frame->fmt) { | ||
338 | err("fimc source format not found\n"); | ||
339 | return -EINVAL; | ||
340 | } | ||
341 | |||
342 | frame->f_width = fmt->width; | ||
343 | frame->f_height = fmt->height; | ||
344 | frame->width = fmt->width; | ||
345 | frame->height = fmt->height; | ||
346 | frame->o_width = fmt->width; | ||
347 | frame->o_height = fmt->height; | ||
348 | frame->offs_h = 0; | ||
349 | frame->offs_v = 0; | ||
350 | |||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | static int fimc_cap_s_fmt(struct file *file, void *priv, | ||
355 | struct v4l2_format *f) | ||
356 | { | ||
357 | struct fimc_ctx *ctx = priv; | ||
358 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
359 | struct fimc_frame *frame; | ||
360 | struct v4l2_pix_format *pix; | ||
361 | int ret; | ||
362 | |||
363 | if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
364 | return -EINVAL; | ||
365 | |||
366 | ret = fimc_vidioc_try_fmt(file, priv, f); | ||
367 | if (ret) | ||
368 | return ret; | ||
369 | |||
370 | if (mutex_lock_interruptible(&fimc->lock)) | ||
371 | return -ERESTARTSYS; | ||
372 | |||
373 | if (fimc_capture_active(fimc)) { | ||
374 | ret = -EBUSY; | ||
375 | goto sf_unlock; | ||
376 | } | ||
377 | |||
378 | frame = &ctx->d_frame; | ||
379 | |||
380 | pix = &f->fmt.pix; | ||
381 | frame->fmt = find_format(f, FMT_FLAGS_M2M | FMT_FLAGS_CAM); | ||
382 | if (!frame->fmt) { | ||
383 | err("fimc target format not found\n"); | ||
384 | ret = -EINVAL; | ||
385 | goto sf_unlock; | ||
386 | } | ||
387 | |||
388 | /* Output DMA frame pixel size and offsets. */ | ||
389 | frame->f_width = pix->bytesperline * 8 / frame->fmt->depth; | ||
390 | frame->f_height = pix->height; | ||
391 | frame->width = pix->width; | ||
392 | frame->height = pix->height; | ||
393 | frame->o_width = pix->width; | ||
394 | frame->o_height = pix->height; | ||
395 | frame->size = (pix->width * pix->height * frame->fmt->depth) >> 3; | ||
396 | frame->offs_h = 0; | ||
397 | frame->offs_v = 0; | ||
398 | |||
399 | ret = sync_capture_fmt(ctx); | ||
400 | |||
401 | ctx->state |= (FIMC_PARAMS | FIMC_DST_FMT); | ||
402 | |||
403 | sf_unlock: | ||
404 | mutex_unlock(&fimc->lock); | ||
405 | return ret; | ||
406 | } | ||
407 | |||
408 | static int fimc_cap_enum_input(struct file *file, void *priv, | ||
409 | struct v4l2_input *i) | ||
410 | { | ||
411 | struct fimc_ctx *ctx = priv; | ||
412 | struct s3c_platform_fimc *pldata = ctx->fimc_dev->pdata; | ||
413 | struct s3c_fimc_isp_info *isp_info; | ||
414 | |||
415 | if (i->index >= FIMC_MAX_CAMIF_CLIENTS) | ||
416 | return -EINVAL; | ||
417 | |||
418 | isp_info = pldata->isp_info[i->index]; | ||
419 | if (isp_info == NULL) | ||
420 | return -EINVAL; | ||
421 | |||
422 | i->type = V4L2_INPUT_TYPE_CAMERA; | ||
423 | strncpy(i->name, isp_info->board_info->type, 32); | ||
424 | return 0; | ||
425 | } | ||
426 | |||
427 | static int fimc_cap_s_input(struct file *file, void *priv, | ||
428 | unsigned int i) | ||
429 | { | ||
430 | struct fimc_ctx *ctx = priv; | ||
431 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
432 | struct s3c_platform_fimc *pdata = fimc->pdata; | ||
433 | int ret; | ||
434 | |||
435 | if (fimc_capture_active(ctx->fimc_dev)) | ||
436 | return -EBUSY; | ||
437 | |||
438 | if (mutex_lock_interruptible(&fimc->lock)) | ||
439 | return -ERESTARTSYS; | ||
440 | |||
441 | if (i >= FIMC_MAX_CAMIF_CLIENTS || !pdata->isp_info[i]) { | ||
442 | ret = -EINVAL; | ||
443 | goto si_unlock; | ||
444 | } | ||
445 | |||
446 | if (fimc->vid_cap.sd) { | ||
447 | ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0); | ||
448 | if (ret) | ||
449 | err("s_power failed: %d", ret); | ||
450 | } | ||
451 | |||
452 | /* Release the attached sensor subdevice. */ | ||
453 | fimc_subdev_unregister(fimc); | ||
454 | |||
455 | ret = fimc_isp_subdev_init(fimc, i); | ||
456 | |||
457 | si_unlock: | ||
458 | mutex_unlock(&fimc->lock); | ||
459 | return ret; | ||
460 | } | ||
461 | |||
462 | static int fimc_cap_g_input(struct file *file, void *priv, | ||
463 | unsigned int *i) | ||
464 | { | ||
465 | struct fimc_ctx *ctx = priv; | ||
466 | struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap; | ||
467 | |||
468 | *i = cap->input_index; | ||
469 | return 0; | ||
470 | } | ||
471 | |||
472 | static int fimc_cap_streamon(struct file *file, void *priv, | ||
473 | enum v4l2_buf_type type) | ||
474 | { | ||
475 | struct s3c_fimc_isp_info *isp_info; | ||
476 | struct fimc_ctx *ctx = priv; | ||
477 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
478 | int ret = -EBUSY; | ||
479 | |||
480 | if (mutex_lock_interruptible(&fimc->lock)) | ||
481 | return -ERESTARTSYS; | ||
482 | |||
483 | if (fimc_capture_active(fimc) || !fimc->vid_cap.sd) | ||
484 | goto s_unlock; | ||
485 | |||
486 | if (!(ctx->state & FIMC_DST_FMT)) { | ||
487 | v4l2_err(&fimc->vid_cap.v4l2_dev, "Format is not set\n"); | ||
488 | ret = -EINVAL; | ||
489 | goto s_unlock; | ||
490 | } | ||
491 | |||
492 | ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_stream, 1); | ||
493 | if (ret && ret != -ENOIOCTLCMD) | ||
494 | goto s_unlock; | ||
495 | |||
496 | ret = fimc_prepare_config(ctx, ctx->state); | ||
497 | if (ret) | ||
498 | goto s_unlock; | ||
499 | |||
500 | isp_info = fimc->pdata->isp_info[fimc->vid_cap.input_index]; | ||
501 | fimc_hw_set_camera_type(fimc, isp_info); | ||
502 | fimc_hw_set_camera_source(fimc, isp_info); | ||
503 | fimc_hw_set_camera_offset(fimc, &ctx->s_frame); | ||
504 | |||
505 | if (ctx->state & FIMC_PARAMS) { | ||
506 | ret = fimc_set_scaler_info(ctx); | ||
507 | if (ret) { | ||
508 | err("Scaler setup error"); | ||
509 | goto s_unlock; | ||
510 | } | ||
511 | fimc_hw_set_input_path(ctx); | ||
512 | fimc_hw_set_scaler(ctx); | ||
513 | fimc_hw_set_target_format(ctx); | ||
514 | fimc_hw_set_rotation(ctx); | ||
515 | fimc_hw_set_effect(ctx); | ||
516 | } | ||
517 | |||
518 | fimc_hw_set_output_path(ctx); | ||
519 | fimc_hw_set_out_dma(ctx); | ||
520 | |||
521 | INIT_LIST_HEAD(&fimc->vid_cap.pending_buf_q); | ||
522 | INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q); | ||
523 | fimc->vid_cap.active_buf_cnt = 0; | ||
524 | fimc->vid_cap.frame_count = 0; | ||
525 | |||
526 | set_bit(ST_CAPT_PEND, &fimc->state); | ||
527 | ret = videobuf_streamon(&fimc->vid_cap.vbq); | ||
528 | |||
529 | s_unlock: | ||
530 | mutex_unlock(&fimc->lock); | ||
531 | return ret; | ||
532 | } | ||
533 | |||
534 | static int fimc_cap_streamoff(struct file *file, void *priv, | ||
535 | enum v4l2_buf_type type) | ||
536 | { | ||
537 | struct fimc_ctx *ctx = priv; | ||
538 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
539 | struct fimc_vid_cap *cap = &fimc->vid_cap; | ||
540 | unsigned long flags; | ||
541 | int ret; | ||
542 | |||
543 | spin_lock_irqsave(&fimc->slock, flags); | ||
544 | if (!fimc_capture_running(fimc) && !fimc_capture_pending(fimc)) { | ||
545 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
546 | dbg("state: 0x%lx", fimc->state); | ||
547 | return -EINVAL; | ||
548 | } | ||
549 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
550 | |||
551 | if (mutex_lock_interruptible(&fimc->lock)) | ||
552 | return -ERESTARTSYS; | ||
553 | |||
554 | fimc_stop_capture(fimc); | ||
555 | ret = videobuf_streamoff(&cap->vbq); | ||
556 | mutex_unlock(&fimc->lock); | ||
557 | return ret; | ||
558 | } | ||
559 | |||
560 | static int fimc_cap_reqbufs(struct file *file, void *priv, | ||
561 | struct v4l2_requestbuffers *reqbufs) | ||
562 | { | ||
563 | struct fimc_ctx *ctx = priv; | ||
564 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
565 | struct fimc_vid_cap *cap = &fimc->vid_cap; | ||
566 | int ret; | ||
567 | |||
568 | if (fimc_capture_active(ctx->fimc_dev)) | ||
569 | return -EBUSY; | ||
570 | |||
571 | if (mutex_lock_interruptible(&fimc->lock)) | ||
572 | return -ERESTARTSYS; | ||
573 | |||
574 | ret = videobuf_reqbufs(&cap->vbq, reqbufs); | ||
575 | if (!ret) | ||
576 | cap->reqbufs_count = reqbufs->count; | ||
577 | |||
578 | mutex_unlock(&fimc->lock); | ||
579 | return ret; | ||
580 | } | ||
581 | |||
582 | static int fimc_cap_querybuf(struct file *file, void *priv, | ||
583 | struct v4l2_buffer *buf) | ||
584 | { | ||
585 | struct fimc_ctx *ctx = priv; | ||
586 | struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap; | ||
587 | |||
588 | if (fimc_capture_active(ctx->fimc_dev)) | ||
589 | return -EBUSY; | ||
590 | |||
591 | return videobuf_querybuf(&cap->vbq, buf); | ||
592 | } | ||
593 | |||
594 | static int fimc_cap_qbuf(struct file *file, void *priv, | ||
595 | struct v4l2_buffer *buf) | ||
596 | { | ||
597 | struct fimc_ctx *ctx = priv; | ||
598 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
599 | struct fimc_vid_cap *cap = &fimc->vid_cap; | ||
600 | int ret; | ||
601 | |||
602 | if (mutex_lock_interruptible(&fimc->lock)) | ||
603 | return -ERESTARTSYS; | ||
604 | |||
605 | ret = videobuf_qbuf(&cap->vbq, buf); | ||
606 | |||
607 | mutex_unlock(&fimc->lock); | ||
608 | return ret; | ||
609 | } | ||
610 | |||
611 | static int fimc_cap_dqbuf(struct file *file, void *priv, | ||
612 | struct v4l2_buffer *buf) | ||
613 | { | ||
614 | struct fimc_ctx *ctx = priv; | ||
615 | int ret; | ||
616 | |||
617 | if (mutex_lock_interruptible(&ctx->fimc_dev->lock)) | ||
618 | return -ERESTARTSYS; | ||
619 | |||
620 | ret = videobuf_dqbuf(&ctx->fimc_dev->vid_cap.vbq, buf, | ||
621 | file->f_flags & O_NONBLOCK); | ||
622 | |||
623 | mutex_unlock(&ctx->fimc_dev->lock); | ||
624 | return ret; | ||
625 | } | ||
626 | |||
627 | static int fimc_cap_s_ctrl(struct file *file, void *priv, | ||
628 | struct v4l2_control *ctrl) | ||
629 | { | ||
630 | struct fimc_ctx *ctx = priv; | ||
631 | int ret = -EINVAL; | ||
632 | |||
633 | if (mutex_lock_interruptible(&ctx->fimc_dev->lock)) | ||
634 | return -ERESTARTSYS; | ||
635 | |||
636 | /* Allow any controls but 90/270 rotation while streaming */ | ||
637 | if (!fimc_capture_active(ctx->fimc_dev) || | ||
638 | ctrl->id != V4L2_CID_ROTATE || | ||
639 | (ctrl->value != 90 && ctrl->value != 270)) { | ||
640 | ret = check_ctrl_val(ctx, ctrl); | ||
641 | if (!ret) { | ||
642 | ret = fimc_s_ctrl(ctx, ctrl); | ||
643 | if (!ret) | ||
644 | ctx->state |= FIMC_PARAMS; | ||
645 | } | ||
646 | } | ||
647 | if (ret == -EINVAL) | ||
648 | ret = v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd, | ||
649 | core, s_ctrl, ctrl); | ||
650 | |||
651 | mutex_unlock(&ctx->fimc_dev->lock); | ||
652 | return ret; | ||
653 | } | ||
654 | |||
655 | static int fimc_cap_s_crop(struct file *file, void *fh, | ||
656 | struct v4l2_crop *cr) | ||
657 | { | ||
658 | struct fimc_frame *f; | ||
659 | struct fimc_ctx *ctx = file->private_data; | ||
660 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
661 | int ret = -EINVAL; | ||
662 | |||
663 | if (fimc_capture_active(fimc)) | ||
664 | return -EBUSY; | ||
665 | |||
666 | ret = fimc_try_crop(ctx, cr); | ||
667 | if (ret) | ||
668 | return ret; | ||
669 | |||
670 | if (mutex_lock_interruptible(&fimc->lock)) | ||
671 | return -ERESTARTSYS; | ||
672 | |||
673 | if (!(ctx->state & FIMC_DST_FMT)) { | ||
674 | v4l2_err(&fimc->vid_cap.v4l2_dev, | ||
675 | "Capture color format not set\n"); | ||
676 | goto sc_unlock; | ||
677 | } | ||
678 | |||
679 | f = &ctx->s_frame; | ||
680 | /* Check for the pixel scaling ratio when cropping input image. */ | ||
681 | ret = fimc_check_scaler_ratio(&cr->c, &ctx->d_frame); | ||
682 | if (ret) { | ||
683 | v4l2_err(&fimc->vid_cap.v4l2_dev, "Out of the scaler range"); | ||
684 | } else { | ||
685 | ret = 0; | ||
686 | f->offs_h = cr->c.left; | ||
687 | f->offs_v = cr->c.top; | ||
688 | f->width = cr->c.width; | ||
689 | f->height = cr->c.height; | ||
690 | } | ||
691 | |||
692 | sc_unlock: | ||
693 | mutex_unlock(&fimc->lock); | ||
694 | return ret; | ||
695 | } | ||
696 | |||
697 | |||
698 | static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { | ||
699 | .vidioc_querycap = fimc_vidioc_querycap_capture, | ||
700 | |||
701 | .vidioc_enum_fmt_vid_cap = fimc_vidioc_enum_fmt, | ||
702 | .vidioc_try_fmt_vid_cap = fimc_vidioc_try_fmt, | ||
703 | .vidioc_s_fmt_vid_cap = fimc_cap_s_fmt, | ||
704 | .vidioc_g_fmt_vid_cap = fimc_vidioc_g_fmt, | ||
705 | |||
706 | .vidioc_reqbufs = fimc_cap_reqbufs, | ||
707 | .vidioc_querybuf = fimc_cap_querybuf, | ||
708 | |||
709 | .vidioc_qbuf = fimc_cap_qbuf, | ||
710 | .vidioc_dqbuf = fimc_cap_dqbuf, | ||
711 | |||
712 | .vidioc_streamon = fimc_cap_streamon, | ||
713 | .vidioc_streamoff = fimc_cap_streamoff, | ||
714 | |||
715 | .vidioc_queryctrl = fimc_vidioc_queryctrl, | ||
716 | .vidioc_g_ctrl = fimc_vidioc_g_ctrl, | ||
717 | .vidioc_s_ctrl = fimc_cap_s_ctrl, | ||
718 | |||
719 | .vidioc_g_crop = fimc_vidioc_g_crop, | ||
720 | .vidioc_s_crop = fimc_cap_s_crop, | ||
721 | .vidioc_cropcap = fimc_vidioc_cropcap, | ||
722 | |||
723 | .vidioc_enum_input = fimc_cap_enum_input, | ||
724 | .vidioc_s_input = fimc_cap_s_input, | ||
725 | .vidioc_g_input = fimc_cap_g_input, | ||
726 | }; | ||
727 | |||
728 | int fimc_register_capture_device(struct fimc_dev *fimc) | ||
729 | { | ||
730 | struct v4l2_device *v4l2_dev = &fimc->vid_cap.v4l2_dev; | ||
731 | struct video_device *vfd; | ||
732 | struct fimc_vid_cap *vid_cap; | ||
733 | struct fimc_ctx *ctx; | ||
734 | struct v4l2_format f; | ||
735 | int ret; | ||
736 | |||
737 | ctx = kzalloc(sizeof *ctx, GFP_KERNEL); | ||
738 | if (!ctx) | ||
739 | return -ENOMEM; | ||
740 | |||
741 | ctx->fimc_dev = fimc; | ||
742 | ctx->in_path = FIMC_CAMERA; | ||
743 | ctx->out_path = FIMC_DMA; | ||
744 | ctx->state = FIMC_CTX_CAP; | ||
745 | |||
746 | f.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; | ||
747 | ctx->d_frame.fmt = find_format(&f, FMT_FLAGS_M2M); | ||
748 | |||
749 | if (!v4l2_dev->name[0]) | ||
750 | snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), | ||
751 | "%s.capture", dev_name(&fimc->pdev->dev)); | ||
752 | |||
753 | ret = v4l2_device_register(NULL, v4l2_dev); | ||
754 | if (ret) | ||
755 | goto err_info; | ||
756 | |||
757 | vfd = video_device_alloc(); | ||
758 | if (!vfd) { | ||
759 | v4l2_err(v4l2_dev, "Failed to allocate video device\n"); | ||
760 | goto err_v4l2_reg; | ||
761 | } | ||
762 | |||
763 | snprintf(vfd->name, sizeof(vfd->name), "%s:cap", | ||
764 | dev_name(&fimc->pdev->dev)); | ||
765 | |||
766 | vfd->fops = &fimc_capture_fops; | ||
767 | vfd->ioctl_ops = &fimc_capture_ioctl_ops; | ||
768 | vfd->minor = -1; | ||
769 | vfd->release = video_device_release; | ||
770 | video_set_drvdata(vfd, fimc); | ||
771 | |||
772 | vid_cap = &fimc->vid_cap; | ||
773 | vid_cap->vfd = vfd; | ||
774 | vid_cap->active_buf_cnt = 0; | ||
775 | vid_cap->reqbufs_count = 0; | ||
776 | vid_cap->refcnt = 0; | ||
777 | /* The default color format for image sensor. */ | ||
778 | vid_cap->fmt.code = V4L2_MBUS_FMT_YUYV8_2X8; | ||
779 | |||
780 | INIT_LIST_HEAD(&vid_cap->pending_buf_q); | ||
781 | INIT_LIST_HEAD(&vid_cap->active_buf_q); | ||
782 | spin_lock_init(&ctx->slock); | ||
783 | vid_cap->ctx = ctx; | ||
784 | |||
785 | videobuf_queue_dma_contig_init(&vid_cap->vbq, &fimc_qops, | ||
786 | vid_cap->v4l2_dev.dev, &fimc->irqlock, | ||
787 | V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, | ||
788 | sizeof(struct fimc_vid_buffer), (void *)ctx); | ||
789 | |||
790 | ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); | ||
791 | if (ret) { | ||
792 | v4l2_err(v4l2_dev, "Failed to register video device\n"); | ||
793 | goto err_vd_reg; | ||
794 | } | ||
795 | |||
796 | v4l2_info(v4l2_dev, | ||
797 | "FIMC capture driver registered as /dev/video%d\n", | ||
798 | vfd->num); | ||
799 | |||
800 | return 0; | ||
801 | |||
802 | err_vd_reg: | ||
803 | video_device_release(vfd); | ||
804 | err_v4l2_reg: | ||
805 | v4l2_device_unregister(v4l2_dev); | ||
806 | err_info: | ||
807 | dev_err(&fimc->pdev->dev, "failed to install\n"); | ||
808 | return ret; | ||
809 | } | ||
810 | |||
811 | void fimc_unregister_capture_device(struct fimc_dev *fimc) | ||
812 | { | ||
813 | struct fimc_vid_cap *capture = &fimc->vid_cap; | ||
814 | |||
815 | if (capture->vfd) | ||
816 | video_unregister_device(capture->vfd); | ||
817 | |||
818 | kfree(capture->ctx); | ||
819 | } | ||
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 23cc054bd7b1..5168a9a5d821 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * S5P camera interface (video postprocessor) driver | 2 | * S5P camera interface (video postprocessor) driver |
3 | * | 3 | * |
4 | * Copyright (c) 2010 Samsung Electronics | 4 | * Copyright (c) 2010 Samsung Electronics Co., Ltd |
5 | * | 5 | * |
6 | * Sylwester Nawrocki, <s.nawrocki@samsung.com> | 6 | * Sylwester Nawrocki, <s.nawrocki@samsung.com> |
7 | * | 7 | * |
@@ -38,85 +38,102 @@ static struct fimc_fmt fimc_formats[] = { | |||
38 | .depth = 16, | 38 | .depth = 16, |
39 | .color = S5P_FIMC_RGB565, | 39 | .color = S5P_FIMC_RGB565, |
40 | .buff_cnt = 1, | 40 | .buff_cnt = 1, |
41 | .planes_cnt = 1 | 41 | .planes_cnt = 1, |
42 | .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_BE, | ||
43 | .flags = FMT_FLAGS_M2M, | ||
42 | }, { | 44 | }, { |
43 | .name = "BGR666", | 45 | .name = "BGR666", |
44 | .fourcc = V4L2_PIX_FMT_BGR666, | 46 | .fourcc = V4L2_PIX_FMT_BGR666, |
45 | .depth = 32, | 47 | .depth = 32, |
46 | .color = S5P_FIMC_RGB666, | 48 | .color = S5P_FIMC_RGB666, |
47 | .buff_cnt = 1, | 49 | .buff_cnt = 1, |
48 | .planes_cnt = 1 | 50 | .planes_cnt = 1, |
51 | .flags = FMT_FLAGS_M2M, | ||
49 | }, { | 52 | }, { |
50 | .name = "XRGB-8-8-8-8, 24 bpp", | 53 | .name = "XRGB-8-8-8-8, 24 bpp", |
51 | .fourcc = V4L2_PIX_FMT_RGB24, | 54 | .fourcc = V4L2_PIX_FMT_RGB24, |
52 | .depth = 32, | 55 | .depth = 32, |
53 | .color = S5P_FIMC_RGB888, | 56 | .color = S5P_FIMC_RGB888, |
54 | .buff_cnt = 1, | 57 | .buff_cnt = 1, |
55 | .planes_cnt = 1 | 58 | .planes_cnt = 1, |
59 | .flags = FMT_FLAGS_M2M, | ||
56 | }, { | 60 | }, { |
57 | .name = "YUV 4:2:2 packed, YCbYCr", | 61 | .name = "YUV 4:2:2 packed, YCbYCr", |
58 | .fourcc = V4L2_PIX_FMT_YUYV, | 62 | .fourcc = V4L2_PIX_FMT_YUYV, |
59 | .depth = 16, | 63 | .depth = 16, |
60 | .color = S5P_FIMC_YCBYCR422, | 64 | .color = S5P_FIMC_YCBYCR422, |
61 | .buff_cnt = 1, | 65 | .buff_cnt = 1, |
62 | .planes_cnt = 1 | 66 | .planes_cnt = 1, |
63 | }, { | 67 | .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, |
68 | .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, | ||
69 | }, { | ||
64 | .name = "YUV 4:2:2 packed, CbYCrY", | 70 | .name = "YUV 4:2:2 packed, CbYCrY", |
65 | .fourcc = V4L2_PIX_FMT_UYVY, | 71 | .fourcc = V4L2_PIX_FMT_UYVY, |
66 | .depth = 16, | 72 | .depth = 16, |
67 | .color = S5P_FIMC_CBYCRY422, | 73 | .color = S5P_FIMC_CBYCRY422, |
68 | .buff_cnt = 1, | 74 | .buff_cnt = 1, |
69 | .planes_cnt = 1 | 75 | .planes_cnt = 1, |
76 | .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, | ||
77 | .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, | ||
70 | }, { | 78 | }, { |
71 | .name = "YUV 4:2:2 packed, CrYCbY", | 79 | .name = "YUV 4:2:2 packed, CrYCbY", |
72 | .fourcc = V4L2_PIX_FMT_VYUY, | 80 | .fourcc = V4L2_PIX_FMT_VYUY, |
73 | .depth = 16, | 81 | .depth = 16, |
74 | .color = S5P_FIMC_CRYCBY422, | 82 | .color = S5P_FIMC_CRYCBY422, |
75 | .buff_cnt = 1, | 83 | .buff_cnt = 1, |
76 | .planes_cnt = 1 | 84 | .planes_cnt = 1, |
85 | .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8, | ||
86 | .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, | ||
77 | }, { | 87 | }, { |
78 | .name = "YUV 4:2:2 packed, YCrYCb", | 88 | .name = "YUV 4:2:2 packed, YCrYCb", |
79 | .fourcc = V4L2_PIX_FMT_YVYU, | 89 | .fourcc = V4L2_PIX_FMT_YVYU, |
80 | .depth = 16, | 90 | .depth = 16, |
81 | .color = S5P_FIMC_YCRYCB422, | 91 | .color = S5P_FIMC_YCRYCB422, |
82 | .buff_cnt = 1, | 92 | .buff_cnt = 1, |
83 | .planes_cnt = 1 | 93 | .planes_cnt = 1, |
94 | .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8, | ||
95 | .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM, | ||
84 | }, { | 96 | }, { |
85 | .name = "YUV 4:2:2 planar, Y/Cb/Cr", | 97 | .name = "YUV 4:2:2 planar, Y/Cb/Cr", |
86 | .fourcc = V4L2_PIX_FMT_YUV422P, | 98 | .fourcc = V4L2_PIX_FMT_YUV422P, |
87 | .depth = 12, | 99 | .depth = 12, |
88 | .color = S5P_FIMC_YCBCR422, | 100 | .color = S5P_FIMC_YCBCR422, |
89 | .buff_cnt = 1, | 101 | .buff_cnt = 1, |
90 | .planes_cnt = 3 | 102 | .planes_cnt = 3, |
103 | .flags = FMT_FLAGS_M2M, | ||
91 | }, { | 104 | }, { |
92 | .name = "YUV 4:2:2 planar, Y/CbCr", | 105 | .name = "YUV 4:2:2 planar, Y/CbCr", |
93 | .fourcc = V4L2_PIX_FMT_NV16, | 106 | .fourcc = V4L2_PIX_FMT_NV16, |
94 | .depth = 16, | 107 | .depth = 16, |
95 | .color = S5P_FIMC_YCBCR422, | 108 | .color = S5P_FIMC_YCBCR422, |
96 | .buff_cnt = 1, | 109 | .buff_cnt = 1, |
97 | .planes_cnt = 2 | 110 | .planes_cnt = 2, |
111 | .flags = FMT_FLAGS_M2M, | ||
98 | }, { | 112 | }, { |
99 | .name = "YUV 4:2:2 planar, Y/CrCb", | 113 | .name = "YUV 4:2:2 planar, Y/CrCb", |
100 | .fourcc = V4L2_PIX_FMT_NV61, | 114 | .fourcc = V4L2_PIX_FMT_NV61, |
101 | .depth = 16, | 115 | .depth = 16, |
102 | .color = S5P_FIMC_RGB565, | 116 | .color = S5P_FIMC_RGB565, |
103 | .buff_cnt = 1, | 117 | .buff_cnt = 1, |
104 | .planes_cnt = 2 | 118 | .planes_cnt = 2, |
119 | .flags = FMT_FLAGS_M2M, | ||
105 | }, { | 120 | }, { |
106 | .name = "YUV 4:2:0 planar, YCbCr", | 121 | .name = "YUV 4:2:0 planar, YCbCr", |
107 | .fourcc = V4L2_PIX_FMT_YUV420, | 122 | .fourcc = V4L2_PIX_FMT_YUV420, |
108 | .depth = 12, | 123 | .depth = 12, |
109 | .color = S5P_FIMC_YCBCR420, | 124 | .color = S5P_FIMC_YCBCR420, |
110 | .buff_cnt = 1, | 125 | .buff_cnt = 1, |
111 | .planes_cnt = 3 | 126 | .planes_cnt = 3, |
127 | .flags = FMT_FLAGS_M2M, | ||
112 | }, { | 128 | }, { |
113 | .name = "YUV 4:2:0 planar, Y/CbCr", | 129 | .name = "YUV 4:2:0 planar, Y/CbCr", |
114 | .fourcc = V4L2_PIX_FMT_NV12, | 130 | .fourcc = V4L2_PIX_FMT_NV12, |
115 | .depth = 12, | 131 | .depth = 12, |
116 | .color = S5P_FIMC_YCBCR420, | 132 | .color = S5P_FIMC_YCBCR420, |
117 | .buff_cnt = 1, | 133 | .buff_cnt = 1, |
118 | .planes_cnt = 2 | 134 | .planes_cnt = 2, |
119 | } | 135 | .flags = FMT_FLAGS_M2M, |
136 | }, | ||
120 | }; | 137 | }; |
121 | 138 | ||
122 | static struct v4l2_queryctrl fimc_ctrls[] = { | 139 | static struct v4l2_queryctrl fimc_ctrls[] = { |
@@ -156,7 +173,7 @@ static struct v4l2_queryctrl *get_ctrl(int id) | |||
156 | return NULL; | 173 | return NULL; |
157 | } | 174 | } |
158 | 175 | ||
159 | static int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f) | 176 | int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f) |
160 | { | 177 | { |
161 | if (r->width > f->width) { | 178 | if (r->width > f->width) { |
162 | if (f->width > (r->width * SCALER_MAX_HRATIO)) | 179 | if (f->width > (r->width * SCALER_MAX_HRATIO)) |
@@ -199,7 +216,7 @@ static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift) | |||
199 | return 0; | 216 | return 0; |
200 | } | 217 | } |
201 | 218 | ||
202 | static int fimc_set_scaler_info(struct fimc_ctx *ctx) | 219 | int fimc_set_scaler_info(struct fimc_ctx *ctx) |
203 | { | 220 | { |
204 | struct fimc_scaler *sc = &ctx->scaler; | 221 | struct fimc_scaler *sc = &ctx->scaler; |
205 | struct fimc_frame *s_frame = &ctx->s_frame; | 222 | struct fimc_frame *s_frame = &ctx->s_frame; |
@@ -259,6 +276,51 @@ static int fimc_set_scaler_info(struct fimc_ctx *ctx) | |||
259 | return 0; | 276 | return 0; |
260 | } | 277 | } |
261 | 278 | ||
279 | static void fimc_capture_handler(struct fimc_dev *fimc) | ||
280 | { | ||
281 | struct fimc_vid_cap *cap = &fimc->vid_cap; | ||
282 | struct fimc_vid_buffer *v_buf = NULL; | ||
283 | |||
284 | if (!list_empty(&cap->active_buf_q)) { | ||
285 | v_buf = active_queue_pop(cap); | ||
286 | fimc_buf_finish(fimc, v_buf); | ||
287 | } | ||
288 | |||
289 | if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) { | ||
290 | wake_up(&fimc->irq_queue); | ||
291 | return; | ||
292 | } | ||
293 | |||
294 | if (!list_empty(&cap->pending_buf_q)) { | ||
295 | |||
296 | v_buf = pending_queue_pop(cap); | ||
297 | fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index); | ||
298 | v_buf->index = cap->buf_index; | ||
299 | |||
300 | dbg("hw ptr: %d, sw ptr: %d", | ||
301 | fimc_hw_get_frame_index(fimc), cap->buf_index); | ||
302 | |||
303 | spin_lock(&fimc->irqlock); | ||
304 | v_buf->vb.state = VIDEOBUF_ACTIVE; | ||
305 | spin_unlock(&fimc->irqlock); | ||
306 | |||
307 | /* Move the buffer to the capture active queue */ | ||
308 | active_queue_add(cap, v_buf); | ||
309 | |||
310 | dbg("next frame: %d, done frame: %d", | ||
311 | fimc_hw_get_frame_index(fimc), v_buf->index); | ||
312 | |||
313 | if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) | ||
314 | cap->buf_index = 0; | ||
315 | |||
316 | } else if (test_and_clear_bit(ST_CAPT_STREAM, &fimc->state) && | ||
317 | cap->active_buf_cnt <= 1) { | ||
318 | fimc_deactivate_capture(fimc); | ||
319 | } | ||
320 | |||
321 | dbg("frame: %d, active_buf_cnt= %d", | ||
322 | fimc_hw_get_frame_index(fimc), cap->active_buf_cnt); | ||
323 | } | ||
262 | 324 | ||
263 | static irqreturn_t fimc_isr(int irq, void *priv) | 325 | static irqreturn_t fimc_isr(int irq, void *priv) |
264 | { | 326 | { |
@@ -285,6 +347,16 @@ static irqreturn_t fimc_isr(int irq, void *priv) | |||
285 | spin_unlock(&fimc->irqlock); | 347 | spin_unlock(&fimc->irqlock); |
286 | v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx); | 348 | v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx); |
287 | } | 349 | } |
350 | goto isr_unlock; | ||
351 | |||
352 | } | ||
353 | |||
354 | if (test_bit(ST_CAPT_RUN, &fimc->state)) | ||
355 | fimc_capture_handler(fimc); | ||
356 | |||
357 | if (test_and_clear_bit(ST_CAPT_PEND, &fimc->state)) { | ||
358 | set_bit(ST_CAPT_RUN, &fimc->state); | ||
359 | wake_up(&fimc->irq_queue); | ||
288 | } | 360 | } |
289 | 361 | ||
290 | isr_unlock: | 362 | isr_unlock: |
@@ -424,7 +496,7 @@ static void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) | |||
424 | * | 496 | * |
425 | * Return: 0 if dimensions are valid or non zero otherwise. | 497 | * Return: 0 if dimensions are valid or non zero otherwise. |
426 | */ | 498 | */ |
427 | static int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags) | 499 | int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags) |
428 | { | 500 | { |
429 | struct fimc_frame *s_frame, *d_frame; | 501 | struct fimc_frame *s_frame, *d_frame; |
430 | struct fimc_vid_buffer *buf = NULL; | 502 | struct fimc_vid_buffer *buf = NULL; |
@@ -513,9 +585,9 @@ static void fimc_dma_run(void *priv) | |||
513 | if (ctx->state & FIMC_PARAMS) | 585 | if (ctx->state & FIMC_PARAMS) |
514 | fimc_hw_set_out_dma(ctx); | 586 | fimc_hw_set_out_dma(ctx); |
515 | 587 | ||
516 | ctx->state = 0; | ||
517 | fimc_activate_capture(ctx); | 588 | fimc_activate_capture(ctx); |
518 | 589 | ||
590 | ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP); | ||
519 | fimc_hw_activate_input_dma(fimc, true); | 591 | fimc_hw_activate_input_dma(fimc, true); |
520 | 592 | ||
521 | dma_unlock: | 593 | dma_unlock: |
@@ -598,10 +670,31 @@ static void fimc_buf_queue(struct videobuf_queue *vq, | |||
598 | struct videobuf_buffer *vb) | 670 | struct videobuf_buffer *vb) |
599 | { | 671 | { |
600 | struct fimc_ctx *ctx = vq->priv_data; | 672 | struct fimc_ctx *ctx = vq->priv_data; |
601 | v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb); | 673 | struct fimc_dev *fimc = ctx->fimc_dev; |
674 | struct fimc_vid_cap *cap = &fimc->vid_cap; | ||
675 | unsigned long flags; | ||
676 | |||
677 | dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); | ||
678 | |||
679 | if ((ctx->state & FIMC_CTX_M2M) && ctx->m2m_ctx) { | ||
680 | v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb); | ||
681 | } else if (ctx->state & FIMC_CTX_CAP) { | ||
682 | spin_lock_irqsave(&fimc->slock, flags); | ||
683 | fimc_vid_cap_buf_queue(fimc, (struct fimc_vid_buffer *)vb); | ||
684 | |||
685 | dbg("fimc->cap.active_buf_cnt: %d", | ||
686 | fimc->vid_cap.active_buf_cnt); | ||
687 | |||
688 | if (cap->active_buf_cnt >= cap->reqbufs_count || | ||
689 | cap->active_buf_cnt >= FIMC_MAX_OUT_BUFS) { | ||
690 | if (!test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) | ||
691 | fimc_activate_capture(ctx); | ||
692 | } | ||
693 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
694 | } | ||
602 | } | 695 | } |
603 | 696 | ||
604 | static struct videobuf_queue_ops fimc_qops = { | 697 | struct videobuf_queue_ops fimc_qops = { |
605 | .buf_setup = fimc_buf_setup, | 698 | .buf_setup = fimc_buf_setup, |
606 | .buf_prepare = fimc_buf_prepare, | 699 | .buf_prepare = fimc_buf_prepare, |
607 | .buf_queue = fimc_buf_queue, | 700 | .buf_queue = fimc_buf_queue, |
@@ -624,7 +717,7 @@ static int fimc_m2m_querycap(struct file *file, void *priv, | |||
624 | return 0; | 717 | return 0; |
625 | } | 718 | } |
626 | 719 | ||
627 | static int fimc_m2m_enum_fmt(struct file *file, void *priv, | 720 | int fimc_vidioc_enum_fmt(struct file *file, void *priv, |
628 | struct v4l2_fmtdesc *f) | 721 | struct v4l2_fmtdesc *f) |
629 | { | 722 | { |
630 | struct fimc_fmt *fmt; | 723 | struct fimc_fmt *fmt; |
@@ -635,109 +728,139 @@ static int fimc_m2m_enum_fmt(struct file *file, void *priv, | |||
635 | fmt = &fimc_formats[f->index]; | 728 | fmt = &fimc_formats[f->index]; |
636 | strncpy(f->description, fmt->name, sizeof(f->description) - 1); | 729 | strncpy(f->description, fmt->name, sizeof(f->description) - 1); |
637 | f->pixelformat = fmt->fourcc; | 730 | f->pixelformat = fmt->fourcc; |
731 | |||
638 | return 0; | 732 | return 0; |
639 | } | 733 | } |
640 | 734 | ||
641 | static int fimc_m2m_g_fmt(struct file *file, void *priv, struct v4l2_format *f) | 735 | int fimc_vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) |
642 | { | 736 | { |
643 | struct fimc_ctx *ctx = priv; | 737 | struct fimc_ctx *ctx = priv; |
738 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
644 | struct fimc_frame *frame; | 739 | struct fimc_frame *frame; |
645 | 740 | ||
646 | frame = ctx_get_frame(ctx, f->type); | 741 | frame = ctx_get_frame(ctx, f->type); |
647 | if (IS_ERR(frame)) | 742 | if (IS_ERR(frame)) |
648 | return PTR_ERR(frame); | 743 | return PTR_ERR(frame); |
649 | 744 | ||
745 | if (mutex_lock_interruptible(&fimc->lock)) | ||
746 | return -ERESTARTSYS; | ||
747 | |||
650 | f->fmt.pix.width = frame->width; | 748 | f->fmt.pix.width = frame->width; |
651 | f->fmt.pix.height = frame->height; | 749 | f->fmt.pix.height = frame->height; |
652 | f->fmt.pix.field = V4L2_FIELD_NONE; | 750 | f->fmt.pix.field = V4L2_FIELD_NONE; |
653 | f->fmt.pix.pixelformat = frame->fmt->fourcc; | 751 | f->fmt.pix.pixelformat = frame->fmt->fourcc; |
654 | 752 | ||
753 | mutex_unlock(&fimc->lock); | ||
655 | return 0; | 754 | return 0; |
656 | } | 755 | } |
657 | 756 | ||
658 | static struct fimc_fmt *find_format(struct v4l2_format *f) | 757 | struct fimc_fmt *find_format(struct v4l2_format *f, unsigned int mask) |
659 | { | 758 | { |
660 | struct fimc_fmt *fmt; | 759 | struct fimc_fmt *fmt; |
661 | unsigned int i; | 760 | unsigned int i; |
662 | 761 | ||
663 | for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) { | 762 | for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) { |
664 | fmt = &fimc_formats[i]; | 763 | fmt = &fimc_formats[i]; |
665 | if (fmt->fourcc == f->fmt.pix.pixelformat) | 764 | if (fmt->fourcc == f->fmt.pix.pixelformat && |
765 | (fmt->flags & mask)) | ||
666 | break; | 766 | break; |
667 | } | 767 | } |
668 | if (i == ARRAY_SIZE(fimc_formats)) | ||
669 | return NULL; | ||
670 | 768 | ||
671 | return fmt; | 769 | return (i == ARRAY_SIZE(fimc_formats)) ? NULL : fmt; |
672 | } | 770 | } |
673 | 771 | ||
674 | static int fimc_m2m_try_fmt(struct file *file, void *priv, | 772 | struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f, |
675 | struct v4l2_format *f) | 773 | unsigned int mask) |
676 | { | 774 | { |
677 | struct fimc_fmt *fmt; | 775 | struct fimc_fmt *fmt; |
678 | u32 max_width, max_height, mod_x, mod_y; | 776 | unsigned int i; |
777 | |||
778 | for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) { | ||
779 | fmt = &fimc_formats[i]; | ||
780 | if (fmt->mbus_code == f->code && (fmt->flags & mask)) | ||
781 | break; | ||
782 | } | ||
783 | |||
784 | return (i == ARRAY_SIZE(fimc_formats)) ? NULL : fmt; | ||
785 | } | ||
786 | |||
787 | |||
788 | int fimc_vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) | ||
789 | { | ||
679 | struct fimc_ctx *ctx = priv; | 790 | struct fimc_ctx *ctx = priv; |
680 | struct fimc_dev *fimc = ctx->fimc_dev; | 791 | struct fimc_dev *fimc = ctx->fimc_dev; |
681 | struct v4l2_pix_format *pix = &f->fmt.pix; | ||
682 | struct samsung_fimc_variant *variant = fimc->variant; | 792 | struct samsung_fimc_variant *variant = fimc->variant; |
793 | struct v4l2_pix_format *pix = &f->fmt.pix; | ||
794 | struct fimc_fmt *fmt; | ||
795 | u32 max_width, mod_x, mod_y, mask; | ||
796 | int ret = -EINVAL, is_output = 0; | ||
683 | 797 | ||
684 | fmt = find_format(f); | 798 | if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { |
685 | if (!fmt) { | 799 | if (ctx->state & FIMC_CTX_CAP) |
686 | v4l2_err(&fimc->m2m.v4l2_dev, | 800 | return -EINVAL; |
687 | "Fourcc format (0x%X) invalid.\n", pix->pixelformat); | 801 | is_output = 1; |
802 | } else if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { | ||
688 | return -EINVAL; | 803 | return -EINVAL; |
689 | } | 804 | } |
690 | 805 | ||
806 | dbg("w: %d, h: %d, bpl: %d", | ||
807 | pix->width, pix->height, pix->bytesperline); | ||
808 | |||
809 | if (mutex_lock_interruptible(&fimc->lock)) | ||
810 | return -ERESTARTSYS; | ||
811 | |||
812 | mask = is_output ? FMT_FLAGS_M2M : FMT_FLAGS_M2M | FMT_FLAGS_CAM; | ||
813 | fmt = find_format(f, mask); | ||
814 | if (!fmt) { | ||
815 | v4l2_err(&fimc->m2m.v4l2_dev, "Fourcc format (0x%X) invalid.\n", | ||
816 | pix->pixelformat); | ||
817 | goto tf_out; | ||
818 | } | ||
819 | |||
691 | if (pix->field == V4L2_FIELD_ANY) | 820 | if (pix->field == V4L2_FIELD_ANY) |
692 | pix->field = V4L2_FIELD_NONE; | 821 | pix->field = V4L2_FIELD_NONE; |
693 | else if (V4L2_FIELD_NONE != pix->field) | 822 | else if (V4L2_FIELD_NONE != pix->field) |
694 | return -EINVAL; | 823 | goto tf_out; |
695 | 824 | ||
696 | if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { | 825 | if (is_output) { |
697 | max_width = variant->scaler_dis_w; | 826 | max_width = variant->scaler_dis_w; |
698 | max_height = variant->scaler_dis_w; | 827 | mod_x = ffs(variant->min_inp_pixsize) - 1; |
699 | mod_x = variant->min_inp_pixsize; | ||
700 | mod_y = variant->min_inp_pixsize; | ||
701 | } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { | ||
702 | max_width = variant->out_rot_dis_w; | ||
703 | max_height = variant->out_rot_dis_w; | ||
704 | mod_x = variant->min_out_pixsize; | ||
705 | mod_y = variant->min_out_pixsize; | ||
706 | } else { | 828 | } else { |
707 | err("Wrong stream type (%d)", f->type); | 829 | max_width = variant->out_rot_dis_w; |
708 | return -EINVAL; | 830 | mod_x = ffs(variant->min_out_pixsize) - 1; |
709 | } | 831 | } |
710 | 832 | ||
711 | dbg("max_w= %d, max_h= %d", max_width, max_height); | ||
712 | |||
713 | if (pix->height > max_height) | ||
714 | pix->height = max_height; | ||
715 | if (pix->width > max_width) | ||
716 | pix->width = max_width; | ||
717 | |||
718 | if (tiled_fmt(fmt)) { | 833 | if (tiled_fmt(fmt)) { |
719 | mod_x = 64; /* 64x32 tile */ | 834 | mod_x = 6; /* 64 x 32 pixels tile */ |
720 | mod_y = 32; | 835 | mod_y = 5; |
836 | } else { | ||
837 | if (fimc->id == 1 && fimc->variant->pix_hoff) | ||
838 | mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1; | ||
839 | else | ||
840 | mod_y = mod_x; | ||
721 | } | 841 | } |
722 | 842 | ||
723 | dbg("mod_x= 0x%X, mod_y= 0x%X", mod_x, mod_y); | 843 | dbg("mod_x: %d, mod_y: %d, max_w: %d", mod_x, mod_y, max_width); |
724 | 844 | ||
725 | pix->width = (pix->width == 0) ? mod_x : ALIGN(pix->width, mod_x); | 845 | v4l_bound_align_image(&pix->width, 16, max_width, mod_x, |
726 | pix->height = (pix->height == 0) ? mod_y : ALIGN(pix->height, mod_y); | 846 | &pix->height, 8, variant->scaler_dis_w, mod_y, 0); |
727 | 847 | ||
728 | if (pix->bytesperline == 0 || | 848 | if (pix->bytesperline == 0 || |
729 | pix->bytesperline * 8 / fmt->depth > pix->width) | 849 | (pix->bytesperline * 8 / fmt->depth) > pix->width) |
730 | pix->bytesperline = (pix->width * fmt->depth) >> 3; | 850 | pix->bytesperline = (pix->width * fmt->depth) >> 3; |
731 | 851 | ||
732 | if (pix->sizeimage == 0) | 852 | if (pix->sizeimage == 0) |
733 | pix->sizeimage = pix->height * pix->bytesperline; | 853 | pix->sizeimage = pix->height * pix->bytesperline; |
734 | 854 | ||
735 | dbg("pix->bytesperline= %d, fmt->depth= %d", | 855 | dbg("w: %d, h: %d, bpl: %d, depth: %d", |
736 | pix->bytesperline, fmt->depth); | 856 | pix->width, pix->height, pix->bytesperline, fmt->depth); |
737 | 857 | ||
738 | return 0; | 858 | ret = 0; |
739 | } | ||
740 | 859 | ||
860 | tf_out: | ||
861 | mutex_unlock(&fimc->lock); | ||
862 | return ret; | ||
863 | } | ||
741 | 864 | ||
742 | static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f) | 865 | static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f) |
743 | { | 866 | { |
@@ -750,9 +873,7 @@ static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f) | |||
750 | unsigned long flags; | 873 | unsigned long flags; |
751 | int ret = 0; | 874 | int ret = 0; |
752 | 875 | ||
753 | BUG_ON(!ctx); | 876 | ret = fimc_vidioc_try_fmt(file, priv, f); |
754 | |||
755 | ret = fimc_m2m_try_fmt(file, priv, f); | ||
756 | if (ret) | 877 | if (ret) |
757 | return ret; | 878 | return ret; |
758 | 879 | ||
@@ -785,7 +906,7 @@ static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f) | |||
785 | spin_unlock_irqrestore(&ctx->slock, flags); | 906 | spin_unlock_irqrestore(&ctx->slock, flags); |
786 | 907 | ||
787 | pix = &f->fmt.pix; | 908 | pix = &f->fmt.pix; |
788 | frame->fmt = find_format(f); | 909 | frame->fmt = find_format(f, FMT_FLAGS_M2M); |
789 | if (!frame->fmt) { | 910 | if (!frame->fmt) { |
790 | ret = -EINVAL; | 911 | ret = -EINVAL; |
791 | goto sf_out; | 912 | goto sf_out; |
@@ -857,21 +978,33 @@ static int fimc_m2m_streamoff(struct file *file, void *priv, | |||
857 | return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); | 978 | return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); |
858 | } | 979 | } |
859 | 980 | ||
860 | int fimc_m2m_queryctrl(struct file *file, void *priv, | 981 | int fimc_vidioc_queryctrl(struct file *file, void *priv, |
861 | struct v4l2_queryctrl *qc) | 982 | struct v4l2_queryctrl *qc) |
862 | { | 983 | { |
984 | struct fimc_ctx *ctx = priv; | ||
863 | struct v4l2_queryctrl *c; | 985 | struct v4l2_queryctrl *c; |
986 | |||
864 | c = get_ctrl(qc->id); | 987 | c = get_ctrl(qc->id); |
865 | if (!c) | 988 | if (c) { |
866 | return -EINVAL; | 989 | *qc = *c; |
867 | *qc = *c; | 990 | return 0; |
868 | return 0; | 991 | } |
992 | |||
993 | if (ctx->state & FIMC_CTX_CAP) | ||
994 | return v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd, | ||
995 | core, queryctrl, qc); | ||
996 | return -EINVAL; | ||
869 | } | 997 | } |
870 | 998 | ||
871 | int fimc_m2m_g_ctrl(struct file *file, void *priv, | 999 | int fimc_vidioc_g_ctrl(struct file *file, void *priv, |
872 | struct v4l2_control *ctrl) | 1000 | struct v4l2_control *ctrl) |
873 | { | 1001 | { |
874 | struct fimc_ctx *ctx = priv; | 1002 | struct fimc_ctx *ctx = priv; |
1003 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
1004 | int ret = 0; | ||
1005 | |||
1006 | if (mutex_lock_interruptible(&fimc->lock)) | ||
1007 | return -ERESTARTSYS; | ||
875 | 1008 | ||
876 | switch (ctrl->id) { | 1009 | switch (ctrl->id) { |
877 | case V4L2_CID_HFLIP: | 1010 | case V4L2_CID_HFLIP: |
@@ -884,15 +1017,22 @@ int fimc_m2m_g_ctrl(struct file *file, void *priv, | |||
884 | ctrl->value = ctx->rotation; | 1017 | ctrl->value = ctx->rotation; |
885 | break; | 1018 | break; |
886 | default: | 1019 | default: |
887 | v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, "Invalid control\n"); | 1020 | if (ctx->state & FIMC_CTX_CAP) { |
888 | return -EINVAL; | 1021 | ret = v4l2_subdev_call(fimc->vid_cap.sd, core, |
1022 | g_ctrl, ctrl); | ||
1023 | } else { | ||
1024 | v4l2_err(&fimc->m2m.v4l2_dev, | ||
1025 | "Invalid control\n"); | ||
1026 | ret = -EINVAL; | ||
1027 | } | ||
889 | } | 1028 | } |
890 | dbg("ctrl->value= %d", ctrl->value); | 1029 | dbg("ctrl->value= %d", ctrl->value); |
891 | return 0; | 1030 | |
1031 | mutex_unlock(&fimc->lock); | ||
1032 | return ret; | ||
892 | } | 1033 | } |
893 | 1034 | ||
894 | static int check_ctrl_val(struct fimc_ctx *ctx, | 1035 | int check_ctrl_val(struct fimc_ctx *ctx, struct v4l2_control *ctrl) |
895 | struct v4l2_control *ctrl) | ||
896 | { | 1036 | { |
897 | struct v4l2_queryctrl *c; | 1037 | struct v4l2_queryctrl *c; |
898 | c = get_ctrl(ctrl->id); | 1038 | c = get_ctrl(ctrl->id); |
@@ -909,22 +1049,23 @@ static int check_ctrl_val(struct fimc_ctx *ctx, | |||
909 | return 0; | 1049 | return 0; |
910 | } | 1050 | } |
911 | 1051 | ||
912 | int fimc_m2m_s_ctrl(struct file *file, void *priv, | 1052 | int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl) |
913 | struct v4l2_control *ctrl) | ||
914 | { | 1053 | { |
915 | struct fimc_ctx *ctx = priv; | ||
916 | struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; | 1054 | struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; |
1055 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
917 | unsigned long flags; | 1056 | unsigned long flags; |
918 | int ret = 0; | ||
919 | 1057 | ||
920 | ret = check_ctrl_val(ctx, ctrl); | 1058 | if (ctx->rotation != 0 && |
921 | if (ret) | 1059 | (ctrl->id == V4L2_CID_HFLIP || ctrl->id == V4L2_CID_VFLIP)) { |
922 | return ret; | 1060 | v4l2_err(&fimc->m2m.v4l2_dev, |
1061 | "Simultaneous flip and rotation is not supported\n"); | ||
1062 | return -EINVAL; | ||
1063 | } | ||
1064 | |||
1065 | spin_lock_irqsave(&ctx->slock, flags); | ||
923 | 1066 | ||
924 | switch (ctrl->id) { | 1067 | switch (ctrl->id) { |
925 | case V4L2_CID_HFLIP: | 1068 | case V4L2_CID_HFLIP: |
926 | if (ctx->rotation != 0) | ||
927 | return 0; | ||
928 | if (ctrl->value) | 1069 | if (ctrl->value) |
929 | ctx->flip |= FLIP_X_AXIS; | 1070 | ctx->flip |= FLIP_X_AXIS; |
930 | else | 1071 | else |
@@ -932,8 +1073,6 @@ int fimc_m2m_s_ctrl(struct file *file, void *priv, | |||
932 | break; | 1073 | break; |
933 | 1074 | ||
934 | case V4L2_CID_VFLIP: | 1075 | case V4L2_CID_VFLIP: |
935 | if (ctx->rotation != 0) | ||
936 | return 0; | ||
937 | if (ctrl->value) | 1076 | if (ctrl->value) |
938 | ctx->flip |= FLIP_Y_AXIS; | 1077 | ctx->flip |= FLIP_Y_AXIS; |
939 | else | 1078 | else |
@@ -941,77 +1080,95 @@ int fimc_m2m_s_ctrl(struct file *file, void *priv, | |||
941 | break; | 1080 | break; |
942 | 1081 | ||
943 | case V4L2_CID_ROTATE: | 1082 | case V4L2_CID_ROTATE: |
944 | if (ctrl->value == 90 || ctrl->value == 270) { | 1083 | /* Check for the output rotator availability */ |
945 | if (ctx->out_path == FIMC_LCDFIFO && | 1084 | if ((ctrl->value == 90 || ctrl->value == 270) && |
946 | !variant->has_inp_rot) { | 1085 | (ctx->in_path == FIMC_DMA && !variant->has_out_rot)) { |
947 | return -EINVAL; | 1086 | spin_unlock_irqrestore(&ctx->slock, flags); |
948 | } else if (ctx->in_path == FIMC_DMA && | 1087 | return -EINVAL; |
949 | !variant->has_out_rot) { | 1088 | } else { |
950 | return -EINVAL; | 1089 | ctx->rotation = ctrl->value; |
951 | } | ||
952 | } | 1090 | } |
953 | ctx->rotation = ctrl->value; | ||
954 | if (ctrl->value == 180) | ||
955 | ctx->flip = FLIP_XY_AXIS; | ||
956 | break; | 1091 | break; |
957 | 1092 | ||
958 | default: | 1093 | default: |
959 | v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, "Invalid control\n"); | 1094 | spin_unlock_irqrestore(&ctx->slock, flags); |
1095 | v4l2_err(&fimc->m2m.v4l2_dev, "Invalid control\n"); | ||
960 | return -EINVAL; | 1096 | return -EINVAL; |
961 | } | 1097 | } |
962 | spin_lock_irqsave(&ctx->slock, flags); | ||
963 | ctx->state |= FIMC_PARAMS; | 1098 | ctx->state |= FIMC_PARAMS; |
964 | spin_unlock_irqrestore(&ctx->slock, flags); | 1099 | spin_unlock_irqrestore(&ctx->slock, flags); |
1100 | |||
965 | return 0; | 1101 | return 0; |
966 | } | 1102 | } |
967 | 1103 | ||
1104 | static int fimc_m2m_s_ctrl(struct file *file, void *priv, | ||
1105 | struct v4l2_control *ctrl) | ||
1106 | { | ||
1107 | struct fimc_ctx *ctx = priv; | ||
1108 | int ret = 0; | ||
1109 | |||
1110 | ret = check_ctrl_val(ctx, ctrl); | ||
1111 | if (ret) | ||
1112 | return ret; | ||
1113 | |||
1114 | ret = fimc_s_ctrl(ctx, ctrl); | ||
1115 | return 0; | ||
1116 | } | ||
968 | 1117 | ||
969 | static int fimc_m2m_cropcap(struct file *file, void *fh, | 1118 | int fimc_vidioc_cropcap(struct file *file, void *fh, |
970 | struct v4l2_cropcap *cr) | 1119 | struct v4l2_cropcap *cr) |
971 | { | 1120 | { |
972 | struct fimc_frame *frame; | 1121 | struct fimc_frame *frame; |
973 | struct fimc_ctx *ctx = fh; | 1122 | struct fimc_ctx *ctx = fh; |
1123 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
974 | 1124 | ||
975 | frame = ctx_get_frame(ctx, cr->type); | 1125 | frame = ctx_get_frame(ctx, cr->type); |
976 | if (IS_ERR(frame)) | 1126 | if (IS_ERR(frame)) |
977 | return PTR_ERR(frame); | 1127 | return PTR_ERR(frame); |
978 | 1128 | ||
979 | cr->bounds.left = 0; | 1129 | if (mutex_lock_interruptible(&fimc->lock)) |
980 | cr->bounds.top = 0; | 1130 | return -ERESTARTSYS; |
981 | cr->bounds.width = frame->f_width; | 1131 | |
982 | cr->bounds.height = frame->f_height; | 1132 | cr->bounds.left = 0; |
983 | cr->defrect.left = frame->offs_h; | 1133 | cr->bounds.top = 0; |
984 | cr->defrect.top = frame->offs_v; | 1134 | cr->bounds.width = frame->f_width; |
985 | cr->defrect.width = frame->o_width; | 1135 | cr->bounds.height = frame->f_height; |
986 | cr->defrect.height = frame->o_height; | 1136 | cr->defrect = cr->bounds; |
1137 | |||
1138 | mutex_unlock(&fimc->lock); | ||
987 | return 0; | 1139 | return 0; |
988 | } | 1140 | } |
989 | 1141 | ||
990 | static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) | 1142 | int fimc_vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) |
991 | { | 1143 | { |
992 | struct fimc_frame *frame; | 1144 | struct fimc_frame *frame; |
993 | struct fimc_ctx *ctx = file->private_data; | 1145 | struct fimc_ctx *ctx = file->private_data; |
1146 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
994 | 1147 | ||
995 | frame = ctx_get_frame(ctx, cr->type); | 1148 | frame = ctx_get_frame(ctx, cr->type); |
996 | if (IS_ERR(frame)) | 1149 | if (IS_ERR(frame)) |
997 | return PTR_ERR(frame); | 1150 | return PTR_ERR(frame); |
998 | 1151 | ||
1152 | if (mutex_lock_interruptible(&fimc->lock)) | ||
1153 | return -ERESTARTSYS; | ||
1154 | |||
999 | cr->c.left = frame->offs_h; | 1155 | cr->c.left = frame->offs_h; |
1000 | cr->c.top = frame->offs_v; | 1156 | cr->c.top = frame->offs_v; |
1001 | cr->c.width = frame->width; | 1157 | cr->c.width = frame->width; |
1002 | cr->c.height = frame->height; | 1158 | cr->c.height = frame->height; |
1003 | 1159 | ||
1160 | mutex_unlock(&fimc->lock); | ||
1004 | return 0; | 1161 | return 0; |
1005 | } | 1162 | } |
1006 | 1163 | ||
1007 | static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) | 1164 | int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) |
1008 | { | 1165 | { |
1009 | struct fimc_ctx *ctx = file->private_data; | ||
1010 | struct fimc_dev *fimc = ctx->fimc_dev; | 1166 | struct fimc_dev *fimc = ctx->fimc_dev; |
1011 | unsigned long flags; | ||
1012 | struct fimc_frame *f; | 1167 | struct fimc_frame *f; |
1013 | u32 min_size; | 1168 | u32 min_size, halign; |
1014 | int ret = 0; | 1169 | |
1170 | f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? | ||
1171 | &ctx->s_frame : &ctx->d_frame; | ||
1015 | 1172 | ||
1016 | if (cr->c.top < 0 || cr->c.left < 0) { | 1173 | if (cr->c.top < 0 || cr->c.left < 0) { |
1017 | v4l2_err(&fimc->m2m.v4l2_dev, | 1174 | v4l2_err(&fimc->m2m.v4l2_dev, |
@@ -1019,66 +1176,98 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) | |||
1019 | return -EINVAL; | 1176 | return -EINVAL; |
1020 | } | 1177 | } |
1021 | 1178 | ||
1022 | if (cr->c.width <= 0 || cr->c.height <= 0) { | ||
1023 | v4l2_err(&fimc->m2m.v4l2_dev, | ||
1024 | "crop width and height must be greater than 0\n"); | ||
1025 | return -EINVAL; | ||
1026 | } | ||
1027 | |||
1028 | f = ctx_get_frame(ctx, cr->type); | 1179 | f = ctx_get_frame(ctx, cr->type); |
1029 | if (IS_ERR(f)) | 1180 | if (IS_ERR(f)) |
1030 | return PTR_ERR(f); | 1181 | return PTR_ERR(f); |
1031 | 1182 | ||
1032 | /* Adjust to required pixel boundary. */ | 1183 | min_size = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) |
1033 | min_size = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? | 1184 | ? fimc->variant->min_inp_pixsize |
1034 | fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize; | 1185 | : fimc->variant->min_out_pixsize; |
1035 | 1186 | ||
1036 | cr->c.width = round_down(cr->c.width, min_size); | 1187 | if (ctx->state & FIMC_CTX_M2M) { |
1037 | cr->c.height = round_down(cr->c.height, min_size); | 1188 | if (fimc->id == 1 && fimc->variant->pix_hoff) |
1038 | cr->c.left = round_down(cr->c.left + 1, min_size); | 1189 | halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1; |
1039 | cr->c.top = round_down(cr->c.top + 1, min_size); | 1190 | else |
1040 | 1191 | halign = ffs(min_size) - 1; | |
1041 | if ((cr->c.left + cr->c.width > f->o_width) | 1192 | /* there are more strict aligment requirements at camera interface */ |
1042 | || (cr->c.top + cr->c.height > f->o_height)) { | 1193 | } else { |
1043 | v4l2_err(&fimc->m2m.v4l2_dev, "Error in S_CROP params\n"); | 1194 | min_size = 16; |
1044 | return -EINVAL; | 1195 | halign = 4; |
1045 | } | 1196 | } |
1046 | 1197 | ||
1198 | v4l_bound_align_image(&cr->c.width, min_size, f->o_width, | ||
1199 | ffs(min_size) - 1, | ||
1200 | &cr->c.height, min_size, f->o_height, | ||
1201 | halign, 64/(ALIGN(f->fmt->depth, 8))); | ||
1202 | |||
1203 | /* adjust left/top if cropping rectangle is out of bounds */ | ||
1204 | if (cr->c.left + cr->c.width > f->o_width) | ||
1205 | cr->c.left = f->o_width - cr->c.width; | ||
1206 | if (cr->c.top + cr->c.height > f->o_height) | ||
1207 | cr->c.top = f->o_height - cr->c.height; | ||
1208 | |||
1209 | cr->c.left = round_down(cr->c.left, min_size); | ||
1210 | cr->c.top = round_down(cr->c.top, | ||
1211 | ctx->state & FIMC_CTX_M2M ? 8 : 16); | ||
1212 | |||
1213 | dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", | ||
1214 | cr->c.left, cr->c.top, cr->c.width, cr->c.height, | ||
1215 | f->f_width, f->f_height); | ||
1216 | |||
1217 | return 0; | ||
1218 | } | ||
1219 | |||
1220 | |||
1221 | static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) | ||
1222 | { | ||
1223 | struct fimc_ctx *ctx = file->private_data; | ||
1224 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
1225 | unsigned long flags; | ||
1226 | struct fimc_frame *f; | ||
1227 | int ret; | ||
1228 | |||
1229 | ret = fimc_try_crop(ctx, cr); | ||
1230 | if (ret) | ||
1231 | return ret; | ||
1232 | |||
1233 | f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? | ||
1234 | &ctx->s_frame : &ctx->d_frame; | ||
1235 | |||
1047 | spin_lock_irqsave(&ctx->slock, flags); | 1236 | spin_lock_irqsave(&ctx->slock, flags); |
1048 | if ((ctx->state & FIMC_SRC_FMT) && (ctx->state & FIMC_DST_FMT)) { | 1237 | if (~ctx->state & (FIMC_SRC_FMT | FIMC_DST_FMT)) { |
1049 | /* Check for the pixel scaling ratio when cropping input img. */ | 1238 | /* Check to see if scaling ratio is within supported range */ |
1050 | if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) | 1239 | if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) |
1051 | ret = fimc_check_scaler_ratio(&cr->c, &ctx->d_frame); | 1240 | ret = fimc_check_scaler_ratio(&cr->c, &ctx->d_frame); |
1052 | else if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | 1241 | else |
1053 | ret = fimc_check_scaler_ratio(&cr->c, &ctx->s_frame); | 1242 | ret = fimc_check_scaler_ratio(&cr->c, &ctx->s_frame); |
1054 | |||
1055 | if (ret) { | 1243 | if (ret) { |
1056 | spin_unlock_irqrestore(&ctx->slock, flags); | 1244 | spin_unlock_irqrestore(&ctx->slock, flags); |
1057 | v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range"); | 1245 | v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range"); |
1058 | return -EINVAL; | 1246 | return -EINVAL; |
1059 | } | 1247 | } |
1060 | } | 1248 | } |
1061 | ctx->state |= FIMC_PARAMS; | 1249 | ctx->state |= FIMC_PARAMS; |
1062 | spin_unlock_irqrestore(&ctx->slock, flags); | ||
1063 | 1250 | ||
1064 | f->offs_h = cr->c.left; | 1251 | f->offs_h = cr->c.left; |
1065 | f->offs_v = cr->c.top; | 1252 | f->offs_v = cr->c.top; |
1066 | f->width = cr->c.width; | 1253 | f->width = cr->c.width; |
1067 | f->height = cr->c.height; | 1254 | f->height = cr->c.height; |
1255 | |||
1256 | spin_unlock_irqrestore(&ctx->slock, flags); | ||
1068 | return 0; | 1257 | return 0; |
1069 | } | 1258 | } |
1070 | 1259 | ||
1071 | static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { | 1260 | static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { |
1072 | .vidioc_querycap = fimc_m2m_querycap, | 1261 | .vidioc_querycap = fimc_m2m_querycap, |
1073 | 1262 | ||
1074 | .vidioc_enum_fmt_vid_cap = fimc_m2m_enum_fmt, | 1263 | .vidioc_enum_fmt_vid_cap = fimc_vidioc_enum_fmt, |
1075 | .vidioc_enum_fmt_vid_out = fimc_m2m_enum_fmt, | 1264 | .vidioc_enum_fmt_vid_out = fimc_vidioc_enum_fmt, |
1076 | 1265 | ||
1077 | .vidioc_g_fmt_vid_cap = fimc_m2m_g_fmt, | 1266 | .vidioc_g_fmt_vid_cap = fimc_vidioc_g_fmt, |
1078 | .vidioc_g_fmt_vid_out = fimc_m2m_g_fmt, | 1267 | .vidioc_g_fmt_vid_out = fimc_vidioc_g_fmt, |
1079 | 1268 | ||
1080 | .vidioc_try_fmt_vid_cap = fimc_m2m_try_fmt, | 1269 | .vidioc_try_fmt_vid_cap = fimc_vidioc_try_fmt, |
1081 | .vidioc_try_fmt_vid_out = fimc_m2m_try_fmt, | 1270 | .vidioc_try_fmt_vid_out = fimc_vidioc_try_fmt, |
1082 | 1271 | ||
1083 | .vidioc_s_fmt_vid_cap = fimc_m2m_s_fmt, | 1272 | .vidioc_s_fmt_vid_cap = fimc_m2m_s_fmt, |
1084 | .vidioc_s_fmt_vid_out = fimc_m2m_s_fmt, | 1273 | .vidioc_s_fmt_vid_out = fimc_m2m_s_fmt, |
@@ -1092,13 +1281,13 @@ static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { | |||
1092 | .vidioc_streamon = fimc_m2m_streamon, | 1281 | .vidioc_streamon = fimc_m2m_streamon, |
1093 | .vidioc_streamoff = fimc_m2m_streamoff, | 1282 | .vidioc_streamoff = fimc_m2m_streamoff, |
1094 | 1283 | ||
1095 | .vidioc_queryctrl = fimc_m2m_queryctrl, | 1284 | .vidioc_queryctrl = fimc_vidioc_queryctrl, |
1096 | .vidioc_g_ctrl = fimc_m2m_g_ctrl, | 1285 | .vidioc_g_ctrl = fimc_vidioc_g_ctrl, |
1097 | .vidioc_s_ctrl = fimc_m2m_s_ctrl, | 1286 | .vidioc_s_ctrl = fimc_m2m_s_ctrl, |
1098 | 1287 | ||
1099 | .vidioc_g_crop = fimc_m2m_g_crop, | 1288 | .vidioc_g_crop = fimc_vidioc_g_crop, |
1100 | .vidioc_s_crop = fimc_m2m_s_crop, | 1289 | .vidioc_s_crop = fimc_m2m_s_crop, |
1101 | .vidioc_cropcap = fimc_m2m_cropcap | 1290 | .vidioc_cropcap = fimc_vidioc_cropcap |
1102 | 1291 | ||
1103 | }; | 1292 | }; |
1104 | 1293 | ||
@@ -1120,11 +1309,23 @@ static int fimc_m2m_open(struct file *file) | |||
1120 | struct fimc_ctx *ctx = NULL; | 1309 | struct fimc_ctx *ctx = NULL; |
1121 | int err = 0; | 1310 | int err = 0; |
1122 | 1311 | ||
1123 | mutex_lock(&fimc->lock); | 1312 | if (mutex_lock_interruptible(&fimc->lock)) |
1313 | return -ERESTARTSYS; | ||
1314 | |||
1315 | dbg("pid: %d, state: 0x%lx, refcnt: %d", | ||
1316 | task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt); | ||
1317 | |||
1318 | /* | ||
1319 | * Return if the corresponding video capture node | ||
1320 | * is already opened. | ||
1321 | */ | ||
1322 | if (fimc->vid_cap.refcnt > 0) { | ||
1323 | mutex_unlock(&fimc->lock); | ||
1324 | return -EBUSY; | ||
1325 | } | ||
1326 | |||
1124 | fimc->m2m.refcnt++; | 1327 | fimc->m2m.refcnt++; |
1125 | set_bit(ST_OUTDMA_RUN, &fimc->state); | 1328 | set_bit(ST_OUTDMA_RUN, &fimc->state); |
1126 | mutex_unlock(&fimc->lock); | ||
1127 | |||
1128 | 1329 | ||
1129 | ctx = kzalloc(sizeof *ctx, GFP_KERNEL); | 1330 | ctx = kzalloc(sizeof *ctx, GFP_KERNEL); |
1130 | if (!ctx) | 1331 | if (!ctx) |
@@ -1135,8 +1336,8 @@ static int fimc_m2m_open(struct file *file) | |||
1135 | /* Default color format */ | 1336 | /* Default color format */ |
1136 | ctx->s_frame.fmt = &fimc_formats[0]; | 1337 | ctx->s_frame.fmt = &fimc_formats[0]; |
1137 | ctx->d_frame.fmt = &fimc_formats[0]; | 1338 | ctx->d_frame.fmt = &fimc_formats[0]; |
1138 | /* per user process device context initialization */ | 1339 | /* Setup the device context for mem2mem mode. */ |
1139 | ctx->state = 0; | 1340 | ctx->state = FIMC_CTX_M2M; |
1140 | ctx->flags = 0; | 1341 | ctx->flags = 0; |
1141 | ctx->in_path = FIMC_DMA; | 1342 | ctx->in_path = FIMC_DMA; |
1142 | ctx->out_path = FIMC_DMA; | 1343 | ctx->out_path = FIMC_DMA; |
@@ -1147,6 +1348,8 @@ static int fimc_m2m_open(struct file *file) | |||
1147 | err = PTR_ERR(ctx->m2m_ctx); | 1348 | err = PTR_ERR(ctx->m2m_ctx); |
1148 | kfree(ctx); | 1349 | kfree(ctx); |
1149 | } | 1350 | } |
1351 | |||
1352 | mutex_unlock(&fimc->lock); | ||
1150 | return err; | 1353 | return err; |
1151 | } | 1354 | } |
1152 | 1355 | ||
@@ -1155,11 +1358,16 @@ static int fimc_m2m_release(struct file *file) | |||
1155 | struct fimc_ctx *ctx = file->private_data; | 1358 | struct fimc_ctx *ctx = file->private_data; |
1156 | struct fimc_dev *fimc = ctx->fimc_dev; | 1359 | struct fimc_dev *fimc = ctx->fimc_dev; |
1157 | 1360 | ||
1361 | mutex_lock(&fimc->lock); | ||
1362 | |||
1363 | dbg("pid: %d, state: 0x%lx, refcnt= %d", | ||
1364 | task_pid_nr(current), fimc->state, fimc->m2m.refcnt); | ||
1365 | |||
1158 | v4l2_m2m_ctx_release(ctx->m2m_ctx); | 1366 | v4l2_m2m_ctx_release(ctx->m2m_ctx); |
1159 | kfree(ctx); | 1367 | kfree(ctx); |
1160 | mutex_lock(&fimc->lock); | ||
1161 | if (--fimc->m2m.refcnt <= 0) | 1368 | if (--fimc->m2m.refcnt <= 0) |
1162 | clear_bit(ST_OUTDMA_RUN, &fimc->state); | 1369 | clear_bit(ST_OUTDMA_RUN, &fimc->state); |
1370 | |||
1163 | mutex_unlock(&fimc->lock); | 1371 | mutex_unlock(&fimc->lock); |
1164 | return 0; | 1372 | return 0; |
1165 | } | 1373 | } |
@@ -1168,6 +1376,7 @@ static unsigned int fimc_m2m_poll(struct file *file, | |||
1168 | struct poll_table_struct *wait) | 1376 | struct poll_table_struct *wait) |
1169 | { | 1377 | { |
1170 | struct fimc_ctx *ctx = file->private_data; | 1378 | struct fimc_ctx *ctx = file->private_data; |
1379 | |||
1171 | return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); | 1380 | return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); |
1172 | } | 1381 | } |
1173 | 1382 | ||
@@ -1175,6 +1384,7 @@ static unsigned int fimc_m2m_poll(struct file *file, | |||
1175 | static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma) | 1384 | static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma) |
1176 | { | 1385 | { |
1177 | struct fimc_ctx *ctx = file->private_data; | 1386 | struct fimc_ctx *ctx = file->private_data; |
1387 | |||
1178 | return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); | 1388 | return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); |
1179 | } | 1389 | } |
1180 | 1390 | ||
@@ -1322,9 +1532,11 @@ static int fimc_probe(struct platform_device *pdev) | |||
1322 | fimc->id = pdev->id; | 1532 | fimc->id = pdev->id; |
1323 | fimc->variant = drv_data->variant[fimc->id]; | 1533 | fimc->variant = drv_data->variant[fimc->id]; |
1324 | fimc->pdev = pdev; | 1534 | fimc->pdev = pdev; |
1535 | fimc->pdata = pdev->dev.platform_data; | ||
1325 | fimc->state = ST_IDLE; | 1536 | fimc->state = ST_IDLE; |
1326 | 1537 | ||
1327 | spin_lock_init(&fimc->irqlock); | 1538 | spin_lock_init(&fimc->irqlock); |
1539 | init_waitqueue_head(&fimc->irq_queue); | ||
1328 | spin_lock_init(&fimc->slock); | 1540 | spin_lock_init(&fimc->slock); |
1329 | 1541 | ||
1330 | mutex_init(&fimc->lock); | 1542 | mutex_init(&fimc->lock); |
@@ -1354,6 +1566,7 @@ static int fimc_probe(struct platform_device *pdev) | |||
1354 | ret = fimc_clk_get(fimc); | 1566 | ret = fimc_clk_get(fimc); |
1355 | if (ret) | 1567 | if (ret) |
1356 | goto err_regs_unmap; | 1568 | goto err_regs_unmap; |
1569 | clk_set_rate(fimc->clock[0], drv_data->lclk_frequency); | ||
1357 | 1570 | ||
1358 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | 1571 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
1359 | if (!res) { | 1572 | if (!res) { |
@@ -1375,11 +1588,27 @@ static int fimc_probe(struct platform_device *pdev) | |||
1375 | if (ret) | 1588 | if (ret) |
1376 | goto err_irq; | 1589 | goto err_irq; |
1377 | 1590 | ||
1591 | /* At least one camera sensor is required to register capture node */ | ||
1592 | if (fimc->pdata) { | ||
1593 | int i; | ||
1594 | for (i = 0; i < FIMC_MAX_CAMIF_CLIENTS; ++i) | ||
1595 | if (fimc->pdata->isp_info[i]) | ||
1596 | break; | ||
1597 | |||
1598 | if (i < FIMC_MAX_CAMIF_CLIENTS) { | ||
1599 | ret = fimc_register_capture_device(fimc); | ||
1600 | if (ret) | ||
1601 | goto err_m2m; | ||
1602 | } | ||
1603 | } | ||
1604 | |||
1378 | dev_dbg(&pdev->dev, "%s(): fimc-%d registered successfully\n", | 1605 | dev_dbg(&pdev->dev, "%s(): fimc-%d registered successfully\n", |
1379 | __func__, fimc->id); | 1606 | __func__, fimc->id); |
1380 | 1607 | ||
1381 | return 0; | 1608 | return 0; |
1382 | 1609 | ||
1610 | err_m2m: | ||
1611 | fimc_unregister_m2m_device(fimc); | ||
1383 | err_irq: | 1612 | err_irq: |
1384 | free_irq(fimc->irq, fimc); | 1613 | free_irq(fimc->irq, fimc); |
1385 | err_clk: | 1614 | err_clk: |
@@ -1404,6 +1633,8 @@ static int __devexit fimc_remove(struct platform_device *pdev) | |||
1404 | fimc_hw_reset(fimc); | 1633 | fimc_hw_reset(fimc); |
1405 | 1634 | ||
1406 | fimc_unregister_m2m_device(fimc); | 1635 | fimc_unregister_m2m_device(fimc); |
1636 | fimc_unregister_capture_device(fimc); | ||
1637 | |||
1407 | fimc_clk_release(fimc); | 1638 | fimc_clk_release(fimc); |
1408 | iounmap(fimc->regs); | 1639 | iounmap(fimc->regs); |
1409 | release_resource(fimc->regs_res); | 1640 | release_resource(fimc->regs_res); |
@@ -1474,7 +1705,8 @@ static struct samsung_fimc_driverdata fimc_drvdata_s5p = { | |||
1474 | [1] = &fimc01_variant_s5p, | 1705 | [1] = &fimc01_variant_s5p, |
1475 | [2] = &fimc2_variant_s5p, | 1706 | [2] = &fimc2_variant_s5p, |
1476 | }, | 1707 | }, |
1477 | .devs_cnt = 3 | 1708 | .devs_cnt = 3, |
1709 | .lclk_frequency = 133000000UL, | ||
1478 | }; | 1710 | }; |
1479 | 1711 | ||
1480 | static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = { | 1712 | static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = { |
@@ -1483,7 +1715,8 @@ static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = { | |||
1483 | [1] = &fimc01_variant_s5pv210, | 1715 | [1] = &fimc01_variant_s5pv210, |
1484 | [2] = &fimc2_variant_s5pv210, | 1716 | [2] = &fimc2_variant_s5pv210, |
1485 | }, | 1717 | }, |
1486 | .devs_cnt = 3 | 1718 | .devs_cnt = 3, |
1719 | .lclk_frequency = 166000000UL, | ||
1487 | }; | 1720 | }; |
1488 | 1721 | ||
1489 | static struct platform_device_id fimc_driver_ids[] = { | 1722 | static struct platform_device_id fimc_driver_ids[] = { |
@@ -1524,6 +1757,6 @@ static void __exit fimc_exit(void) | |||
1524 | module_init(fimc_init); | 1757 | module_init(fimc_init); |
1525 | module_exit(fimc_exit); | 1758 | module_exit(fimc_exit); |
1526 | 1759 | ||
1527 | MODULE_AUTHOR("Sylwester Nawrocki, s.nawrocki@samsung.com"); | 1760 | MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); |
1528 | MODULE_DESCRIPTION("S3C/S5P FIMC (video postprocessor) driver"); | 1761 | MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver"); |
1529 | MODULE_LICENSE("GPL"); | 1762 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 5aeb3efbd457..ce0a6b8a7d54 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h | |||
@@ -11,10 +11,14 @@ | |||
11 | #ifndef FIMC_CORE_H_ | 11 | #ifndef FIMC_CORE_H_ |
12 | #define FIMC_CORE_H_ | 12 | #define FIMC_CORE_H_ |
13 | 13 | ||
14 | /*#define DEBUG*/ | ||
15 | |||
14 | #include <linux/types.h> | 16 | #include <linux/types.h> |
15 | #include <media/videobuf-core.h> | 17 | #include <media/videobuf-core.h> |
16 | #include <media/v4l2-device.h> | 18 | #include <media/v4l2-device.h> |
17 | #include <media/v4l2-mem2mem.h> | 19 | #include <media/v4l2-mem2mem.h> |
20 | #include <media/v4l2-mediabus.h> | ||
21 | #include <media/s3c_fimc.h> | ||
18 | #include <linux/videodev2.h> | 22 | #include <linux/videodev2.h> |
19 | #include "regs-fimc.h" | 23 | #include "regs-fimc.h" |
20 | 24 | ||
@@ -28,6 +32,8 @@ | |||
28 | #define dbg(fmt, args...) | 32 | #define dbg(fmt, args...) |
29 | #endif | 33 | #endif |
30 | 34 | ||
35 | /* Time to wait for next frame VSYNC interrupt while stopping operation. */ | ||
36 | #define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) | ||
31 | #define NUM_FIMC_CLOCKS 2 | 37 | #define NUM_FIMC_CLOCKS 2 |
32 | #define MODULE_NAME "s5p-fimc" | 38 | #define MODULE_NAME "s5p-fimc" |
33 | #define FIMC_MAX_DEVS 3 | 39 | #define FIMC_MAX_DEVS 3 |
@@ -36,19 +42,41 @@ | |||
36 | #define SCALER_MAX_VRATIO 64 | 42 | #define SCALER_MAX_VRATIO 64 |
37 | #define DMA_MIN_SIZE 8 | 43 | #define DMA_MIN_SIZE 8 |
38 | 44 | ||
39 | enum { | 45 | /* FIMC device state flags */ |
46 | enum fimc_dev_flags { | ||
47 | /* for m2m node */ | ||
40 | ST_IDLE, | 48 | ST_IDLE, |
41 | ST_OUTDMA_RUN, | 49 | ST_OUTDMA_RUN, |
42 | ST_M2M_PEND, | 50 | ST_M2M_PEND, |
51 | /* for capture node */ | ||
52 | ST_CAPT_PEND, | ||
53 | ST_CAPT_RUN, | ||
54 | ST_CAPT_STREAM, | ||
55 | ST_CAPT_SHUT, | ||
43 | }; | 56 | }; |
44 | 57 | ||
45 | #define fimc_m2m_active(dev) test_bit(ST_OUTDMA_RUN, &(dev)->state) | 58 | #define fimc_m2m_active(dev) test_bit(ST_OUTDMA_RUN, &(dev)->state) |
46 | #define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state) | 59 | #define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state) |
47 | 60 | ||
61 | #define fimc_capture_running(dev) test_bit(ST_CAPT_RUN, &(dev)->state) | ||
62 | #define fimc_capture_pending(dev) test_bit(ST_CAPT_PEND, &(dev)->state) | ||
63 | |||
64 | #define fimc_capture_active(dev) \ | ||
65 | (test_bit(ST_CAPT_RUN, &(dev)->state) || \ | ||
66 | test_bit(ST_CAPT_PEND, &(dev)->state)) | ||
67 | |||
68 | #define fimc_capture_streaming(dev) \ | ||
69 | test_bit(ST_CAPT_STREAM, &(dev)->state) | ||
70 | |||
71 | #define fimc_buf_finish(dev, vid_buf) do { \ | ||
72 | spin_lock(&(dev)->irqlock); \ | ||
73 | (vid_buf)->vb.state = VIDEOBUF_DONE; \ | ||
74 | spin_unlock(&(dev)->irqlock); \ | ||
75 | wake_up(&(vid_buf)->vb.done); \ | ||
76 | } while (0) | ||
77 | |||
48 | enum fimc_datapath { | 78 | enum fimc_datapath { |
49 | FIMC_ITU_CAM_A, | 79 | FIMC_CAMERA, |
50 | FIMC_ITU_CAM_B, | ||
51 | FIMC_MIPI_CAM, | ||
52 | FIMC_DMA, | 80 | FIMC_DMA, |
53 | FIMC_LCDFIFO, | 81 | FIMC_LCDFIFO, |
54 | FIMC_WRITEBACK | 82 | FIMC_WRITEBACK |
@@ -123,20 +151,25 @@ enum fimc_color_fmt { | |||
123 | 151 | ||
124 | /** | 152 | /** |
125 | * struct fimc_fmt - the driver's internal color format data | 153 | * struct fimc_fmt - the driver's internal color format data |
154 | * @mbus_code: Media Bus pixel code, -1 if not applicable | ||
126 | * @name: format description | 155 | * @name: format description |
127 | * @fourcc: the fourcc code for this format | 156 | * @fourcc: the fourcc code for this format, 0 if not applicable |
128 | * @color: the corresponding fimc_color_fmt | 157 | * @color: the corresponding fimc_color_fmt |
129 | * @depth: number of bits per pixel | 158 | * @depth: driver's private 'number of bits per pixel' |
130 | * @buff_cnt: number of physically non-contiguous data planes | 159 | * @buff_cnt: number of physically non-contiguous data planes |
131 | * @planes_cnt: number of physically contiguous data planes | 160 | * @planes_cnt: number of physically contiguous data planes |
132 | */ | 161 | */ |
133 | struct fimc_fmt { | 162 | struct fimc_fmt { |
163 | enum v4l2_mbus_pixelcode mbus_code; | ||
134 | char *name; | 164 | char *name; |
135 | u32 fourcc; | 165 | u32 fourcc; |
136 | u32 color; | 166 | u32 color; |
137 | u32 depth; | ||
138 | u16 buff_cnt; | 167 | u16 buff_cnt; |
139 | u16 planes_cnt; | 168 | u16 planes_cnt; |
169 | u16 depth; | ||
170 | u16 flags; | ||
171 | #define FMT_FLAGS_CAM (1 << 0) | ||
172 | #define FMT_FLAGS_M2M (1 << 1) | ||
140 | }; | 173 | }; |
141 | 174 | ||
142 | /** | 175 | /** |
@@ -220,10 +253,14 @@ struct fimc_addr { | |||
220 | 253 | ||
221 | /** | 254 | /** |
222 | * struct fimc_vid_buffer - the driver's video buffer | 255 | * struct fimc_vid_buffer - the driver's video buffer |
223 | * @vb: v4l videobuf buffer | 256 | * @vb: v4l videobuf buffer |
257 | * @paddr: precalculated physical address set | ||
258 | * @index: buffer index for the output DMA engine | ||
224 | */ | 259 | */ |
225 | struct fimc_vid_buffer { | 260 | struct fimc_vid_buffer { |
226 | struct videobuf_buffer vb; | 261 | struct videobuf_buffer vb; |
262 | struct fimc_addr paddr; | ||
263 | int index; | ||
227 | }; | 264 | }; |
228 | 265 | ||
229 | /** | 266 | /** |
@@ -274,6 +311,40 @@ struct fimc_m2m_device { | |||
274 | }; | 311 | }; |
275 | 312 | ||
276 | /** | 313 | /** |
314 | * struct fimc_vid_cap - camera capture device information | ||
315 | * @ctx: hardware context data | ||
316 | * @vfd: video device node for camera capture mode | ||
317 | * @v4l2_dev: v4l2_device struct to manage subdevs | ||
318 | * @sd: pointer to camera sensor subdevice currently in use | ||
319 | * @fmt: Media Bus format configured at selected image sensor | ||
320 | * @pending_buf_q: the pending buffer queue head | ||
321 | * @active_buf_q: the queue head of buffers scheduled in hardware | ||
322 | * @vbq: the capture am video buffer queue | ||
323 | * @active_buf_cnt: number of video buffers scheduled in hardware | ||
324 | * @buf_index: index for managing the output DMA buffers | ||
325 | * @frame_count: the frame counter for statistics | ||
326 | * @reqbufs_count: the number of buffers requested in REQBUFS ioctl | ||
327 | * @input_index: input (camera sensor) index | ||
328 | * @refcnt: driver's private reference counter | ||
329 | */ | ||
330 | struct fimc_vid_cap { | ||
331 | struct fimc_ctx *ctx; | ||
332 | struct video_device *vfd; | ||
333 | struct v4l2_device v4l2_dev; | ||
334 | struct v4l2_subdev *sd; | ||
335 | struct v4l2_mbus_framefmt fmt; | ||
336 | struct list_head pending_buf_q; | ||
337 | struct list_head active_buf_q; | ||
338 | struct videobuf_queue vbq; | ||
339 | int active_buf_cnt; | ||
340 | int buf_index; | ||
341 | unsigned int frame_count; | ||
342 | unsigned int reqbufs_count; | ||
343 | int input_index; | ||
344 | int refcnt; | ||
345 | }; | ||
346 | |||
347 | /** | ||
277 | * struct samsung_fimc_variant - camera interface variant information | 348 | * struct samsung_fimc_variant - camera interface variant information |
278 | * | 349 | * |
279 | * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes | 350 | * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes |
@@ -308,10 +379,12 @@ struct samsung_fimc_variant { | |||
308 | * | 379 | * |
309 | * @variant: the variant information for this driver. | 380 | * @variant: the variant information for this driver. |
310 | * @dev_cnt: number of fimc sub-devices available in SoC | 381 | * @dev_cnt: number of fimc sub-devices available in SoC |
382 | * @lclk_frequency: fimc bus clock frequency | ||
311 | */ | 383 | */ |
312 | struct samsung_fimc_driverdata { | 384 | struct samsung_fimc_driverdata { |
313 | struct samsung_fimc_variant *variant[FIMC_MAX_DEVS]; | 385 | struct samsung_fimc_variant *variant[FIMC_MAX_DEVS]; |
314 | int devs_cnt; | 386 | unsigned long lclk_frequency; |
387 | int devs_cnt; | ||
315 | }; | 388 | }; |
316 | 389 | ||
317 | struct fimc_ctx; | 390 | struct fimc_ctx; |
@@ -322,19 +395,23 @@ struct fimc_ctx; | |||
322 | * @slock: the spinlock protecting this data structure | 395 | * @slock: the spinlock protecting this data structure |
323 | * @lock: the mutex protecting this data structure | 396 | * @lock: the mutex protecting this data structure |
324 | * @pdev: pointer to the FIMC platform device | 397 | * @pdev: pointer to the FIMC platform device |
398 | * @pdata: pointer to the device platform data | ||
325 | * @id: FIMC device index (0..2) | 399 | * @id: FIMC device index (0..2) |
326 | * @clock[]: the clocks required for FIMC operation | 400 | * @clock[]: the clocks required for FIMC operation |
327 | * @regs: the mapped hardware registers | 401 | * @regs: the mapped hardware registers |
328 | * @regs_res: the resource claimed for IO registers | 402 | * @regs_res: the resource claimed for IO registers |
329 | * @irq: interrupt number of the FIMC subdevice | 403 | * @irq: interrupt number of the FIMC subdevice |
330 | * @irqlock: spinlock protecting videobuffer queue | 404 | * @irqlock: spinlock protecting videobuffer queue |
405 | * @irq_queue: | ||
331 | * @m2m: memory-to-memory V4L2 device information | 406 | * @m2m: memory-to-memory V4L2 device information |
332 | * @state: the FIMC device state flags | 407 | * @vid_cap: camera capture device information |
408 | * @state: flags used to synchronize m2m and capture mode operation | ||
333 | */ | 409 | */ |
334 | struct fimc_dev { | 410 | struct fimc_dev { |
335 | spinlock_t slock; | 411 | spinlock_t slock; |
336 | struct mutex lock; | 412 | struct mutex lock; |
337 | struct platform_device *pdev; | 413 | struct platform_device *pdev; |
414 | struct s3c_platform_fimc *pdata; | ||
338 | struct samsung_fimc_variant *variant; | 415 | struct samsung_fimc_variant *variant; |
339 | int id; | 416 | int id; |
340 | struct clk *clock[NUM_FIMC_CLOCKS]; | 417 | struct clk *clock[NUM_FIMC_CLOCKS]; |
@@ -342,7 +419,9 @@ struct fimc_dev { | |||
342 | struct resource *regs_res; | 419 | struct resource *regs_res; |
343 | int irq; | 420 | int irq; |
344 | spinlock_t irqlock; | 421 | spinlock_t irqlock; |
422 | wait_queue_head_t irq_queue; | ||
345 | struct fimc_m2m_device m2m; | 423 | struct fimc_m2m_device m2m; |
424 | struct fimc_vid_cap vid_cap; | ||
346 | unsigned long state; | 425 | unsigned long state; |
347 | }; | 426 | }; |
348 | 427 | ||
@@ -387,6 +466,7 @@ struct fimc_ctx { | |||
387 | struct v4l2_m2m_ctx *m2m_ctx; | 466 | struct v4l2_m2m_ctx *m2m_ctx; |
388 | }; | 467 | }; |
389 | 468 | ||
469 | extern struct videobuf_queue_ops fimc_qops; | ||
390 | 470 | ||
391 | static inline int tiled_fmt(struct fimc_fmt *fmt) | 471 | static inline int tiled_fmt(struct fimc_fmt *fmt) |
392 | { | 472 | { |
@@ -433,7 +513,10 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx, | |||
433 | struct fimc_frame *frame; | 513 | struct fimc_frame *frame; |
434 | 514 | ||
435 | if (V4L2_BUF_TYPE_VIDEO_OUTPUT == type) { | 515 | if (V4L2_BUF_TYPE_VIDEO_OUTPUT == type) { |
436 | frame = &ctx->s_frame; | 516 | if (ctx->state & FIMC_CTX_M2M) |
517 | frame = &ctx->s_frame; | ||
518 | else | ||
519 | return ERR_PTR(-EINVAL); | ||
437 | } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE == type) { | 520 | } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE == type) { |
438 | frame = &ctx->d_frame; | 521 | frame = &ctx->d_frame; |
439 | } else { | 522 | } else { |
@@ -445,6 +528,13 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx, | |||
445 | return frame; | 528 | return frame; |
446 | } | 529 | } |
447 | 530 | ||
531 | static inline u32 fimc_hw_get_frame_index(struct fimc_dev *dev) | ||
532 | { | ||
533 | u32 reg = readl(dev->regs + S5P_CISTATUS); | ||
534 | return (reg & S5P_CISTATUS_FRAMECNT_MASK) >> | ||
535 | S5P_CISTATUS_FRAMECNT_SHIFT; | ||
536 | } | ||
537 | |||
448 | /* -----------------------------------------------------*/ | 538 | /* -----------------------------------------------------*/ |
449 | /* fimc-reg.c */ | 539 | /* fimc-reg.c */ |
450 | void fimc_hw_reset(struct fimc_dev *fimc); | 540 | void fimc_hw_reset(struct fimc_dev *fimc); |
@@ -462,6 +552,52 @@ void fimc_hw_set_output_path(struct fimc_ctx *ctx); | |||
462 | void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr); | 552 | void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr); |
463 | void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr, | 553 | void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr, |
464 | int index); | 554 | int index); |
555 | int fimc_hw_set_camera_source(struct fimc_dev *fimc, | ||
556 | struct s3c_fimc_isp_info *cam); | ||
557 | int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f); | ||
558 | int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, | ||
559 | struct s3c_fimc_isp_info *cam); | ||
560 | int fimc_hw_set_camera_type(struct fimc_dev *fimc, | ||
561 | struct s3c_fimc_isp_info *cam); | ||
562 | |||
563 | /* -----------------------------------------------------*/ | ||
564 | /* fimc-core.c */ | ||
565 | int fimc_vidioc_enum_fmt(struct file *file, void *priv, | ||
566 | struct v4l2_fmtdesc *f); | ||
567 | int fimc_vidioc_g_fmt(struct file *file, void *priv, | ||
568 | struct v4l2_format *f); | ||
569 | int fimc_vidioc_try_fmt(struct file *file, void *priv, | ||
570 | struct v4l2_format *f); | ||
571 | int fimc_vidioc_g_crop(struct file *file, void *fh, | ||
572 | struct v4l2_crop *cr); | ||
573 | int fimc_vidioc_cropcap(struct file *file, void *fh, | ||
574 | struct v4l2_cropcap *cr); | ||
575 | int fimc_vidioc_queryctrl(struct file *file, void *priv, | ||
576 | struct v4l2_queryctrl *qc); | ||
577 | int fimc_vidioc_g_ctrl(struct file *file, void *priv, | ||
578 | struct v4l2_control *ctrl); | ||
579 | |||
580 | int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr); | ||
581 | int check_ctrl_val(struct fimc_ctx *ctx, struct v4l2_control *ctrl); | ||
582 | int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl); | ||
583 | |||
584 | struct fimc_fmt *find_format(struct v4l2_format *f, unsigned int mask); | ||
585 | struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f, | ||
586 | unsigned int mask); | ||
587 | |||
588 | int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f); | ||
589 | int fimc_set_scaler_info(struct fimc_ctx *ctx); | ||
590 | int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags); | ||
591 | int fimc_prepare_addr(struct fimc_ctx *ctx, struct fimc_vid_buffer *buf, | ||
592 | struct fimc_frame *frame, struct fimc_addr *paddr); | ||
593 | |||
594 | /* -----------------------------------------------------*/ | ||
595 | /* fimc-capture.c */ | ||
596 | int fimc_register_capture_device(struct fimc_dev *fimc); | ||
597 | void fimc_unregister_capture_device(struct fimc_dev *fimc); | ||
598 | int fimc_sensor_sd_init(struct fimc_dev *fimc, int index); | ||
599 | int fimc_vid_cap_buf_queue(struct fimc_dev *fimc, | ||
600 | struct fimc_vid_buffer *fimc_vb); | ||
465 | 601 | ||
466 | /* Locking: the caller holds fimc->slock */ | 602 | /* Locking: the caller holds fimc->slock */ |
467 | static inline void fimc_activate_capture(struct fimc_ctx *ctx) | 603 | static inline void fimc_activate_capture(struct fimc_ctx *ctx) |
@@ -478,4 +614,51 @@ static inline void fimc_deactivate_capture(struct fimc_dev *fimc) | |||
478 | fimc_hw_en_lastirq(fimc, false); | 614 | fimc_hw_en_lastirq(fimc, false); |
479 | } | 615 | } |
480 | 616 | ||
617 | /* | ||
618 | * Add video buffer to the active buffers queue. | ||
619 | * The caller holds irqlock spinlock. | ||
620 | */ | ||
621 | static inline void active_queue_add(struct fimc_vid_cap *vid_cap, | ||
622 | struct fimc_vid_buffer *buf) | ||
623 | { | ||
624 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
625 | list_add_tail(&buf->vb.queue, &vid_cap->active_buf_q); | ||
626 | vid_cap->active_buf_cnt++; | ||
627 | } | ||
628 | |||
629 | /* | ||
630 | * Pop a video buffer from the capture active buffers queue | ||
631 | * Locking: Need to be called with dev->slock held. | ||
632 | */ | ||
633 | static inline struct fimc_vid_buffer * | ||
634 | active_queue_pop(struct fimc_vid_cap *vid_cap) | ||
635 | { | ||
636 | struct fimc_vid_buffer *buf; | ||
637 | buf = list_entry(vid_cap->active_buf_q.next, | ||
638 | struct fimc_vid_buffer, vb.queue); | ||
639 | list_del(&buf->vb.queue); | ||
640 | vid_cap->active_buf_cnt--; | ||
641 | return buf; | ||
642 | } | ||
643 | |||
644 | /* Add video buffer to the capture pending buffers queue */ | ||
645 | static inline void fimc_pending_queue_add(struct fimc_vid_cap *vid_cap, | ||
646 | struct fimc_vid_buffer *buf) | ||
647 | { | ||
648 | buf->vb.state = VIDEOBUF_QUEUED; | ||
649 | list_add_tail(&buf->vb.queue, &vid_cap->pending_buf_q); | ||
650 | } | ||
651 | |||
652 | /* Add video buffer to the capture pending buffers queue */ | ||
653 | static inline struct fimc_vid_buffer * | ||
654 | pending_queue_pop(struct fimc_vid_cap *vid_cap) | ||
655 | { | ||
656 | struct fimc_vid_buffer *buf; | ||
657 | buf = list_entry(vid_cap->pending_buf_q.next, | ||
658 | struct fimc_vid_buffer, vb.queue); | ||
659 | list_del(&buf->vb.queue); | ||
660 | return buf; | ||
661 | } | ||
662 | |||
663 | |||
481 | #endif /* FIMC_CORE_H_ */ | 664 | #endif /* FIMC_CORE_H_ */ |
diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 95adc8464693..511631a2e5c3 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/io.h> | 13 | #include <linux/io.h> |
14 | #include <linux/delay.h> | 14 | #include <linux/delay.h> |
15 | #include <mach/map.h> | 15 | #include <mach/map.h> |
16 | #include <media/s3c_fimc.h> | ||
16 | 17 | ||
17 | #include "fimc-core.h" | 18 | #include "fimc-core.h" |
18 | 19 | ||
@@ -176,6 +177,15 @@ static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx) | |||
176 | cfg = S5P_ORIG_SIZE_HOR(frame->f_width); | 177 | cfg = S5P_ORIG_SIZE_HOR(frame->f_width); |
177 | cfg |= S5P_ORIG_SIZE_VER(frame->f_height); | 178 | cfg |= S5P_ORIG_SIZE_VER(frame->f_height); |
178 | writel(cfg, dev->regs + S5P_ORGOSIZE); | 179 | writel(cfg, dev->regs + S5P_ORGOSIZE); |
180 | |||
181 | /* Select color space conversion equation (HD/SD size).*/ | ||
182 | cfg = readl(dev->regs + S5P_CIGCTRL); | ||
183 | if (frame->f_width >= 1280) /* HD */ | ||
184 | cfg |= S5P_CIGCTRL_CSC_ITU601_709; | ||
185 | else /* SD */ | ||
186 | cfg &= ~S5P_CIGCTRL_CSC_ITU601_709; | ||
187 | writel(cfg, dev->regs + S5P_CIGCTRL); | ||
188 | |||
179 | } | 189 | } |
180 | 190 | ||
181 | void fimc_hw_set_out_dma(struct fimc_ctx *ctx) | 191 | void fimc_hw_set_out_dma(struct fimc_ctx *ctx) |
@@ -231,19 +241,12 @@ static void fimc_hw_en_autoload(struct fimc_dev *dev, int enable) | |||
231 | 241 | ||
232 | void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable) | 242 | void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable) |
233 | { | 243 | { |
234 | unsigned long flags; | 244 | u32 cfg = readl(dev->regs + S5P_CIOCTRL); |
235 | u32 cfg; | ||
236 | |||
237 | spin_lock_irqsave(&dev->slock, flags); | ||
238 | |||
239 | cfg = readl(dev->regs + S5P_CIOCTRL); | ||
240 | if (enable) | 245 | if (enable) |
241 | cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE; | 246 | cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE; |
242 | else | 247 | else |
243 | cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE; | 248 | cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE; |
244 | writel(cfg, dev->regs + S5P_CIOCTRL); | 249 | writel(cfg, dev->regs + S5P_CIOCTRL); |
245 | |||
246 | spin_unlock_irqrestore(&dev->slock, flags); | ||
247 | } | 250 | } |
248 | 251 | ||
249 | static void fimc_hw_set_prescaler(struct fimc_ctx *ctx) | 252 | static void fimc_hw_set_prescaler(struct fimc_ctx *ctx) |
@@ -325,14 +328,18 @@ void fimc_hw_set_scaler(struct fimc_ctx *ctx) | |||
325 | void fimc_hw_en_capture(struct fimc_ctx *ctx) | 328 | void fimc_hw_en_capture(struct fimc_ctx *ctx) |
326 | { | 329 | { |
327 | struct fimc_dev *dev = ctx->fimc_dev; | 330 | struct fimc_dev *dev = ctx->fimc_dev; |
328 | u32 cfg; | ||
329 | 331 | ||
330 | cfg = readl(dev->regs + S5P_CIIMGCPT); | 332 | u32 cfg = readl(dev->regs + S5P_CIIMGCPT); |
331 | /* One shot mode for output DMA or freerun for FIFO. */ | 333 | |
332 | if (ctx->out_path == FIMC_DMA) | 334 | if (ctx->out_path == FIMC_DMA) { |
333 | cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE; | 335 | /* one shot mode */ |
334 | else | 336 | cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE | S5P_CIIMGCPT_IMGCPTEN; |
335 | cfg &= ~S5P_CIIMGCPT_CPT_FREN_ENABLE; | 337 | } else { |
338 | /* Continous frame capture mode (freerun). */ | ||
339 | cfg &= ~(S5P_CIIMGCPT_CPT_FREN_ENABLE | | ||
340 | S5P_CIIMGCPT_CPT_FRMOD_CNT); | ||
341 | cfg |= S5P_CIIMGCPT_IMGCPTEN; | ||
342 | } | ||
336 | 343 | ||
337 | if (ctx->scaler.enabled) | 344 | if (ctx->scaler.enabled) |
338 | cfg |= S5P_CIIMGCPT_IMGCPTEN_SC; | 345 | cfg |= S5P_CIIMGCPT_IMGCPTEN_SC; |
@@ -523,3 +530,139 @@ void fimc_hw_set_output_addr(struct fimc_dev *dev, | |||
523 | i, paddr->y, paddr->cb, paddr->cr); | 530 | i, paddr->y, paddr->cb, paddr->cr); |
524 | } while (index == -1 && ++i < FIMC_MAX_OUT_BUFS); | 531 | } while (index == -1 && ++i < FIMC_MAX_OUT_BUFS); |
525 | } | 532 | } |
533 | |||
534 | int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, | ||
535 | struct s3c_fimc_isp_info *cam) | ||
536 | { | ||
537 | u32 cfg = readl(fimc->regs + S5P_CIGCTRL); | ||
538 | |||
539 | cfg &= ~(S5P_CIGCTRL_INVPOLPCLK | S5P_CIGCTRL_INVPOLVSYNC | | ||
540 | S5P_CIGCTRL_INVPOLHREF | S5P_CIGCTRL_INVPOLHSYNC); | ||
541 | |||
542 | if (cam->flags & FIMC_CLK_INV_PCLK) | ||
543 | cfg |= S5P_CIGCTRL_INVPOLPCLK; | ||
544 | |||
545 | if (cam->flags & FIMC_CLK_INV_VSYNC) | ||
546 | cfg |= S5P_CIGCTRL_INVPOLVSYNC; | ||
547 | |||
548 | if (cam->flags & FIMC_CLK_INV_HREF) | ||
549 | cfg |= S5P_CIGCTRL_INVPOLHREF; | ||
550 | |||
551 | if (cam->flags & FIMC_CLK_INV_HSYNC) | ||
552 | cfg |= S5P_CIGCTRL_INVPOLHSYNC; | ||
553 | |||
554 | writel(cfg, fimc->regs + S5P_CIGCTRL); | ||
555 | |||
556 | return 0; | ||
557 | } | ||
558 | |||
559 | int fimc_hw_set_camera_source(struct fimc_dev *fimc, | ||
560 | struct s3c_fimc_isp_info *cam) | ||
561 | { | ||
562 | struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame; | ||
563 | u32 cfg = 0; | ||
564 | |||
565 | if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) { | ||
566 | |||
567 | switch (fimc->vid_cap.fmt.code) { | ||
568 | case V4L2_MBUS_FMT_YUYV8_2X8: | ||
569 | cfg = S5P_CISRCFMT_ORDER422_YCBYCR; | ||
570 | break; | ||
571 | case V4L2_MBUS_FMT_YVYU8_2X8: | ||
572 | cfg = S5P_CISRCFMT_ORDER422_YCRYCB; | ||
573 | break; | ||
574 | case V4L2_MBUS_FMT_VYUY8_2X8: | ||
575 | cfg = S5P_CISRCFMT_ORDER422_CRYCBY; | ||
576 | break; | ||
577 | case V4L2_MBUS_FMT_UYVY8_2X8: | ||
578 | cfg = S5P_CISRCFMT_ORDER422_CBYCRY; | ||
579 | break; | ||
580 | default: | ||
581 | err("camera image format not supported: %d", | ||
582 | fimc->vid_cap.fmt.code); | ||
583 | return -EINVAL; | ||
584 | } | ||
585 | |||
586 | if (cam->bus_type == FIMC_ITU_601) { | ||
587 | if (cam->bus_width == 8) { | ||
588 | cfg |= S5P_CISRCFMT_ITU601_8BIT; | ||
589 | } else if (cam->bus_width == 16) { | ||
590 | cfg |= S5P_CISRCFMT_ITU601_16BIT; | ||
591 | } else { | ||
592 | err("invalid bus width: %d", cam->bus_width); | ||
593 | return -EINVAL; | ||
594 | } | ||
595 | } /* else defaults to ITU-R BT.656 8-bit */ | ||
596 | } | ||
597 | |||
598 | cfg |= S5P_CISRCFMT_HSIZE(f->o_width) | S5P_CISRCFMT_VSIZE(f->o_height); | ||
599 | writel(cfg, fimc->regs + S5P_CISRCFMT); | ||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | |||
604 | int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f) | ||
605 | { | ||
606 | u32 hoff2, voff2; | ||
607 | |||
608 | u32 cfg = readl(fimc->regs + S5P_CIWDOFST); | ||
609 | |||
610 | cfg &= ~(S5P_CIWDOFST_HOROFF_MASK | S5P_CIWDOFST_VEROFF_MASK); | ||
611 | cfg |= S5P_CIWDOFST_OFF_EN | | ||
612 | S5P_CIWDOFST_HOROFF(f->offs_h) | | ||
613 | S5P_CIWDOFST_VEROFF(f->offs_v); | ||
614 | |||
615 | writel(cfg, fimc->regs + S5P_CIWDOFST); | ||
616 | |||
617 | /* See CIWDOFSTn register description in the datasheet for details. */ | ||
618 | hoff2 = f->o_width - f->width - f->offs_h; | ||
619 | voff2 = f->o_height - f->height - f->offs_v; | ||
620 | cfg = S5P_CIWDOFST2_HOROFF(hoff2) | S5P_CIWDOFST2_VEROFF(voff2); | ||
621 | |||
622 | writel(cfg, fimc->regs + S5P_CIWDOFST2); | ||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | int fimc_hw_set_camera_type(struct fimc_dev *fimc, | ||
627 | struct s3c_fimc_isp_info *cam) | ||
628 | { | ||
629 | u32 cfg, tmp; | ||
630 | struct fimc_vid_cap *vid_cap = &fimc->vid_cap; | ||
631 | |||
632 | cfg = readl(fimc->regs + S5P_CIGCTRL); | ||
633 | |||
634 | /* Select ITU B interface, disable Writeback path and test pattern. */ | ||
635 | cfg &= ~(S5P_CIGCTRL_TESTPAT_MASK | S5P_CIGCTRL_SELCAM_ITU_A | | ||
636 | S5P_CIGCTRL_SELCAM_MIPI | S5P_CIGCTRL_CAMIF_SELWB | | ||
637 | S5P_CIGCTRL_SELCAM_MIPI_A); | ||
638 | |||
639 | if (cam->bus_type == FIMC_MIPI_CSI2) { | ||
640 | cfg |= S5P_CIGCTRL_SELCAM_MIPI; | ||
641 | |||
642 | if (cam->mux_id == 0) | ||
643 | cfg |= S5P_CIGCTRL_SELCAM_MIPI_A; | ||
644 | |||
645 | /* TODO: add remaining supported formats. */ | ||
646 | if (vid_cap->fmt.code == V4L2_MBUS_FMT_VYUY8_2X8) { | ||
647 | tmp = S5P_CSIIMGFMT_YCBCR422_8BIT; | ||
648 | } else { | ||
649 | err("camera image format not supported: %d", | ||
650 | vid_cap->fmt.code); | ||
651 | return -EINVAL; | ||
652 | } | ||
653 | writel(tmp | (0x1 << 8), fimc->regs + S5P_CSIIMGFMT); | ||
654 | |||
655 | } else if (cam->bus_type == FIMC_ITU_601 || | ||
656 | cam->bus_type == FIMC_ITU_656) { | ||
657 | if (cam->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */ | ||
658 | cfg |= S5P_CIGCTRL_SELCAM_ITU_A; | ||
659 | } else if (cam->bus_type == FIMC_LCD_WB) { | ||
660 | cfg |= S5P_CIGCTRL_CAMIF_SELWB; | ||
661 | } else { | ||
662 | err("invalid camera bus type selected\n"); | ||
663 | return -EINVAL; | ||
664 | } | ||
665 | writel(cfg, fimc->regs + S5P_CIGCTRL); | ||
666 | |||
667 | return 0; | ||
668 | } | ||
diff --git a/include/media/s3c_fimc.h b/include/media/s3c_fimc.h new file mode 100644 index 000000000000..ca1b6738e4a4 --- /dev/null +++ b/include/media/s3c_fimc.h | |||
@@ -0,0 +1,60 @@ | |||
1 | /* | ||
2 | * Samsung S5P SoC camera interface driver header | ||
3 | * | ||
4 | * Copyright (c) 2010 Samsung Electronics Co., Ltd | ||
5 | * Author: Sylwester Nawrocki, <s.nawrocki@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef S3C_FIMC_H_ | ||
13 | #define S3C_FIMC_H_ | ||
14 | |||
15 | enum cam_bus_type { | ||
16 | FIMC_ITU_601 = 1, | ||
17 | FIMC_ITU_656, | ||
18 | FIMC_MIPI_CSI2, | ||
19 | FIMC_LCD_WB, /* FIFO link from LCD mixer */ | ||
20 | }; | ||
21 | |||
22 | #define FIMC_CLK_INV_PCLK (1 << 0) | ||
23 | #define FIMC_CLK_INV_VSYNC (1 << 1) | ||
24 | #define FIMC_CLK_INV_HREF (1 << 2) | ||
25 | #define FIMC_CLK_INV_HSYNC (1 << 3) | ||
26 | |||
27 | struct i2c_board_info; | ||
28 | |||
29 | /** | ||
30 | * struct s3c_fimc_isp_info - image sensor information required for host | ||
31 | * interace configuration. | ||
32 | * | ||
33 | * @board_info: pointer to I2C subdevice's board info | ||
34 | * @bus_type: determines bus type, MIPI, ITU-R BT.601 etc. | ||
35 | * @i2c_bus_num: i2c control bus id the sensor is attached to | ||
36 | * @mux_id: FIMC camera interface multiplexer index (separate for MIPI and ITU) | ||
37 | * @bus_width: camera data bus width in bits | ||
38 | * @flags: flags defining bus signals polarity inversion (High by default) | ||
39 | */ | ||
40 | struct s3c_fimc_isp_info { | ||
41 | struct i2c_board_info *board_info; | ||
42 | enum cam_bus_type bus_type; | ||
43 | u16 i2c_bus_num; | ||
44 | u16 mux_id; | ||
45 | u16 bus_width; | ||
46 | u16 flags; | ||
47 | }; | ||
48 | |||
49 | |||
50 | #define FIMC_MAX_CAMIF_CLIENTS 2 | ||
51 | |||
52 | /** | ||
53 | * struct s3c_platform_fimc - camera host interface platform data | ||
54 | * | ||
55 | * @isp_info: properties of camera sensor required for host interface setup | ||
56 | */ | ||
57 | struct s3c_platform_fimc { | ||
58 | struct s3c_fimc_isp_info *isp_info[FIMC_MAX_CAMIF_CLIENTS]; | ||
59 | }; | ||
60 | #endif /* S3C_FIMC_H_ */ | ||