diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/media/video/sh_mobile_ceu_camera.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/media/video/sh_mobile_ceu_camera.c')
-rw-r--r-- | drivers/media/video/sh_mobile_ceu_camera.c | 2220 |
1 files changed, 2220 insertions, 0 deletions
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c new file mode 100644 index 00000000000..e54089802b6 --- /dev/null +++ b/drivers/media/video/sh_mobile_ceu_camera.c | |||
@@ -0,0 +1,2220 @@ | |||
1 | /* | ||
2 | * V4L2 Driver for SuperH Mobile CEU interface | ||
3 | * | ||
4 | * Copyright (C) 2008 Magnus Damm | ||
5 | * | ||
6 | * Based on V4L2 Driver for PXA camera host - "pxa_camera.c", | ||
7 | * | ||
8 | * Copyright (C) 2006, Sascha Hauer, Pengutronix | ||
9 | * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | */ | ||
16 | |||
17 | #include <linux/init.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/io.h> | ||
20 | #include <linux/completion.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/dma-mapping.h> | ||
23 | #include <linux/errno.h> | ||
24 | #include <linux/fs.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/mm.h> | ||
28 | #include <linux/moduleparam.h> | ||
29 | #include <linux/time.h> | ||
30 | #include <linux/slab.h> | ||
31 | #include <linux/device.h> | ||
32 | #include <linux/platform_device.h> | ||
33 | #include <linux/videodev2.h> | ||
34 | #include <linux/pm_runtime.h> | ||
35 | #include <linux/sched.h> | ||
36 | |||
37 | #include <media/v4l2-common.h> | ||
38 | #include <media/v4l2-dev.h> | ||
39 | #include <media/soc_camera.h> | ||
40 | #include <media/sh_mobile_ceu.h> | ||
41 | #include <media/sh_mobile_csi2.h> | ||
42 | #include <media/videobuf2-dma-contig.h> | ||
43 | #include <media/v4l2-mediabus.h> | ||
44 | #include <media/soc_mediabus.h> | ||
45 | |||
46 | /* register offsets for sh7722 / sh7723 */ | ||
47 | |||
48 | #define CAPSR 0x00 /* Capture start register */ | ||
49 | #define CAPCR 0x04 /* Capture control register */ | ||
50 | #define CAMCR 0x08 /* Capture interface control register */ | ||
51 | #define CMCYR 0x0c /* Capture interface cycle register */ | ||
52 | #define CAMOR 0x10 /* Capture interface offset register */ | ||
53 | #define CAPWR 0x14 /* Capture interface width register */ | ||
54 | #define CAIFR 0x18 /* Capture interface input format register */ | ||
55 | #define CSTCR 0x20 /* Camera strobe control register (<= sh7722) */ | ||
56 | #define CSECR 0x24 /* Camera strobe emission count register (<= sh7722) */ | ||
57 | #define CRCNTR 0x28 /* CEU register control register */ | ||
58 | #define CRCMPR 0x2c /* CEU register forcible control register */ | ||
59 | #define CFLCR 0x30 /* Capture filter control register */ | ||
60 | #define CFSZR 0x34 /* Capture filter size clip register */ | ||
61 | #define CDWDR 0x38 /* Capture destination width register */ | ||
62 | #define CDAYR 0x3c /* Capture data address Y register */ | ||
63 | #define CDACR 0x40 /* Capture data address C register */ | ||
64 | #define CDBYR 0x44 /* Capture data bottom-field address Y register */ | ||
65 | #define CDBCR 0x48 /* Capture data bottom-field address C register */ | ||
66 | #define CBDSR 0x4c /* Capture bundle destination size register */ | ||
67 | #define CFWCR 0x5c /* Firewall operation control register */ | ||
68 | #define CLFCR 0x60 /* Capture low-pass filter control register */ | ||
69 | #define CDOCR 0x64 /* Capture data output control register */ | ||
70 | #define CDDCR 0x68 /* Capture data complexity level register */ | ||
71 | #define CDDAR 0x6c /* Capture data complexity level address register */ | ||
72 | #define CEIER 0x70 /* Capture event interrupt enable register */ | ||
73 | #define CETCR 0x74 /* Capture event flag clear register */ | ||
74 | #define CSTSR 0x7c /* Capture status register */ | ||
75 | #define CSRTR 0x80 /* Capture software reset register */ | ||
76 | #define CDSSR 0x84 /* Capture data size register */ | ||
77 | #define CDAYR2 0x90 /* Capture data address Y register 2 */ | ||
78 | #define CDACR2 0x94 /* Capture data address C register 2 */ | ||
79 | #define CDBYR2 0x98 /* Capture data bottom-field address Y register 2 */ | ||
80 | #define CDBCR2 0x9c /* Capture data bottom-field address C register 2 */ | ||
81 | |||
82 | #undef DEBUG_GEOMETRY | ||
83 | #ifdef DEBUG_GEOMETRY | ||
84 | #define dev_geo dev_info | ||
85 | #else | ||
86 | #define dev_geo dev_dbg | ||
87 | #endif | ||
88 | |||
89 | /* per video frame buffer */ | ||
90 | struct sh_mobile_ceu_buffer { | ||
91 | struct vb2_buffer vb; /* v4l buffer must be first */ | ||
92 | struct list_head queue; | ||
93 | enum v4l2_mbus_pixelcode code; | ||
94 | }; | ||
95 | |||
96 | struct sh_mobile_ceu_dev { | ||
97 | struct soc_camera_host ici; | ||
98 | struct soc_camera_device *icd; | ||
99 | struct platform_device *csi2_pdev; | ||
100 | |||
101 | unsigned int irq; | ||
102 | void __iomem *base; | ||
103 | unsigned long video_limit; | ||
104 | |||
105 | spinlock_t lock; /* Protects video buffer lists */ | ||
106 | struct list_head capture; | ||
107 | struct vb2_buffer *active; | ||
108 | struct vb2_alloc_ctx *alloc_ctx; | ||
109 | |||
110 | struct sh_mobile_ceu_info *pdata; | ||
111 | struct completion complete; | ||
112 | |||
113 | u32 cflcr; | ||
114 | |||
115 | enum v4l2_field field; | ||
116 | int sequence; | ||
117 | |||
118 | unsigned int image_mode:1; | ||
119 | unsigned int is_16bit:1; | ||
120 | unsigned int frozen:1; | ||
121 | }; | ||
122 | |||
123 | struct sh_mobile_ceu_cam { | ||
124 | /* CEU offsets within scaled by the CEU camera output */ | ||
125 | unsigned int ceu_left; | ||
126 | unsigned int ceu_top; | ||
127 | /* Client output, as seen by the CEU */ | ||
128 | unsigned int width; | ||
129 | unsigned int height; | ||
130 | /* | ||
131 | * User window from S_CROP / G_CROP, produced by client cropping and | ||
132 | * scaling, CEU scaling and CEU cropping, mapped back onto the client | ||
133 | * input window | ||
134 | */ | ||
135 | struct v4l2_rect subrect; | ||
136 | /* Camera cropping rectangle */ | ||
137 | struct v4l2_rect rect; | ||
138 | const struct soc_mbus_pixelfmt *extra_fmt; | ||
139 | enum v4l2_mbus_pixelcode code; | ||
140 | }; | ||
141 | |||
142 | static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_buffer *vb) | ||
143 | { | ||
144 | return container_of(vb, struct sh_mobile_ceu_buffer, vb); | ||
145 | } | ||
146 | |||
147 | static unsigned long make_bus_param(struct sh_mobile_ceu_dev *pcdev) | ||
148 | { | ||
149 | unsigned long flags; | ||
150 | |||
151 | flags = SOCAM_MASTER | | ||
152 | SOCAM_PCLK_SAMPLE_RISING | | ||
153 | SOCAM_HSYNC_ACTIVE_HIGH | | ||
154 | SOCAM_HSYNC_ACTIVE_LOW | | ||
155 | SOCAM_VSYNC_ACTIVE_HIGH | | ||
156 | SOCAM_VSYNC_ACTIVE_LOW | | ||
157 | SOCAM_DATA_ACTIVE_HIGH; | ||
158 | |||
159 | if (pcdev->pdata->flags & SH_CEU_FLAG_USE_8BIT_BUS) | ||
160 | flags |= SOCAM_DATAWIDTH_8; | ||
161 | |||
162 | if (pcdev->pdata->flags & SH_CEU_FLAG_USE_16BIT_BUS) | ||
163 | flags |= SOCAM_DATAWIDTH_16; | ||
164 | |||
165 | if (flags & SOCAM_DATAWIDTH_MASK) | ||
166 | return flags; | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | static void ceu_write(struct sh_mobile_ceu_dev *priv, | ||
172 | unsigned long reg_offs, u32 data) | ||
173 | { | ||
174 | iowrite32(data, priv->base + reg_offs); | ||
175 | } | ||
176 | |||
177 | static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs) | ||
178 | { | ||
179 | return ioread32(priv->base + reg_offs); | ||
180 | } | ||
181 | |||
182 | static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev) | ||
183 | { | ||
184 | int i, success = 0; | ||
185 | struct soc_camera_device *icd = pcdev->icd; | ||
186 | |||
187 | ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ | ||
188 | |||
189 | /* wait CSTSR.CPTON bit */ | ||
190 | for (i = 0; i < 1000; i++) { | ||
191 | if (!(ceu_read(pcdev, CSTSR) & 1)) { | ||
192 | success++; | ||
193 | break; | ||
194 | } | ||
195 | udelay(1); | ||
196 | } | ||
197 | |||
198 | /* wait CAPSR.CPKIL bit */ | ||
199 | for (i = 0; i < 1000; i++) { | ||
200 | if (!(ceu_read(pcdev, CAPSR) & (1 << 16))) { | ||
201 | success++; | ||
202 | break; | ||
203 | } | ||
204 | udelay(1); | ||
205 | } | ||
206 | |||
207 | |||
208 | if (2 != success) { | ||
209 | dev_warn(icd->pdev, "soft reset time out\n"); | ||
210 | return -EIO; | ||
211 | } | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | /* | ||
217 | * Videobuf operations | ||
218 | */ | ||
219 | static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq, | ||
220 | unsigned int *count, unsigned int *num_planes, | ||
221 | unsigned long sizes[], void *alloc_ctxs[]) | ||
222 | { | ||
223 | struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq); | ||
224 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
225 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
226 | int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, | ||
227 | icd->current_fmt->host_fmt); | ||
228 | |||
229 | if (bytes_per_line < 0) | ||
230 | return bytes_per_line; | ||
231 | |||
232 | *num_planes = 1; | ||
233 | |||
234 | pcdev->sequence = 0; | ||
235 | sizes[0] = bytes_per_line * icd->user_height; | ||
236 | alloc_ctxs[0] = pcdev->alloc_ctx; | ||
237 | |||
238 | if (!*count) | ||
239 | *count = 2; | ||
240 | |||
241 | if (pcdev->video_limit) { | ||
242 | if (PAGE_ALIGN(sizes[0]) * *count > pcdev->video_limit) | ||
243 | *count = pcdev->video_limit / PAGE_ALIGN(sizes[0]); | ||
244 | } | ||
245 | |||
246 | dev_dbg(icd->parent, "count=%d, size=%lu\n", *count, sizes[0]); | ||
247 | |||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | #define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */ | ||
252 | #define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */ | ||
253 | #define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */ | ||
254 | #define CEU_CEIER_VBP (1 << 20) /* vbp error */ | ||
255 | #define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */ | ||
256 | #define CEU_CEIER_MASK (CEU_CEIER_CPEIE | CEU_CEIER_VBP) | ||
257 | |||
258 | |||
259 | /* | ||
260 | * return value doesn't reflex the success/failure to queue the new buffer, | ||
261 | * but rather the status of the previous buffer. | ||
262 | */ | ||
263 | static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) | ||
264 | { | ||
265 | struct soc_camera_device *icd = pcdev->icd; | ||
266 | dma_addr_t phys_addr_top, phys_addr_bottom; | ||
267 | unsigned long top1, top2; | ||
268 | unsigned long bottom1, bottom2; | ||
269 | u32 status; | ||
270 | int ret = 0; | ||
271 | |||
272 | /* | ||
273 | * The hardware is _very_ picky about this sequence. Especially | ||
274 | * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge | ||
275 | * several not-so-well documented interrupt sources in CETCR. | ||
276 | */ | ||
277 | ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK); | ||
278 | status = ceu_read(pcdev, CETCR); | ||
279 | ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC); | ||
280 | if (!pcdev->frozen) | ||
281 | ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK); | ||
282 | ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP); | ||
283 | ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW); | ||
284 | |||
285 | /* | ||
286 | * When a VBP interrupt occurs, a capture end interrupt does not occur | ||
287 | * and the image of that frame is not captured correctly. So, soft reset | ||
288 | * is needed here. | ||
289 | */ | ||
290 | if (status & CEU_CEIER_VBP) { | ||
291 | sh_mobile_ceu_soft_reset(pcdev); | ||
292 | ret = -EIO; | ||
293 | } | ||
294 | |||
295 | if (pcdev->frozen) { | ||
296 | complete(&pcdev->complete); | ||
297 | return ret; | ||
298 | } | ||
299 | |||
300 | if (!pcdev->active) | ||
301 | return ret; | ||
302 | |||
303 | if (V4L2_FIELD_INTERLACED_BT == pcdev->field) { | ||
304 | top1 = CDBYR; | ||
305 | top2 = CDBCR; | ||
306 | bottom1 = CDAYR; | ||
307 | bottom2 = CDACR; | ||
308 | } else { | ||
309 | top1 = CDAYR; | ||
310 | top2 = CDACR; | ||
311 | bottom1 = CDBYR; | ||
312 | bottom2 = CDBCR; | ||
313 | } | ||
314 | |||
315 | phys_addr_top = vb2_dma_contig_plane_paddr(pcdev->active, 0); | ||
316 | |||
317 | ceu_write(pcdev, top1, phys_addr_top); | ||
318 | if (V4L2_FIELD_NONE != pcdev->field) { | ||
319 | phys_addr_bottom = phys_addr_top + icd->user_width; | ||
320 | ceu_write(pcdev, bottom1, phys_addr_bottom); | ||
321 | } | ||
322 | |||
323 | switch (icd->current_fmt->host_fmt->fourcc) { | ||
324 | case V4L2_PIX_FMT_NV12: | ||
325 | case V4L2_PIX_FMT_NV21: | ||
326 | case V4L2_PIX_FMT_NV16: | ||
327 | case V4L2_PIX_FMT_NV61: | ||
328 | phys_addr_top += icd->user_width * | ||
329 | icd->user_height; | ||
330 | ceu_write(pcdev, top2, phys_addr_top); | ||
331 | if (V4L2_FIELD_NONE != pcdev->field) { | ||
332 | phys_addr_bottom = phys_addr_top + icd->user_width; | ||
333 | ceu_write(pcdev, bottom2, phys_addr_bottom); | ||
334 | } | ||
335 | } | ||
336 | |||
337 | ceu_write(pcdev, CAPSR, 0x1); /* start capture */ | ||
338 | |||
339 | return ret; | ||
340 | } | ||
341 | |||
342 | static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb) | ||
343 | { | ||
344 | struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq); | ||
345 | struct sh_mobile_ceu_buffer *buf; | ||
346 | int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, | ||
347 | icd->current_fmt->host_fmt); | ||
348 | unsigned long size; | ||
349 | |||
350 | if (bytes_per_line < 0) | ||
351 | return bytes_per_line; | ||
352 | |||
353 | buf = to_ceu_vb(vb); | ||
354 | |||
355 | dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, | ||
356 | vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); | ||
357 | |||
358 | /* Added list head initialization on alloc */ | ||
359 | WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb); | ||
360 | |||
361 | #ifdef DEBUG | ||
362 | /* | ||
363 | * This can be useful if you want to see if we actually fill | ||
364 | * the buffer with something | ||
365 | */ | ||
366 | if (vb2_plane_vaddr(vb, 0)) | ||
367 | memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0)); | ||
368 | #endif | ||
369 | |||
370 | BUG_ON(NULL == icd->current_fmt); | ||
371 | |||
372 | size = icd->user_height * bytes_per_line; | ||
373 | |||
374 | if (vb2_plane_size(vb, 0) < size) { | ||
375 | dev_err(icd->parent, "Buffer too small (%lu < %lu)\n", | ||
376 | vb2_plane_size(vb, 0), size); | ||
377 | return -ENOBUFS; | ||
378 | } | ||
379 | |||
380 | vb2_set_plane_payload(vb, 0, size); | ||
381 | |||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) | ||
386 | { | ||
387 | struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq); | ||
388 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
389 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
390 | struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); | ||
391 | |||
392 | dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, | ||
393 | vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); | ||
394 | |||
395 | spin_lock_irq(&pcdev->lock); | ||
396 | list_add_tail(&buf->queue, &pcdev->capture); | ||
397 | |||
398 | if (!pcdev->active) { | ||
399 | /* | ||
400 | * Because there were no active buffer at this moment, | ||
401 | * we are not interested in the return value of | ||
402 | * sh_mobile_ceu_capture here. | ||
403 | */ | ||
404 | pcdev->active = vb; | ||
405 | sh_mobile_ceu_capture(pcdev); | ||
406 | } | ||
407 | spin_unlock_irq(&pcdev->lock); | ||
408 | } | ||
409 | |||
410 | static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) | ||
411 | { | ||
412 | struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq); | ||
413 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
414 | struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); | ||
415 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
416 | |||
417 | spin_lock_irq(&pcdev->lock); | ||
418 | |||
419 | if (pcdev->active == vb) { | ||
420 | /* disable capture (release DMA buffer), reset */ | ||
421 | ceu_write(pcdev, CAPSR, 1 << 16); | ||
422 | pcdev->active = NULL; | ||
423 | } | ||
424 | |||
425 | /* | ||
426 | * Doesn't hurt also if the list is empty, but it hurts, if queuing the | ||
427 | * buffer failed, and .buf_init() hasn't been called | ||
428 | */ | ||
429 | if (buf->queue.next) | ||
430 | list_del_init(&buf->queue); | ||
431 | |||
432 | spin_unlock_irq(&pcdev->lock); | ||
433 | } | ||
434 | |||
435 | static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) | ||
436 | { | ||
437 | /* This is for locking debugging only */ | ||
438 | INIT_LIST_HEAD(&to_ceu_vb(vb)->queue); | ||
439 | return 0; | ||
440 | } | ||
441 | |||
442 | static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q) | ||
443 | { | ||
444 | struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq); | ||
445 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
446 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
447 | struct list_head *buf_head, *tmp; | ||
448 | |||
449 | spin_lock_irq(&pcdev->lock); | ||
450 | |||
451 | pcdev->active = NULL; | ||
452 | |||
453 | list_for_each_safe(buf_head, tmp, &pcdev->capture) | ||
454 | list_del_init(buf_head); | ||
455 | |||
456 | spin_unlock_irq(&pcdev->lock); | ||
457 | |||
458 | return sh_mobile_ceu_soft_reset(pcdev); | ||
459 | } | ||
460 | |||
461 | static struct vb2_ops sh_mobile_ceu_videobuf_ops = { | ||
462 | .queue_setup = sh_mobile_ceu_videobuf_setup, | ||
463 | .buf_prepare = sh_mobile_ceu_videobuf_prepare, | ||
464 | .buf_queue = sh_mobile_ceu_videobuf_queue, | ||
465 | .buf_cleanup = sh_mobile_ceu_videobuf_release, | ||
466 | .buf_init = sh_mobile_ceu_videobuf_init, | ||
467 | .wait_prepare = soc_camera_unlock, | ||
468 | .wait_finish = soc_camera_lock, | ||
469 | .stop_streaming = sh_mobile_ceu_stop_streaming, | ||
470 | }; | ||
471 | |||
472 | static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) | ||
473 | { | ||
474 | struct sh_mobile_ceu_dev *pcdev = data; | ||
475 | struct vb2_buffer *vb; | ||
476 | int ret; | ||
477 | |||
478 | spin_lock(&pcdev->lock); | ||
479 | |||
480 | vb = pcdev->active; | ||
481 | if (!vb) | ||
482 | /* Stale interrupt from a released buffer */ | ||
483 | goto out; | ||
484 | |||
485 | list_del_init(&to_ceu_vb(vb)->queue); | ||
486 | |||
487 | if (!list_empty(&pcdev->capture)) | ||
488 | pcdev->active = &list_entry(pcdev->capture.next, | ||
489 | struct sh_mobile_ceu_buffer, queue)->vb; | ||
490 | else | ||
491 | pcdev->active = NULL; | ||
492 | |||
493 | ret = sh_mobile_ceu_capture(pcdev); | ||
494 | do_gettimeofday(&vb->v4l2_buf.timestamp); | ||
495 | if (!ret) { | ||
496 | vb->v4l2_buf.field = pcdev->field; | ||
497 | vb->v4l2_buf.sequence = pcdev->sequence++; | ||
498 | } | ||
499 | vb2_buffer_done(vb, ret < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); | ||
500 | |||
501 | out: | ||
502 | spin_unlock(&pcdev->lock); | ||
503 | |||
504 | return IRQ_HANDLED; | ||
505 | } | ||
506 | |||
507 | static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev) | ||
508 | { | ||
509 | struct v4l2_subdev *sd; | ||
510 | |||
511 | if (!pcdev->csi2_pdev) | ||
512 | return NULL; | ||
513 | |||
514 | v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev) | ||
515 | if (&pcdev->csi2_pdev->dev == v4l2_get_subdevdata(sd)) | ||
516 | return sd; | ||
517 | |||
518 | return NULL; | ||
519 | } | ||
520 | |||
521 | /* Called with .video_lock held */ | ||
522 | static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) | ||
523 | { | ||
524 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
525 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
526 | struct v4l2_subdev *csi2_sd; | ||
527 | int ret; | ||
528 | |||
529 | if (pcdev->icd) | ||
530 | return -EBUSY; | ||
531 | |||
532 | dev_info(icd->parent, | ||
533 | "SuperH Mobile CEU driver attached to camera %d\n", | ||
534 | icd->devnum); | ||
535 | |||
536 | pm_runtime_get_sync(ici->v4l2_dev.dev); | ||
537 | |||
538 | ret = sh_mobile_ceu_soft_reset(pcdev); | ||
539 | |||
540 | csi2_sd = find_csi2(pcdev); | ||
541 | |||
542 | ret = v4l2_subdev_call(csi2_sd, core, s_power, 1); | ||
543 | if (ret != -ENODEV && ret != -ENOIOCTLCMD && ret < 0) { | ||
544 | pm_runtime_put_sync(ici->v4l2_dev.dev); | ||
545 | } else { | ||
546 | pcdev->icd = icd; | ||
547 | ret = 0; | ||
548 | } | ||
549 | |||
550 | return ret; | ||
551 | } | ||
552 | |||
553 | /* Called with .video_lock held */ | ||
554 | static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) | ||
555 | { | ||
556 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
557 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
558 | struct v4l2_subdev *csi2_sd = find_csi2(pcdev); | ||
559 | |||
560 | BUG_ON(icd != pcdev->icd); | ||
561 | |||
562 | v4l2_subdev_call(csi2_sd, core, s_power, 0); | ||
563 | /* disable capture, disable interrupts */ | ||
564 | ceu_write(pcdev, CEIER, 0); | ||
565 | sh_mobile_ceu_soft_reset(pcdev); | ||
566 | |||
567 | /* make sure active buffer is canceled */ | ||
568 | spin_lock_irq(&pcdev->lock); | ||
569 | if (pcdev->active) { | ||
570 | list_del_init(&to_ceu_vb(pcdev->active)->queue); | ||
571 | vb2_buffer_done(pcdev->active, VB2_BUF_STATE_ERROR); | ||
572 | pcdev->active = NULL; | ||
573 | } | ||
574 | spin_unlock_irq(&pcdev->lock); | ||
575 | |||
576 | pm_runtime_put_sync(ici->v4l2_dev.dev); | ||
577 | |||
578 | dev_info(icd->parent, | ||
579 | "SuperH Mobile CEU driver detached from camera %d\n", | ||
580 | icd->devnum); | ||
581 | |||
582 | pcdev->icd = NULL; | ||
583 | } | ||
584 | |||
585 | /* | ||
586 | * See chapter 29.4.12 "Capture Filter Control Register (CFLCR)" | ||
587 | * in SH7722 Hardware Manual | ||
588 | */ | ||
589 | static unsigned int size_dst(unsigned int src, unsigned int scale) | ||
590 | { | ||
591 | unsigned int mant_pre = scale >> 12; | ||
592 | if (!src || !scale) | ||
593 | return src; | ||
594 | return ((mant_pre + 2 * (src - 1)) / (2 * mant_pre) - 1) * | ||
595 | mant_pre * 4096 / scale + 1; | ||
596 | } | ||
597 | |||
598 | static u16 calc_scale(unsigned int src, unsigned int *dst) | ||
599 | { | ||
600 | u16 scale; | ||
601 | |||
602 | if (src == *dst) | ||
603 | return 0; | ||
604 | |||
605 | scale = (src * 4096 / *dst) & ~7; | ||
606 | |||
607 | while (scale > 4096 && size_dst(src, scale) < *dst) | ||
608 | scale -= 8; | ||
609 | |||
610 | *dst = size_dst(src, scale); | ||
611 | |||
612 | return scale; | ||
613 | } | ||
614 | |||
615 | /* rect is guaranteed to not exceed the scaled camera rectangle */ | ||
616 | static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) | ||
617 | { | ||
618 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
619 | struct sh_mobile_ceu_cam *cam = icd->host_priv; | ||
620 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
621 | unsigned int height, width, cdwdr_width, in_width, in_height; | ||
622 | unsigned int left_offset, top_offset; | ||
623 | u32 camor; | ||
624 | |||
625 | dev_geo(icd->parent, "Crop %ux%u@%u:%u\n", | ||
626 | icd->user_width, icd->user_height, cam->ceu_left, cam->ceu_top); | ||
627 | |||
628 | left_offset = cam->ceu_left; | ||
629 | top_offset = cam->ceu_top; | ||
630 | |||
631 | /* CEU cropping (CFSZR) is applied _after_ the scaling filter (CFLCR) */ | ||
632 | if (pcdev->image_mode) { | ||
633 | in_width = cam->width; | ||
634 | if (!pcdev->is_16bit) { | ||
635 | in_width *= 2; | ||
636 | left_offset *= 2; | ||
637 | } | ||
638 | width = icd->user_width; | ||
639 | cdwdr_width = icd->user_width; | ||
640 | } else { | ||
641 | int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, | ||
642 | icd->current_fmt->host_fmt); | ||
643 | unsigned int w_factor; | ||
644 | |||
645 | width = icd->user_width; | ||
646 | |||
647 | switch (icd->current_fmt->host_fmt->packing) { | ||
648 | case SOC_MBUS_PACKING_2X8_PADHI: | ||
649 | w_factor = 2; | ||
650 | break; | ||
651 | default: | ||
652 | w_factor = 1; | ||
653 | } | ||
654 | |||
655 | in_width = cam->width * w_factor; | ||
656 | left_offset = left_offset * w_factor; | ||
657 | |||
658 | if (bytes_per_line < 0) | ||
659 | cdwdr_width = icd->user_width; | ||
660 | else | ||
661 | cdwdr_width = bytes_per_line; | ||
662 | } | ||
663 | |||
664 | height = icd->user_height; | ||
665 | in_height = cam->height; | ||
666 | if (V4L2_FIELD_NONE != pcdev->field) { | ||
667 | height /= 2; | ||
668 | in_height /= 2; | ||
669 | top_offset /= 2; | ||
670 | cdwdr_width *= 2; | ||
671 | } | ||
672 | |||
673 | /* CSI2 special configuration */ | ||
674 | if (pcdev->pdata->csi2) { | ||
675 | in_width = ((in_width - 2) * 2); | ||
676 | left_offset *= 2; | ||
677 | } | ||
678 | |||
679 | /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */ | ||
680 | camor = left_offset | (top_offset << 16); | ||
681 | |||
682 | dev_geo(icd->parent, | ||
683 | "CAMOR 0x%x, CAPWR 0x%x, CFSZR 0x%x, CDWDR 0x%x\n", camor, | ||
684 | (in_height << 16) | in_width, (height << 16) | width, | ||
685 | cdwdr_width); | ||
686 | |||
687 | ceu_write(pcdev, CAMOR, camor); | ||
688 | ceu_write(pcdev, CAPWR, (in_height << 16) | in_width); | ||
689 | ceu_write(pcdev, CFSZR, (height << 16) | width); | ||
690 | ceu_write(pcdev, CDWDR, cdwdr_width); | ||
691 | } | ||
692 | |||
693 | static u32 capture_save_reset(struct sh_mobile_ceu_dev *pcdev) | ||
694 | { | ||
695 | u32 capsr = ceu_read(pcdev, CAPSR); | ||
696 | ceu_write(pcdev, CAPSR, 1 << 16); /* reset, stop capture */ | ||
697 | return capsr; | ||
698 | } | ||
699 | |||
700 | static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr) | ||
701 | { | ||
702 | unsigned long timeout = jiffies + 10 * HZ; | ||
703 | |||
704 | /* | ||
705 | * Wait until the end of the current frame. It can take a long time, | ||
706 | * but if it has been aborted by a CAPSR reset, it shoule exit sooner. | ||
707 | */ | ||
708 | while ((ceu_read(pcdev, CSTSR) & 1) && time_before(jiffies, timeout)) | ||
709 | msleep(1); | ||
710 | |||
711 | if (time_after(jiffies, timeout)) { | ||
712 | dev_err(pcdev->ici.v4l2_dev.dev, | ||
713 | "Timeout waiting for frame end! Interface problem?\n"); | ||
714 | return; | ||
715 | } | ||
716 | |||
717 | /* Wait until reset clears, this shall not hang... */ | ||
718 | while (ceu_read(pcdev, CAPSR) & (1 << 16)) | ||
719 | udelay(10); | ||
720 | |||
721 | /* Anything to restore? */ | ||
722 | if (capsr & ~(1 << 16)) | ||
723 | ceu_write(pcdev, CAPSR, capsr); | ||
724 | } | ||
725 | |||
726 | /* Capture is not running, no interrupts, no locking needed */ | ||
727 | static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, | ||
728 | __u32 pixfmt) | ||
729 | { | ||
730 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
731 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
732 | int ret; | ||
733 | unsigned long camera_flags, common_flags, value; | ||
734 | int yuv_lineskip; | ||
735 | struct sh_mobile_ceu_cam *cam = icd->host_priv; | ||
736 | u32 capsr = capture_save_reset(pcdev); | ||
737 | |||
738 | camera_flags = icd->ops->query_bus_param(icd); | ||
739 | common_flags = soc_camera_bus_param_compatible(camera_flags, | ||
740 | make_bus_param(pcdev)); | ||
741 | if (!common_flags) | ||
742 | return -EINVAL; | ||
743 | |||
744 | /* Make choises, based on platform preferences */ | ||
745 | if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && | ||
746 | (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { | ||
747 | if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW) | ||
748 | common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH; | ||
749 | else | ||
750 | common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW; | ||
751 | } | ||
752 | |||
753 | if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) && | ||
754 | (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) { | ||
755 | if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW) | ||
756 | common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH; | ||
757 | else | ||
758 | common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW; | ||
759 | } | ||
760 | |||
761 | ret = icd->ops->set_bus_param(icd, common_flags); | ||
762 | if (ret < 0) | ||
763 | return ret; | ||
764 | |||
765 | switch (common_flags & SOCAM_DATAWIDTH_MASK) { | ||
766 | case SOCAM_DATAWIDTH_8: | ||
767 | pcdev->is_16bit = 0; | ||
768 | break; | ||
769 | case SOCAM_DATAWIDTH_16: | ||
770 | pcdev->is_16bit = 1; | ||
771 | break; | ||
772 | default: | ||
773 | return -EINVAL; | ||
774 | } | ||
775 | |||
776 | ceu_write(pcdev, CRCNTR, 0); | ||
777 | ceu_write(pcdev, CRCMPR, 0); | ||
778 | |||
779 | value = 0x00000010; /* data fetch by default */ | ||
780 | yuv_lineskip = 0; | ||
781 | |||
782 | switch (icd->current_fmt->host_fmt->fourcc) { | ||
783 | case V4L2_PIX_FMT_NV12: | ||
784 | case V4L2_PIX_FMT_NV21: | ||
785 | yuv_lineskip = 1; /* skip for NV12/21, no skip for NV16/61 */ | ||
786 | /* fall-through */ | ||
787 | case V4L2_PIX_FMT_NV16: | ||
788 | case V4L2_PIX_FMT_NV61: | ||
789 | switch (cam->code) { | ||
790 | case V4L2_MBUS_FMT_UYVY8_2X8: | ||
791 | value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */ | ||
792 | break; | ||
793 | case V4L2_MBUS_FMT_VYUY8_2X8: | ||
794 | value = 0x00000100; /* Cr0, Y0, Cb0, Y1 */ | ||
795 | break; | ||
796 | case V4L2_MBUS_FMT_YUYV8_2X8: | ||
797 | value = 0x00000200; /* Y0, Cb0, Y1, Cr0 */ | ||
798 | break; | ||
799 | case V4L2_MBUS_FMT_YVYU8_2X8: | ||
800 | value = 0x00000300; /* Y0, Cr0, Y1, Cb0 */ | ||
801 | break; | ||
802 | default: | ||
803 | BUG(); | ||
804 | } | ||
805 | } | ||
806 | |||
807 | if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV21 || | ||
808 | icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV61) | ||
809 | value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */ | ||
810 | |||
811 | value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; | ||
812 | value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; | ||
813 | value |= pcdev->is_16bit ? 1 << 12 : 0; | ||
814 | |||
815 | /* CSI2 mode */ | ||
816 | if (pcdev->pdata->csi2) | ||
817 | value |= 3 << 12; | ||
818 | |||
819 | ceu_write(pcdev, CAMCR, value); | ||
820 | |||
821 | ceu_write(pcdev, CAPCR, 0x00300000); | ||
822 | |||
823 | switch (pcdev->field) { | ||
824 | case V4L2_FIELD_INTERLACED_TB: | ||
825 | value = 0x101; | ||
826 | break; | ||
827 | case V4L2_FIELD_INTERLACED_BT: | ||
828 | value = 0x102; | ||
829 | break; | ||
830 | default: | ||
831 | value = 0; | ||
832 | break; | ||
833 | } | ||
834 | ceu_write(pcdev, CAIFR, value); | ||
835 | |||
836 | sh_mobile_ceu_set_rect(icd); | ||
837 | mdelay(1); | ||
838 | |||
839 | dev_geo(icd->parent, "CFLCR 0x%x\n", pcdev->cflcr); | ||
840 | ceu_write(pcdev, CFLCR, pcdev->cflcr); | ||
841 | |||
842 | /* | ||
843 | * A few words about byte order (observed in Big Endian mode) | ||
844 | * | ||
845 | * In data fetch mode bytes are received in chunks of 8 bytes. | ||
846 | * D0, D1, D2, D3, D4, D5, D6, D7 (D0 received first) | ||
847 | * | ||
848 | * The data is however by default written to memory in reverse order: | ||
849 | * D7, D6, D5, D4, D3, D2, D1, D0 (D7 written to lowest byte) | ||
850 | * | ||
851 | * The lowest three bits of CDOCR allows us to do swapping, | ||
852 | * using 7 we swap the data bytes to match the incoming order: | ||
853 | * D0, D1, D2, D3, D4, D5, D6, D7 | ||
854 | */ | ||
855 | value = 0x00000017; | ||
856 | if (yuv_lineskip) | ||
857 | value &= ~0x00000010; /* convert 4:2:2 -> 4:2:0 */ | ||
858 | |||
859 | ceu_write(pcdev, CDOCR, value); | ||
860 | ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */ | ||
861 | |||
862 | dev_dbg(icd->parent, "S_FMT successful for %c%c%c%c %ux%u\n", | ||
863 | pixfmt & 0xff, (pixfmt >> 8) & 0xff, | ||
864 | (pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff, | ||
865 | icd->user_width, icd->user_height); | ||
866 | |||
867 | capture_restore(pcdev, capsr); | ||
868 | |||
869 | /* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */ | ||
870 | return 0; | ||
871 | } | ||
872 | |||
873 | static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, | ||
874 | unsigned char buswidth) | ||
875 | { | ||
876 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
877 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
878 | unsigned long camera_flags, common_flags; | ||
879 | |||
880 | camera_flags = icd->ops->query_bus_param(icd); | ||
881 | common_flags = soc_camera_bus_param_compatible(camera_flags, | ||
882 | make_bus_param(pcdev)); | ||
883 | if (!common_flags || buswidth > 16 || | ||
884 | (buswidth > 8 && !(common_flags & SOCAM_DATAWIDTH_16))) | ||
885 | return -EINVAL; | ||
886 | |||
887 | return 0; | ||
888 | } | ||
889 | |||
890 | static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = { | ||
891 | { | ||
892 | .fourcc = V4L2_PIX_FMT_NV12, | ||
893 | .name = "NV12", | ||
894 | .bits_per_sample = 12, | ||
895 | .packing = SOC_MBUS_PACKING_NONE, | ||
896 | .order = SOC_MBUS_ORDER_LE, | ||
897 | }, { | ||
898 | .fourcc = V4L2_PIX_FMT_NV21, | ||
899 | .name = "NV21", | ||
900 | .bits_per_sample = 12, | ||
901 | .packing = SOC_MBUS_PACKING_NONE, | ||
902 | .order = SOC_MBUS_ORDER_LE, | ||
903 | }, { | ||
904 | .fourcc = V4L2_PIX_FMT_NV16, | ||
905 | .name = "NV16", | ||
906 | .bits_per_sample = 16, | ||
907 | .packing = SOC_MBUS_PACKING_NONE, | ||
908 | .order = SOC_MBUS_ORDER_LE, | ||
909 | }, { | ||
910 | .fourcc = V4L2_PIX_FMT_NV61, | ||
911 | .name = "NV61", | ||
912 | .bits_per_sample = 16, | ||
913 | .packing = SOC_MBUS_PACKING_NONE, | ||
914 | .order = SOC_MBUS_ORDER_LE, | ||
915 | }, | ||
916 | }; | ||
917 | |||
918 | /* This will be corrected as we get more formats */ | ||
919 | static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt) | ||
920 | { | ||
921 | return fmt->packing == SOC_MBUS_PACKING_NONE || | ||
922 | (fmt->bits_per_sample == 8 && | ||
923 | fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) || | ||
924 | (fmt->bits_per_sample > 8 && | ||
925 | fmt->packing == SOC_MBUS_PACKING_EXTEND16); | ||
926 | } | ||
927 | |||
928 | static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect); | ||
929 | |||
930 | static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int idx, | ||
931 | struct soc_camera_format_xlate *xlate) | ||
932 | { | ||
933 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); | ||
934 | struct device *dev = icd->parent; | ||
935 | struct soc_camera_host *ici = to_soc_camera_host(dev); | ||
936 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
937 | int ret, k, n; | ||
938 | int formats = 0; | ||
939 | struct sh_mobile_ceu_cam *cam; | ||
940 | enum v4l2_mbus_pixelcode code; | ||
941 | const struct soc_mbus_pixelfmt *fmt; | ||
942 | |||
943 | ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code); | ||
944 | if (ret < 0) | ||
945 | /* No more formats */ | ||
946 | return 0; | ||
947 | |||
948 | fmt = soc_mbus_get_fmtdesc(code); | ||
949 | if (!fmt) { | ||
950 | dev_warn(dev, "unsupported format code #%u: %d\n", idx, code); | ||
951 | return 0; | ||
952 | } | ||
953 | |||
954 | if (!pcdev->pdata->csi2) { | ||
955 | ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); | ||
956 | if (ret < 0) | ||
957 | return 0; | ||
958 | } | ||
959 | |||
960 | if (!icd->host_priv) { | ||
961 | struct v4l2_mbus_framefmt mf; | ||
962 | struct v4l2_rect rect; | ||
963 | int shift = 0; | ||
964 | |||
965 | /* FIXME: subwindow is lost between close / open */ | ||
966 | |||
967 | /* Cache current client geometry */ | ||
968 | ret = client_g_rect(sd, &rect); | ||
969 | if (ret < 0) | ||
970 | return ret; | ||
971 | |||
972 | /* First time */ | ||
973 | ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); | ||
974 | if (ret < 0) | ||
975 | return ret; | ||
976 | |||
977 | while ((mf.width > 2560 || mf.height > 1920) && shift < 4) { | ||
978 | /* Try 2560x1920, 1280x960, 640x480, 320x240 */ | ||
979 | mf.width = 2560 >> shift; | ||
980 | mf.height = 1920 >> shift; | ||
981 | ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video, | ||
982 | s_mbus_fmt, &mf); | ||
983 | if (ret < 0) | ||
984 | return ret; | ||
985 | shift++; | ||
986 | } | ||
987 | |||
988 | if (shift == 4) { | ||
989 | dev_err(dev, "Failed to configure the client below %ux%x\n", | ||
990 | mf.width, mf.height); | ||
991 | return -EIO; | ||
992 | } | ||
993 | |||
994 | dev_geo(dev, "camera fmt %ux%u\n", mf.width, mf.height); | ||
995 | |||
996 | cam = kzalloc(sizeof(*cam), GFP_KERNEL); | ||
997 | if (!cam) | ||
998 | return -ENOMEM; | ||
999 | |||
1000 | /* We are called with current camera crop, initialise subrect with it */ | ||
1001 | cam->rect = rect; | ||
1002 | cam->subrect = rect; | ||
1003 | |||
1004 | cam->width = mf.width; | ||
1005 | cam->height = mf.height; | ||
1006 | |||
1007 | cam->width = mf.width; | ||
1008 | cam->height = mf.height; | ||
1009 | |||
1010 | icd->host_priv = cam; | ||
1011 | } else { | ||
1012 | cam = icd->host_priv; | ||
1013 | } | ||
1014 | |||
1015 | /* Beginning of a pass */ | ||
1016 | if (!idx) | ||
1017 | cam->extra_fmt = NULL; | ||
1018 | |||
1019 | switch (code) { | ||
1020 | case V4L2_MBUS_FMT_UYVY8_2X8: | ||
1021 | case V4L2_MBUS_FMT_VYUY8_2X8: | ||
1022 | case V4L2_MBUS_FMT_YUYV8_2X8: | ||
1023 | case V4L2_MBUS_FMT_YVYU8_2X8: | ||
1024 | if (cam->extra_fmt) | ||
1025 | break; | ||
1026 | |||
1027 | /* | ||
1028 | * Our case is simple so far: for any of the above four camera | ||
1029 | * formats we add all our four synthesized NV* formats, so, | ||
1030 | * just marking the device with a single flag suffices. If | ||
1031 | * the format generation rules are more complex, you would have | ||
1032 | * to actually hang your already added / counted formats onto | ||
1033 | * the host_priv pointer and check whether the format you're | ||
1034 | * going to add now is already there. | ||
1035 | */ | ||
1036 | cam->extra_fmt = sh_mobile_ceu_formats; | ||
1037 | |||
1038 | n = ARRAY_SIZE(sh_mobile_ceu_formats); | ||
1039 | formats += n; | ||
1040 | for (k = 0; xlate && k < n; k++) { | ||
1041 | xlate->host_fmt = &sh_mobile_ceu_formats[k]; | ||
1042 | xlate->code = code; | ||
1043 | xlate++; | ||
1044 | dev_dbg(dev, "Providing format %s using code %d\n", | ||
1045 | sh_mobile_ceu_formats[k].name, code); | ||
1046 | } | ||
1047 | break; | ||
1048 | default: | ||
1049 | if (!sh_mobile_ceu_packing_supported(fmt)) | ||
1050 | return 0; | ||
1051 | } | ||
1052 | |||
1053 | /* Generic pass-through */ | ||
1054 | formats++; | ||
1055 | if (xlate) { | ||
1056 | xlate->host_fmt = fmt; | ||
1057 | xlate->code = code; | ||
1058 | xlate++; | ||
1059 | dev_dbg(dev, "Providing format %s in pass-through mode\n", | ||
1060 | fmt->name); | ||
1061 | } | ||
1062 | |||
1063 | return formats; | ||
1064 | } | ||
1065 | |||
1066 | static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd) | ||
1067 | { | ||
1068 | kfree(icd->host_priv); | ||
1069 | icd->host_priv = NULL; | ||
1070 | } | ||
1071 | |||
1072 | /* Check if any dimension of r1 is smaller than respective one of r2 */ | ||
1073 | static bool is_smaller(struct v4l2_rect *r1, struct v4l2_rect *r2) | ||
1074 | { | ||
1075 | return r1->width < r2->width || r1->height < r2->height; | ||
1076 | } | ||
1077 | |||
1078 | /* Check if r1 fails to cover r2 */ | ||
1079 | static bool is_inside(struct v4l2_rect *r1, struct v4l2_rect *r2) | ||
1080 | { | ||
1081 | return r1->left > r2->left || r1->top > r2->top || | ||
1082 | r1->left + r1->width < r2->left + r2->width || | ||
1083 | r1->top + r1->height < r2->top + r2->height; | ||
1084 | } | ||
1085 | |||
1086 | static unsigned int scale_down(unsigned int size, unsigned int scale) | ||
1087 | { | ||
1088 | return (size * 4096 + scale / 2) / scale; | ||
1089 | } | ||
1090 | |||
1091 | static unsigned int calc_generic_scale(unsigned int input, unsigned int output) | ||
1092 | { | ||
1093 | return (input * 4096 + output / 2) / output; | ||
1094 | } | ||
1095 | |||
1096 | /* Get and store current client crop */ | ||
1097 | static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect) | ||
1098 | { | ||
1099 | struct v4l2_crop crop; | ||
1100 | struct v4l2_cropcap cap; | ||
1101 | int ret; | ||
1102 | |||
1103 | crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1104 | |||
1105 | ret = v4l2_subdev_call(sd, video, g_crop, &crop); | ||
1106 | if (!ret) { | ||
1107 | *rect = crop.c; | ||
1108 | return ret; | ||
1109 | } | ||
1110 | |||
1111 | /* Camera driver doesn't support .g_crop(), assume default rectangle */ | ||
1112 | cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1113 | |||
1114 | ret = v4l2_subdev_call(sd, video, cropcap, &cap); | ||
1115 | if (!ret) | ||
1116 | *rect = cap.defrect; | ||
1117 | |||
1118 | return ret; | ||
1119 | } | ||
1120 | |||
1121 | /* Client crop has changed, update our sub-rectangle to remain within the area */ | ||
1122 | static void update_subrect(struct sh_mobile_ceu_cam *cam) | ||
1123 | { | ||
1124 | struct v4l2_rect *rect = &cam->rect, *subrect = &cam->subrect; | ||
1125 | |||
1126 | if (rect->width < subrect->width) | ||
1127 | subrect->width = rect->width; | ||
1128 | |||
1129 | if (rect->height < subrect->height) | ||
1130 | subrect->height = rect->height; | ||
1131 | |||
1132 | if (rect->left > subrect->left) | ||
1133 | subrect->left = rect->left; | ||
1134 | else if (rect->left + rect->width > | ||
1135 | subrect->left + subrect->width) | ||
1136 | subrect->left = rect->left + rect->width - | ||
1137 | subrect->width; | ||
1138 | |||
1139 | if (rect->top > subrect->top) | ||
1140 | subrect->top = rect->top; | ||
1141 | else if (rect->top + rect->height > | ||
1142 | subrect->top + subrect->height) | ||
1143 | subrect->top = rect->top + rect->height - | ||
1144 | subrect->height; | ||
1145 | } | ||
1146 | |||
1147 | /* | ||
1148 | * The common for both scaling and cropping iterative approach is: | ||
1149 | * 1. try if the client can produce exactly what requested by the user | ||
1150 | * 2. if (1) failed, try to double the client image until we get one big enough | ||
1151 | * 3. if (2) failed, try to request the maximum image | ||
1152 | */ | ||
1153 | static int client_s_crop(struct soc_camera_device *icd, struct v4l2_crop *crop, | ||
1154 | struct v4l2_crop *cam_crop) | ||
1155 | { | ||
1156 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); | ||
1157 | struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c; | ||
1158 | struct device *dev = sd->v4l2_dev->dev; | ||
1159 | struct sh_mobile_ceu_cam *cam = icd->host_priv; | ||
1160 | struct v4l2_cropcap cap; | ||
1161 | int ret; | ||
1162 | unsigned int width, height; | ||
1163 | |||
1164 | v4l2_subdev_call(sd, video, s_crop, crop); | ||
1165 | ret = client_g_rect(sd, cam_rect); | ||
1166 | if (ret < 0) | ||
1167 | return ret; | ||
1168 | |||
1169 | /* | ||
1170 | * Now cam_crop contains the current camera input rectangle, and it must | ||
1171 | * be within camera cropcap bounds | ||
1172 | */ | ||
1173 | if (!memcmp(rect, cam_rect, sizeof(*rect))) { | ||
1174 | /* Even if camera S_CROP failed, but camera rectangle matches */ | ||
1175 | dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n", | ||
1176 | rect->width, rect->height, rect->left, rect->top); | ||
1177 | cam->rect = *cam_rect; | ||
1178 | return 0; | ||
1179 | } | ||
1180 | |||
1181 | /* Try to fix cropping, that camera hasn't managed to set */ | ||
1182 | dev_geo(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n", | ||
1183 | cam_rect->width, cam_rect->height, | ||
1184 | cam_rect->left, cam_rect->top, | ||
1185 | rect->width, rect->height, rect->left, rect->top); | ||
1186 | |||
1187 | /* We need sensor maximum rectangle */ | ||
1188 | ret = v4l2_subdev_call(sd, video, cropcap, &cap); | ||
1189 | if (ret < 0) | ||
1190 | return ret; | ||
1191 | |||
1192 | /* Put user requested rectangle within sensor bounds */ | ||
1193 | soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2, | ||
1194 | cap.bounds.width); | ||
1195 | soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4, | ||
1196 | cap.bounds.height); | ||
1197 | |||
1198 | /* | ||
1199 | * Popular special case - some cameras can only handle fixed sizes like | ||
1200 | * QVGA, VGA,... Take care to avoid infinite loop. | ||
1201 | */ | ||
1202 | width = max(cam_rect->width, 2); | ||
1203 | height = max(cam_rect->height, 2); | ||
1204 | |||
1205 | /* | ||
1206 | * Loop as long as sensor is not covering the requested rectangle and | ||
1207 | * is still within its bounds | ||
1208 | */ | ||
1209 | while (!ret && (is_smaller(cam_rect, rect) || | ||
1210 | is_inside(cam_rect, rect)) && | ||
1211 | (cap.bounds.width > width || cap.bounds.height > height)) { | ||
1212 | |||
1213 | width *= 2; | ||
1214 | height *= 2; | ||
1215 | |||
1216 | cam_rect->width = width; | ||
1217 | cam_rect->height = height; | ||
1218 | |||
1219 | /* | ||
1220 | * We do not know what capabilities the camera has to set up | ||
1221 | * left and top borders. We could try to be smarter in iterating | ||
1222 | * them, e.g., if camera current left is to the right of the | ||
1223 | * target left, set it to the middle point between the current | ||
1224 | * left and minimum left. But that would add too much | ||
1225 | * complexity: we would have to iterate each border separately. | ||
1226 | * Instead we just drop to the left and top bounds. | ||
1227 | */ | ||
1228 | if (cam_rect->left > rect->left) | ||
1229 | cam_rect->left = cap.bounds.left; | ||
1230 | |||
1231 | if (cam_rect->left + cam_rect->width < rect->left + rect->width) | ||
1232 | cam_rect->width = rect->left + rect->width - | ||
1233 | cam_rect->left; | ||
1234 | |||
1235 | if (cam_rect->top > rect->top) | ||
1236 | cam_rect->top = cap.bounds.top; | ||
1237 | |||
1238 | if (cam_rect->top + cam_rect->height < rect->top + rect->height) | ||
1239 | cam_rect->height = rect->top + rect->height - | ||
1240 | cam_rect->top; | ||
1241 | |||
1242 | v4l2_subdev_call(sd, video, s_crop, cam_crop); | ||
1243 | ret = client_g_rect(sd, cam_rect); | ||
1244 | dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret, | ||
1245 | cam_rect->width, cam_rect->height, | ||
1246 | cam_rect->left, cam_rect->top); | ||
1247 | } | ||
1248 | |||
1249 | /* S_CROP must not modify the rectangle */ | ||
1250 | if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) { | ||
1251 | /* | ||
1252 | * The camera failed to configure a suitable cropping, | ||
1253 | * we cannot use the current rectangle, set to max | ||
1254 | */ | ||
1255 | *cam_rect = cap.bounds; | ||
1256 | v4l2_subdev_call(sd, video, s_crop, cam_crop); | ||
1257 | ret = client_g_rect(sd, cam_rect); | ||
1258 | dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret, | ||
1259 | cam_rect->width, cam_rect->height, | ||
1260 | cam_rect->left, cam_rect->top); | ||
1261 | } | ||
1262 | |||
1263 | if (!ret) { | ||
1264 | cam->rect = *cam_rect; | ||
1265 | update_subrect(cam); | ||
1266 | } | ||
1267 | |||
1268 | return ret; | ||
1269 | } | ||
1270 | |||
1271 | /* Iterative s_mbus_fmt, also updates cached client crop on success */ | ||
1272 | static int client_s_fmt(struct soc_camera_device *icd, | ||
1273 | struct v4l2_mbus_framefmt *mf, bool ceu_can_scale) | ||
1274 | { | ||
1275 | struct sh_mobile_ceu_cam *cam = icd->host_priv; | ||
1276 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); | ||
1277 | struct device *dev = icd->parent; | ||
1278 | unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h; | ||
1279 | unsigned int max_width, max_height; | ||
1280 | struct v4l2_cropcap cap; | ||
1281 | int ret; | ||
1282 | |||
1283 | ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video, | ||
1284 | s_mbus_fmt, mf); | ||
1285 | if (ret < 0) | ||
1286 | return ret; | ||
1287 | |||
1288 | dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height); | ||
1289 | |||
1290 | if ((width == mf->width && height == mf->height) || !ceu_can_scale) | ||
1291 | goto update_cache; | ||
1292 | |||
1293 | cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1294 | |||
1295 | ret = v4l2_subdev_call(sd, video, cropcap, &cap); | ||
1296 | if (ret < 0) | ||
1297 | return ret; | ||
1298 | |||
1299 | max_width = min(cap.bounds.width, 2560); | ||
1300 | max_height = min(cap.bounds.height, 1920); | ||
1301 | |||
1302 | /* Camera set a format, but geometry is not precise, try to improve */ | ||
1303 | tmp_w = mf->width; | ||
1304 | tmp_h = mf->height; | ||
1305 | |||
1306 | /* width <= max_width && height <= max_height - guaranteed by try_fmt */ | ||
1307 | while ((width > tmp_w || height > tmp_h) && | ||
1308 | tmp_w < max_width && tmp_h < max_height) { | ||
1309 | tmp_w = min(2 * tmp_w, max_width); | ||
1310 | tmp_h = min(2 * tmp_h, max_height); | ||
1311 | mf->width = tmp_w; | ||
1312 | mf->height = tmp_h; | ||
1313 | ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video, | ||
1314 | s_mbus_fmt, mf); | ||
1315 | dev_geo(dev, "Camera scaled to %ux%u\n", | ||
1316 | mf->width, mf->height); | ||
1317 | if (ret < 0) { | ||
1318 | /* This shouldn't happen */ | ||
1319 | dev_err(dev, "Client failed to set format: %d\n", ret); | ||
1320 | return ret; | ||
1321 | } | ||
1322 | } | ||
1323 | |||
1324 | update_cache: | ||
1325 | /* Update cache */ | ||
1326 | ret = client_g_rect(sd, &cam->rect); | ||
1327 | if (ret < 0) | ||
1328 | return ret; | ||
1329 | |||
1330 | update_subrect(cam); | ||
1331 | |||
1332 | return 0; | ||
1333 | } | ||
1334 | |||
1335 | /** | ||
1336 | * @width - on output: user width, mapped back to input | ||
1337 | * @height - on output: user height, mapped back to input | ||
1338 | * @mf - in- / output camera output window | ||
1339 | */ | ||
1340 | static int client_scale(struct soc_camera_device *icd, | ||
1341 | struct v4l2_mbus_framefmt *mf, | ||
1342 | unsigned int *width, unsigned int *height, | ||
1343 | bool ceu_can_scale) | ||
1344 | { | ||
1345 | struct sh_mobile_ceu_cam *cam = icd->host_priv; | ||
1346 | struct device *dev = icd->parent; | ||
1347 | struct v4l2_mbus_framefmt mf_tmp = *mf; | ||
1348 | unsigned int scale_h, scale_v; | ||
1349 | int ret; | ||
1350 | |||
1351 | /* | ||
1352 | * 5. Apply iterative camera S_FMT for camera user window (also updates | ||
1353 | * client crop cache and the imaginary sub-rectangle). | ||
1354 | */ | ||
1355 | ret = client_s_fmt(icd, &mf_tmp, ceu_can_scale); | ||
1356 | if (ret < 0) | ||
1357 | return ret; | ||
1358 | |||
1359 | dev_geo(dev, "5: camera scaled to %ux%u\n", | ||
1360 | mf_tmp.width, mf_tmp.height); | ||
1361 | |||
1362 | /* 6. Retrieve camera output window (g_fmt) */ | ||
1363 | |||
1364 | /* unneeded - it is already in "mf_tmp" */ | ||
1365 | |||
1366 | /* 7. Calculate new client scales. */ | ||
1367 | scale_h = calc_generic_scale(cam->rect.width, mf_tmp.width); | ||
1368 | scale_v = calc_generic_scale(cam->rect.height, mf_tmp.height); | ||
1369 | |||
1370 | mf->width = mf_tmp.width; | ||
1371 | mf->height = mf_tmp.height; | ||
1372 | mf->colorspace = mf_tmp.colorspace; | ||
1373 | |||
1374 | /* | ||
1375 | * 8. Calculate new CEU crop - apply camera scales to previously | ||
1376 | * updated "effective" crop. | ||
1377 | */ | ||
1378 | *width = scale_down(cam->subrect.width, scale_h); | ||
1379 | *height = scale_down(cam->subrect.height, scale_v); | ||
1380 | |||
1381 | dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height); | ||
1382 | |||
1383 | return 0; | ||
1384 | } | ||
1385 | |||
1386 | /* | ||
1387 | * CEU can scale and crop, but we don't want to waste bandwidth and kill the | ||
1388 | * framerate by always requesting the maximum image from the client. See | ||
1389 | * Documentation/video4linux/sh_mobile_ceu_camera.txt for a description of | ||
1390 | * scaling and cropping algorithms and for the meaning of referenced here steps. | ||
1391 | */ | ||
1392 | static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, | ||
1393 | struct v4l2_crop *a) | ||
1394 | { | ||
1395 | struct v4l2_rect *rect = &a->c; | ||
1396 | struct device *dev = icd->parent; | ||
1397 | struct soc_camera_host *ici = to_soc_camera_host(dev); | ||
1398 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
1399 | struct v4l2_crop cam_crop; | ||
1400 | struct sh_mobile_ceu_cam *cam = icd->host_priv; | ||
1401 | struct v4l2_rect *cam_rect = &cam_crop.c; | ||
1402 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); | ||
1403 | struct v4l2_mbus_framefmt mf; | ||
1404 | unsigned int scale_cam_h, scale_cam_v, scale_ceu_h, scale_ceu_v, | ||
1405 | out_width, out_height; | ||
1406 | int interm_width, interm_height; | ||
1407 | u32 capsr, cflcr; | ||
1408 | int ret; | ||
1409 | |||
1410 | dev_geo(dev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height, | ||
1411 | rect->left, rect->top); | ||
1412 | |||
1413 | /* During camera cropping its output window can change too, stop CEU */ | ||
1414 | capsr = capture_save_reset(pcdev); | ||
1415 | dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr); | ||
1416 | |||
1417 | /* 1. - 2. Apply iterative camera S_CROP for new input window. */ | ||
1418 | ret = client_s_crop(icd, a, &cam_crop); | ||
1419 | if (ret < 0) | ||
1420 | return ret; | ||
1421 | |||
1422 | dev_geo(dev, "1-2: camera cropped to %ux%u@%u:%u\n", | ||
1423 | cam_rect->width, cam_rect->height, | ||
1424 | cam_rect->left, cam_rect->top); | ||
1425 | |||
1426 | /* On success cam_crop contains current camera crop */ | ||
1427 | |||
1428 | /* 3. Retrieve camera output window */ | ||
1429 | ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); | ||
1430 | if (ret < 0) | ||
1431 | return ret; | ||
1432 | |||
1433 | if (mf.width > 2560 || mf.height > 1920) | ||
1434 | return -EINVAL; | ||
1435 | |||
1436 | /* 4. Calculate camera scales */ | ||
1437 | scale_cam_h = calc_generic_scale(cam_rect->width, mf.width); | ||
1438 | scale_cam_v = calc_generic_scale(cam_rect->height, mf.height); | ||
1439 | |||
1440 | /* Calculate intermediate window */ | ||
1441 | interm_width = scale_down(rect->width, scale_cam_h); | ||
1442 | interm_height = scale_down(rect->height, scale_cam_v); | ||
1443 | |||
1444 | if (interm_width < icd->user_width) { | ||
1445 | u32 new_scale_h; | ||
1446 | |||
1447 | new_scale_h = calc_generic_scale(rect->width, icd->user_width); | ||
1448 | |||
1449 | mf.width = scale_down(cam_rect->width, new_scale_h); | ||
1450 | } | ||
1451 | |||
1452 | if (interm_height < icd->user_height) { | ||
1453 | u32 new_scale_v; | ||
1454 | |||
1455 | new_scale_v = calc_generic_scale(rect->height, icd->user_height); | ||
1456 | |||
1457 | mf.height = scale_down(cam_rect->height, new_scale_v); | ||
1458 | } | ||
1459 | |||
1460 | if (interm_width < icd->user_width || interm_height < icd->user_height) { | ||
1461 | ret = v4l2_device_call_until_err(sd->v4l2_dev, (int)icd, video, | ||
1462 | s_mbus_fmt, &mf); | ||
1463 | if (ret < 0) | ||
1464 | return ret; | ||
1465 | |||
1466 | dev_geo(dev, "New camera output %ux%u\n", mf.width, mf.height); | ||
1467 | scale_cam_h = calc_generic_scale(cam_rect->width, mf.width); | ||
1468 | scale_cam_v = calc_generic_scale(cam_rect->height, mf.height); | ||
1469 | interm_width = scale_down(rect->width, scale_cam_h); | ||
1470 | interm_height = scale_down(rect->height, scale_cam_v); | ||
1471 | } | ||
1472 | |||
1473 | /* Cache camera output window */ | ||
1474 | cam->width = mf.width; | ||
1475 | cam->height = mf.height; | ||
1476 | |||
1477 | if (pcdev->image_mode) { | ||
1478 | out_width = min(interm_width, icd->user_width); | ||
1479 | out_height = min(interm_height, icd->user_height); | ||
1480 | } else { | ||
1481 | out_width = interm_width; | ||
1482 | out_height = interm_height; | ||
1483 | } | ||
1484 | |||
1485 | /* | ||
1486 | * 5. Calculate CEU scales from camera scales from results of (5) and | ||
1487 | * the user window | ||
1488 | */ | ||
1489 | scale_ceu_h = calc_scale(interm_width, &out_width); | ||
1490 | scale_ceu_v = calc_scale(interm_height, &out_height); | ||
1491 | |||
1492 | dev_geo(dev, "5: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v); | ||
1493 | |||
1494 | /* Apply CEU scales. */ | ||
1495 | cflcr = scale_ceu_h | (scale_ceu_v << 16); | ||
1496 | if (cflcr != pcdev->cflcr) { | ||
1497 | pcdev->cflcr = cflcr; | ||
1498 | ceu_write(pcdev, CFLCR, cflcr); | ||
1499 | } | ||
1500 | |||
1501 | icd->user_width = out_width; | ||
1502 | icd->user_height = out_height; | ||
1503 | cam->ceu_left = scale_down(rect->left - cam_rect->left, scale_cam_h) & ~1; | ||
1504 | cam->ceu_top = scale_down(rect->top - cam_rect->top, scale_cam_v) & ~1; | ||
1505 | |||
1506 | /* 6. Use CEU cropping to crop to the new window. */ | ||
1507 | sh_mobile_ceu_set_rect(icd); | ||
1508 | |||
1509 | cam->subrect = *rect; | ||
1510 | |||
1511 | dev_geo(dev, "6: CEU cropped to %ux%u@%u:%u\n", | ||
1512 | icd->user_width, icd->user_height, | ||
1513 | cam->ceu_left, cam->ceu_top); | ||
1514 | |||
1515 | /* Restore capture. The CE bit can be cleared by the hardware */ | ||
1516 | if (pcdev->active) | ||
1517 | capsr |= 1; | ||
1518 | capture_restore(pcdev, capsr); | ||
1519 | |||
1520 | /* Even if only camera cropping succeeded */ | ||
1521 | return ret; | ||
1522 | } | ||
1523 | |||
1524 | static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd, | ||
1525 | struct v4l2_crop *a) | ||
1526 | { | ||
1527 | struct sh_mobile_ceu_cam *cam = icd->host_priv; | ||
1528 | |||
1529 | a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1530 | a->c = cam->subrect; | ||
1531 | |||
1532 | return 0; | ||
1533 | } | ||
1534 | |||
1535 | /* | ||
1536 | * Calculate real client output window by applying new scales to the current | ||
1537 | * client crop. New scales are calculated from the requested output format and | ||
1538 | * CEU crop, mapped backed onto the client input (subrect). | ||
1539 | */ | ||
1540 | static void calculate_client_output(struct soc_camera_device *icd, | ||
1541 | struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf) | ||
1542 | { | ||
1543 | struct sh_mobile_ceu_cam *cam = icd->host_priv; | ||
1544 | struct device *dev = icd->parent; | ||
1545 | struct v4l2_rect *cam_subrect = &cam->subrect; | ||
1546 | unsigned int scale_v, scale_h; | ||
1547 | |||
1548 | if (cam_subrect->width == cam->rect.width && | ||
1549 | cam_subrect->height == cam->rect.height) { | ||
1550 | /* No sub-cropping */ | ||
1551 | mf->width = pix->width; | ||
1552 | mf->height = pix->height; | ||
1553 | return; | ||
1554 | } | ||
1555 | |||
1556 | /* 1.-2. Current camera scales and subwin - cached. */ | ||
1557 | |||
1558 | dev_geo(dev, "2: subwin %ux%u@%u:%u\n", | ||
1559 | cam_subrect->width, cam_subrect->height, | ||
1560 | cam_subrect->left, cam_subrect->top); | ||
1561 | |||
1562 | /* | ||
1563 | * 3. Calculate new combined scales from input sub-window to requested | ||
1564 | * user window. | ||
1565 | */ | ||
1566 | |||
1567 | /* | ||
1568 | * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF | ||
1569 | * (128x96) or larger than VGA | ||
1570 | */ | ||
1571 | scale_h = calc_generic_scale(cam_subrect->width, pix->width); | ||
1572 | scale_v = calc_generic_scale(cam_subrect->height, pix->height); | ||
1573 | |||
1574 | dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v); | ||
1575 | |||
1576 | /* | ||
1577 | * 4. Calculate client output window by applying combined scales to real | ||
1578 | * input window. | ||
1579 | */ | ||
1580 | mf->width = scale_down(cam->rect.width, scale_h); | ||
1581 | mf->height = scale_down(cam->rect.height, scale_v); | ||
1582 | } | ||
1583 | |||
1584 | /* Similar to set_crop multistage iterative algorithm */ | ||
1585 | static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, | ||
1586 | struct v4l2_format *f) | ||
1587 | { | ||
1588 | struct device *dev = icd->parent; | ||
1589 | struct soc_camera_host *ici = to_soc_camera_host(dev); | ||
1590 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
1591 | struct sh_mobile_ceu_cam *cam = icd->host_priv; | ||
1592 | struct v4l2_pix_format *pix = &f->fmt.pix; | ||
1593 | struct v4l2_mbus_framefmt mf; | ||
1594 | __u32 pixfmt = pix->pixelformat; | ||
1595 | const struct soc_camera_format_xlate *xlate; | ||
1596 | /* Keep Compiler Happy */ | ||
1597 | unsigned int ceu_sub_width = 0, ceu_sub_height = 0; | ||
1598 | u16 scale_v, scale_h; | ||
1599 | int ret; | ||
1600 | bool image_mode; | ||
1601 | enum v4l2_field field; | ||
1602 | |||
1603 | dev_geo(dev, "S_FMT(pix=0x%x, %ux%u)\n", pixfmt, pix->width, pix->height); | ||
1604 | |||
1605 | switch (pix->field) { | ||
1606 | default: | ||
1607 | pix->field = V4L2_FIELD_NONE; | ||
1608 | /* fall-through */ | ||
1609 | case V4L2_FIELD_INTERLACED_TB: | ||
1610 | case V4L2_FIELD_INTERLACED_BT: | ||
1611 | case V4L2_FIELD_NONE: | ||
1612 | field = pix->field; | ||
1613 | break; | ||
1614 | case V4L2_FIELD_INTERLACED: | ||
1615 | field = V4L2_FIELD_INTERLACED_TB; | ||
1616 | break; | ||
1617 | } | ||
1618 | |||
1619 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); | ||
1620 | if (!xlate) { | ||
1621 | dev_warn(dev, "Format %x not found\n", pixfmt); | ||
1622 | return -EINVAL; | ||
1623 | } | ||
1624 | |||
1625 | /* 1.-4. Calculate client output geometry */ | ||
1626 | calculate_client_output(icd, &f->fmt.pix, &mf); | ||
1627 | mf.field = pix->field; | ||
1628 | mf.colorspace = pix->colorspace; | ||
1629 | mf.code = xlate->code; | ||
1630 | |||
1631 | switch (pixfmt) { | ||
1632 | case V4L2_PIX_FMT_NV12: | ||
1633 | case V4L2_PIX_FMT_NV21: | ||
1634 | case V4L2_PIX_FMT_NV16: | ||
1635 | case V4L2_PIX_FMT_NV61: | ||
1636 | image_mode = true; | ||
1637 | break; | ||
1638 | default: | ||
1639 | image_mode = false; | ||
1640 | } | ||
1641 | |||
1642 | dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height); | ||
1643 | |||
1644 | /* 5. - 9. */ | ||
1645 | ret = client_scale(icd, &mf, &ceu_sub_width, &ceu_sub_height, | ||
1646 | image_mode && V4L2_FIELD_NONE == field); | ||
1647 | |||
1648 | dev_geo(dev, "5-9: client scale return %d\n", ret); | ||
1649 | |||
1650 | /* Done with the camera. Now see if we can improve the result */ | ||
1651 | |||
1652 | dev_geo(dev, "fmt %ux%u, requested %ux%u\n", | ||
1653 | mf.width, mf.height, pix->width, pix->height); | ||
1654 | if (ret < 0) | ||
1655 | return ret; | ||
1656 | |||
1657 | if (mf.code != xlate->code) | ||
1658 | return -EINVAL; | ||
1659 | |||
1660 | /* 9. Prepare CEU crop */ | ||
1661 | cam->width = mf.width; | ||
1662 | cam->height = mf.height; | ||
1663 | |||
1664 | /* 10. Use CEU scaling to scale to the requested user window. */ | ||
1665 | |||
1666 | /* We cannot scale up */ | ||
1667 | if (pix->width > ceu_sub_width) | ||
1668 | ceu_sub_width = pix->width; | ||
1669 | |||
1670 | if (pix->height > ceu_sub_height) | ||
1671 | ceu_sub_height = pix->height; | ||
1672 | |||
1673 | pix->colorspace = mf.colorspace; | ||
1674 | |||
1675 | if (image_mode) { | ||
1676 | /* Scale pix->{width x height} down to width x height */ | ||
1677 | scale_h = calc_scale(ceu_sub_width, &pix->width); | ||
1678 | scale_v = calc_scale(ceu_sub_height, &pix->height); | ||
1679 | } else { | ||
1680 | pix->width = ceu_sub_width; | ||
1681 | pix->height = ceu_sub_height; | ||
1682 | scale_h = 0; | ||
1683 | scale_v = 0; | ||
1684 | } | ||
1685 | |||
1686 | pcdev->cflcr = scale_h | (scale_v << 16); | ||
1687 | |||
1688 | /* | ||
1689 | * We have calculated CFLCR, the actual configuration will be performed | ||
1690 | * in sh_mobile_ceu_set_bus_param() | ||
1691 | */ | ||
1692 | |||
1693 | dev_geo(dev, "10: W: %u : 0x%x = %u, H: %u : 0x%x = %u\n", | ||
1694 | ceu_sub_width, scale_h, pix->width, | ||
1695 | ceu_sub_height, scale_v, pix->height); | ||
1696 | |||
1697 | cam->code = xlate->code; | ||
1698 | icd->current_fmt = xlate; | ||
1699 | |||
1700 | pcdev->field = field; | ||
1701 | pcdev->image_mode = image_mode; | ||
1702 | |||
1703 | return 0; | ||
1704 | } | ||
1705 | |||
1706 | static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, | ||
1707 | struct v4l2_format *f) | ||
1708 | { | ||
1709 | const struct soc_camera_format_xlate *xlate; | ||
1710 | struct v4l2_pix_format *pix = &f->fmt.pix; | ||
1711 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); | ||
1712 | struct v4l2_mbus_framefmt mf; | ||
1713 | __u32 pixfmt = pix->pixelformat; | ||
1714 | int width, height; | ||
1715 | int ret; | ||
1716 | |||
1717 | dev_geo(icd->parent, "TRY_FMT(pix=0x%x, %ux%u)\n", | ||
1718 | pixfmt, pix->width, pix->height); | ||
1719 | |||
1720 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); | ||
1721 | if (!xlate) { | ||
1722 | dev_warn(icd->parent, "Format %x not found\n", pixfmt); | ||
1723 | return -EINVAL; | ||
1724 | } | ||
1725 | |||
1726 | /* FIXME: calculate using depth and bus width */ | ||
1727 | |||
1728 | v4l_bound_align_image(&pix->width, 2, 2560, 1, | ||
1729 | &pix->height, 4, 1920, 2, 0); | ||
1730 | |||
1731 | width = pix->width; | ||
1732 | height = pix->height; | ||
1733 | |||
1734 | /* limit to sensor capabilities */ | ||
1735 | mf.width = pix->width; | ||
1736 | mf.height = pix->height; | ||
1737 | mf.field = pix->field; | ||
1738 | mf.code = xlate->code; | ||
1739 | mf.colorspace = pix->colorspace; | ||
1740 | |||
1741 | ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video, try_mbus_fmt, &mf); | ||
1742 | if (ret < 0) | ||
1743 | return ret; | ||
1744 | |||
1745 | pix->width = mf.width; | ||
1746 | pix->height = mf.height; | ||
1747 | pix->field = mf.field; | ||
1748 | pix->colorspace = mf.colorspace; | ||
1749 | |||
1750 | switch (pixfmt) { | ||
1751 | case V4L2_PIX_FMT_NV12: | ||
1752 | case V4L2_PIX_FMT_NV21: | ||
1753 | case V4L2_PIX_FMT_NV16: | ||
1754 | case V4L2_PIX_FMT_NV61: | ||
1755 | /* FIXME: check against rect_max after converting soc-camera */ | ||
1756 | /* We can scale precisely, need a bigger image from camera */ | ||
1757 | if (pix->width < width || pix->height < height) { | ||
1758 | /* | ||
1759 | * We presume, the sensor behaves sanely, i.e., if | ||
1760 | * requested a bigger rectangle, it will not return a | ||
1761 | * smaller one. | ||
1762 | */ | ||
1763 | mf.width = 2560; | ||
1764 | mf.height = 1920; | ||
1765 | ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video, | ||
1766 | try_mbus_fmt, &mf); | ||
1767 | if (ret < 0) { | ||
1768 | /* Shouldn't actually happen... */ | ||
1769 | dev_err(icd->parent, | ||
1770 | "FIXME: client try_fmt() = %d\n", ret); | ||
1771 | return ret; | ||
1772 | } | ||
1773 | } | ||
1774 | /* We will scale exactly */ | ||
1775 | if (mf.width > width) | ||
1776 | pix->width = width; | ||
1777 | if (mf.height > height) | ||
1778 | pix->height = height; | ||
1779 | } | ||
1780 | |||
1781 | dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n", | ||
1782 | __func__, ret, pix->pixelformat, pix->width, pix->height); | ||
1783 | |||
1784 | return ret; | ||
1785 | } | ||
1786 | |||
1787 | static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd, | ||
1788 | struct v4l2_crop *a) | ||
1789 | { | ||
1790 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); | ||
1791 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
1792 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
1793 | u32 out_width = icd->user_width, out_height = icd->user_height; | ||
1794 | int ret; | ||
1795 | |||
1796 | /* Freeze queue */ | ||
1797 | pcdev->frozen = 1; | ||
1798 | /* Wait for frame */ | ||
1799 | ret = wait_for_completion_interruptible(&pcdev->complete); | ||
1800 | /* Stop the client */ | ||
1801 | ret = v4l2_subdev_call(sd, video, s_stream, 0); | ||
1802 | if (ret < 0) | ||
1803 | dev_warn(icd->parent, | ||
1804 | "Client failed to stop the stream: %d\n", ret); | ||
1805 | else | ||
1806 | /* Do the crop, if it fails, there's nothing more we can do */ | ||
1807 | sh_mobile_ceu_set_crop(icd, a); | ||
1808 | |||
1809 | dev_geo(icd->parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height); | ||
1810 | |||
1811 | if (icd->user_width != out_width || icd->user_height != out_height) { | ||
1812 | struct v4l2_format f = { | ||
1813 | .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
1814 | .fmt.pix = { | ||
1815 | .width = out_width, | ||
1816 | .height = out_height, | ||
1817 | .pixelformat = icd->current_fmt->host_fmt->fourcc, | ||
1818 | .field = pcdev->field, | ||
1819 | .colorspace = icd->colorspace, | ||
1820 | }, | ||
1821 | }; | ||
1822 | ret = sh_mobile_ceu_set_fmt(icd, &f); | ||
1823 | if (!ret && (out_width != f.fmt.pix.width || | ||
1824 | out_height != f.fmt.pix.height)) | ||
1825 | ret = -EINVAL; | ||
1826 | if (!ret) { | ||
1827 | icd->user_width = out_width; | ||
1828 | icd->user_height = out_height; | ||
1829 | ret = sh_mobile_ceu_set_bus_param(icd, | ||
1830 | icd->current_fmt->host_fmt->fourcc); | ||
1831 | } | ||
1832 | } | ||
1833 | |||
1834 | /* Thaw the queue */ | ||
1835 | pcdev->frozen = 0; | ||
1836 | spin_lock_irq(&pcdev->lock); | ||
1837 | sh_mobile_ceu_capture(pcdev); | ||
1838 | spin_unlock_irq(&pcdev->lock); | ||
1839 | /* Start the client */ | ||
1840 | ret = v4l2_subdev_call(sd, video, s_stream, 1); | ||
1841 | return ret; | ||
1842 | } | ||
1843 | |||
1844 | static unsigned int sh_mobile_ceu_poll(struct file *file, poll_table *pt) | ||
1845 | { | ||
1846 | struct soc_camera_device *icd = file->private_data; | ||
1847 | |||
1848 | return vb2_poll(&icd->vb2_vidq, file, pt); | ||
1849 | } | ||
1850 | |||
1851 | static int sh_mobile_ceu_querycap(struct soc_camera_host *ici, | ||
1852 | struct v4l2_capability *cap) | ||
1853 | { | ||
1854 | strlcpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card)); | ||
1855 | cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; | ||
1856 | return 0; | ||
1857 | } | ||
1858 | |||
1859 | static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q, | ||
1860 | struct soc_camera_device *icd) | ||
1861 | { | ||
1862 | q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1863 | q->io_modes = VB2_MMAP | VB2_USERPTR; | ||
1864 | q->drv_priv = icd; | ||
1865 | q->ops = &sh_mobile_ceu_videobuf_ops; | ||
1866 | q->mem_ops = &vb2_dma_contig_memops; | ||
1867 | q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer); | ||
1868 | |||
1869 | return vb2_queue_init(q); | ||
1870 | } | ||
1871 | |||
1872 | static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd, | ||
1873 | struct v4l2_control *ctrl) | ||
1874 | { | ||
1875 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
1876 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
1877 | u32 val; | ||
1878 | |||
1879 | switch (ctrl->id) { | ||
1880 | case V4L2_CID_SHARPNESS: | ||
1881 | val = ceu_read(pcdev, CLFCR); | ||
1882 | ctrl->value = val ^ 1; | ||
1883 | return 0; | ||
1884 | } | ||
1885 | return -ENOIOCTLCMD; | ||
1886 | } | ||
1887 | |||
1888 | static int sh_mobile_ceu_set_ctrl(struct soc_camera_device *icd, | ||
1889 | struct v4l2_control *ctrl) | ||
1890 | { | ||
1891 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
1892 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
1893 | |||
1894 | switch (ctrl->id) { | ||
1895 | case V4L2_CID_SHARPNESS: | ||
1896 | switch (icd->current_fmt->host_fmt->fourcc) { | ||
1897 | case V4L2_PIX_FMT_NV12: | ||
1898 | case V4L2_PIX_FMT_NV21: | ||
1899 | case V4L2_PIX_FMT_NV16: | ||
1900 | case V4L2_PIX_FMT_NV61: | ||
1901 | ceu_write(pcdev, CLFCR, !ctrl->value); | ||
1902 | return 0; | ||
1903 | } | ||
1904 | return -EINVAL; | ||
1905 | } | ||
1906 | return -ENOIOCTLCMD; | ||
1907 | } | ||
1908 | |||
1909 | static const struct v4l2_queryctrl sh_mobile_ceu_controls[] = { | ||
1910 | { | ||
1911 | .id = V4L2_CID_SHARPNESS, | ||
1912 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
1913 | .name = "Low-pass filter", | ||
1914 | .minimum = 0, | ||
1915 | .maximum = 1, | ||
1916 | .step = 1, | ||
1917 | .default_value = 0, | ||
1918 | }, | ||
1919 | }; | ||
1920 | |||
1921 | static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { | ||
1922 | .owner = THIS_MODULE, | ||
1923 | .add = sh_mobile_ceu_add_device, | ||
1924 | .remove = sh_mobile_ceu_remove_device, | ||
1925 | .get_formats = sh_mobile_ceu_get_formats, | ||
1926 | .put_formats = sh_mobile_ceu_put_formats, | ||
1927 | .get_crop = sh_mobile_ceu_get_crop, | ||
1928 | .set_crop = sh_mobile_ceu_set_crop, | ||
1929 | .set_livecrop = sh_mobile_ceu_set_livecrop, | ||
1930 | .set_fmt = sh_mobile_ceu_set_fmt, | ||
1931 | .try_fmt = sh_mobile_ceu_try_fmt, | ||
1932 | .set_ctrl = sh_mobile_ceu_set_ctrl, | ||
1933 | .get_ctrl = sh_mobile_ceu_get_ctrl, | ||
1934 | .poll = sh_mobile_ceu_poll, | ||
1935 | .querycap = sh_mobile_ceu_querycap, | ||
1936 | .set_bus_param = sh_mobile_ceu_set_bus_param, | ||
1937 | .init_videobuf2 = sh_mobile_ceu_init_videobuf, | ||
1938 | .controls = sh_mobile_ceu_controls, | ||
1939 | .num_controls = ARRAY_SIZE(sh_mobile_ceu_controls), | ||
1940 | }; | ||
1941 | |||
1942 | struct bus_wait { | ||
1943 | struct notifier_block notifier; | ||
1944 | struct completion completion; | ||
1945 | struct device *dev; | ||
1946 | }; | ||
1947 | |||
1948 | static int bus_notify(struct notifier_block *nb, | ||
1949 | unsigned long action, void *data) | ||
1950 | { | ||
1951 | struct device *dev = data; | ||
1952 | struct bus_wait *wait = container_of(nb, struct bus_wait, notifier); | ||
1953 | |||
1954 | if (wait->dev != dev) | ||
1955 | return NOTIFY_DONE; | ||
1956 | |||
1957 | switch (action) { | ||
1958 | case BUS_NOTIFY_UNBOUND_DRIVER: | ||
1959 | /* Protect from module unloading */ | ||
1960 | wait_for_completion(&wait->completion); | ||
1961 | return NOTIFY_OK; | ||
1962 | } | ||
1963 | return NOTIFY_DONE; | ||
1964 | } | ||
1965 | |||
1966 | static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) | ||
1967 | { | ||
1968 | struct sh_mobile_ceu_dev *pcdev; | ||
1969 | struct resource *res; | ||
1970 | void __iomem *base; | ||
1971 | unsigned int irq; | ||
1972 | int err = 0; | ||
1973 | struct bus_wait wait = { | ||
1974 | .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion), | ||
1975 | .notifier.notifier_call = bus_notify, | ||
1976 | }; | ||
1977 | struct sh_mobile_ceu_companion *csi2; | ||
1978 | |||
1979 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1980 | irq = platform_get_irq(pdev, 0); | ||
1981 | if (!res || (int)irq <= 0) { | ||
1982 | dev_err(&pdev->dev, "Not enough CEU platform resources.\n"); | ||
1983 | err = -ENODEV; | ||
1984 | goto exit; | ||
1985 | } | ||
1986 | |||
1987 | pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); | ||
1988 | if (!pcdev) { | ||
1989 | dev_err(&pdev->dev, "Could not allocate pcdev\n"); | ||
1990 | err = -ENOMEM; | ||
1991 | goto exit; | ||
1992 | } | ||
1993 | |||
1994 | INIT_LIST_HEAD(&pcdev->capture); | ||
1995 | spin_lock_init(&pcdev->lock); | ||
1996 | init_completion(&pcdev->complete); | ||
1997 | |||
1998 | pcdev->pdata = pdev->dev.platform_data; | ||
1999 | if (!pcdev->pdata) { | ||
2000 | err = -EINVAL; | ||
2001 | dev_err(&pdev->dev, "CEU platform data not set.\n"); | ||
2002 | goto exit_kfree; | ||
2003 | } | ||
2004 | |||
2005 | base = ioremap_nocache(res->start, resource_size(res)); | ||
2006 | if (!base) { | ||
2007 | err = -ENXIO; | ||
2008 | dev_err(&pdev->dev, "Unable to ioremap CEU registers.\n"); | ||
2009 | goto exit_kfree; | ||
2010 | } | ||
2011 | |||
2012 | pcdev->irq = irq; | ||
2013 | pcdev->base = base; | ||
2014 | pcdev->video_limit = 0; /* only enabled if second resource exists */ | ||
2015 | |||
2016 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
2017 | if (res) { | ||
2018 | err = dma_declare_coherent_memory(&pdev->dev, res->start, | ||
2019 | res->start, | ||
2020 | resource_size(res), | ||
2021 | DMA_MEMORY_MAP | | ||
2022 | DMA_MEMORY_EXCLUSIVE); | ||
2023 | if (!err) { | ||
2024 | dev_err(&pdev->dev, "Unable to declare CEU memory.\n"); | ||
2025 | err = -ENXIO; | ||
2026 | goto exit_iounmap; | ||
2027 | } | ||
2028 | |||
2029 | pcdev->video_limit = resource_size(res); | ||
2030 | } | ||
2031 | |||
2032 | /* request irq */ | ||
2033 | err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED, | ||
2034 | dev_name(&pdev->dev), pcdev); | ||
2035 | if (err) { | ||
2036 | dev_err(&pdev->dev, "Unable to register CEU interrupt.\n"); | ||
2037 | goto exit_release_mem; | ||
2038 | } | ||
2039 | |||
2040 | pm_suspend_ignore_children(&pdev->dev, true); | ||
2041 | pm_runtime_enable(&pdev->dev); | ||
2042 | pm_runtime_resume(&pdev->dev); | ||
2043 | |||
2044 | pcdev->ici.priv = pcdev; | ||
2045 | pcdev->ici.v4l2_dev.dev = &pdev->dev; | ||
2046 | pcdev->ici.nr = pdev->id; | ||
2047 | pcdev->ici.drv_name = dev_name(&pdev->dev); | ||
2048 | pcdev->ici.ops = &sh_mobile_ceu_host_ops; | ||
2049 | |||
2050 | pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); | ||
2051 | if (IS_ERR(pcdev->alloc_ctx)) { | ||
2052 | err = PTR_ERR(pcdev->alloc_ctx); | ||
2053 | goto exit_free_clk; | ||
2054 | } | ||
2055 | |||
2056 | err = soc_camera_host_register(&pcdev->ici); | ||
2057 | if (err) | ||
2058 | goto exit_free_ctx; | ||
2059 | |||
2060 | /* CSI2 interfacing */ | ||
2061 | csi2 = pcdev->pdata->csi2; | ||
2062 | if (csi2) { | ||
2063 | struct platform_device *csi2_pdev = | ||
2064 | platform_device_alloc("sh-mobile-csi2", csi2->id); | ||
2065 | struct sh_csi2_pdata *csi2_pdata = csi2->platform_data; | ||
2066 | |||
2067 | if (!csi2_pdev) { | ||
2068 | err = -ENOMEM; | ||
2069 | goto exit_host_unregister; | ||
2070 | } | ||
2071 | |||
2072 | pcdev->csi2_pdev = csi2_pdev; | ||
2073 | |||
2074 | err = platform_device_add_data(csi2_pdev, csi2_pdata, sizeof(*csi2_pdata)); | ||
2075 | if (err < 0) | ||
2076 | goto exit_pdev_put; | ||
2077 | |||
2078 | csi2_pdata = csi2_pdev->dev.platform_data; | ||
2079 | csi2_pdata->v4l2_dev = &pcdev->ici.v4l2_dev; | ||
2080 | |||
2081 | csi2_pdev->resource = csi2->resource; | ||
2082 | csi2_pdev->num_resources = csi2->num_resources; | ||
2083 | |||
2084 | err = platform_device_add(csi2_pdev); | ||
2085 | if (err < 0) | ||
2086 | goto exit_pdev_put; | ||
2087 | |||
2088 | wait.dev = &csi2_pdev->dev; | ||
2089 | |||
2090 | err = bus_register_notifier(&platform_bus_type, &wait.notifier); | ||
2091 | if (err < 0) | ||
2092 | goto exit_pdev_unregister; | ||
2093 | |||
2094 | /* | ||
2095 | * From this point the driver module will not unload, until | ||
2096 | * we complete the completion. | ||
2097 | */ | ||
2098 | |||
2099 | if (!csi2_pdev->dev.driver) { | ||
2100 | complete(&wait.completion); | ||
2101 | /* Either too late, or probing failed */ | ||
2102 | bus_unregister_notifier(&platform_bus_type, &wait.notifier); | ||
2103 | err = -ENXIO; | ||
2104 | goto exit_pdev_unregister; | ||
2105 | } | ||
2106 | |||
2107 | /* | ||
2108 | * The module is still loaded, in the worst case it is hanging | ||
2109 | * in device release on our completion. So, _now_ dereferencing | ||
2110 | * the "owner" is safe! | ||
2111 | */ | ||
2112 | |||
2113 | err = try_module_get(csi2_pdev->dev.driver->owner); | ||
2114 | |||
2115 | /* Let notifier complete, if it has been locked */ | ||
2116 | complete(&wait.completion); | ||
2117 | bus_unregister_notifier(&platform_bus_type, &wait.notifier); | ||
2118 | if (!err) { | ||
2119 | err = -ENODEV; | ||
2120 | goto exit_pdev_unregister; | ||
2121 | } | ||
2122 | } | ||
2123 | |||
2124 | return 0; | ||
2125 | |||
2126 | exit_pdev_unregister: | ||
2127 | platform_device_del(pcdev->csi2_pdev); | ||
2128 | exit_pdev_put: | ||
2129 | pcdev->csi2_pdev->resource = NULL; | ||
2130 | platform_device_put(pcdev->csi2_pdev); | ||
2131 | exit_host_unregister: | ||
2132 | soc_camera_host_unregister(&pcdev->ici); | ||
2133 | exit_free_ctx: | ||
2134 | vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); | ||
2135 | exit_free_clk: | ||
2136 | pm_runtime_disable(&pdev->dev); | ||
2137 | free_irq(pcdev->irq, pcdev); | ||
2138 | exit_release_mem: | ||
2139 | if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) | ||
2140 | dma_release_declared_memory(&pdev->dev); | ||
2141 | exit_iounmap: | ||
2142 | iounmap(base); | ||
2143 | exit_kfree: | ||
2144 | kfree(pcdev); | ||
2145 | exit: | ||
2146 | return err; | ||
2147 | } | ||
2148 | |||
2149 | static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) | ||
2150 | { | ||
2151 | struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); | ||
2152 | struct sh_mobile_ceu_dev *pcdev = container_of(soc_host, | ||
2153 | struct sh_mobile_ceu_dev, ici); | ||
2154 | struct platform_device *csi2_pdev = pcdev->csi2_pdev; | ||
2155 | |||
2156 | soc_camera_host_unregister(soc_host); | ||
2157 | pm_runtime_disable(&pdev->dev); | ||
2158 | free_irq(pcdev->irq, pcdev); | ||
2159 | if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) | ||
2160 | dma_release_declared_memory(&pdev->dev); | ||
2161 | iounmap(pcdev->base); | ||
2162 | vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); | ||
2163 | if (csi2_pdev && csi2_pdev->dev.driver) { | ||
2164 | struct module *csi2_drv = csi2_pdev->dev.driver->owner; | ||
2165 | platform_device_del(csi2_pdev); | ||
2166 | csi2_pdev->resource = NULL; | ||
2167 | platform_device_put(csi2_pdev); | ||
2168 | module_put(csi2_drv); | ||
2169 | } | ||
2170 | kfree(pcdev); | ||
2171 | |||
2172 | return 0; | ||
2173 | } | ||
2174 | |||
2175 | static int sh_mobile_ceu_runtime_nop(struct device *dev) | ||
2176 | { | ||
2177 | /* Runtime PM callback shared between ->runtime_suspend() | ||
2178 | * and ->runtime_resume(). Simply returns success. | ||
2179 | * | ||
2180 | * This driver re-initializes all registers after | ||
2181 | * pm_runtime_get_sync() anyway so there is no need | ||
2182 | * to save and restore registers here. | ||
2183 | */ | ||
2184 | return 0; | ||
2185 | } | ||
2186 | |||
2187 | static const struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = { | ||
2188 | .runtime_suspend = sh_mobile_ceu_runtime_nop, | ||
2189 | .runtime_resume = sh_mobile_ceu_runtime_nop, | ||
2190 | }; | ||
2191 | |||
2192 | static struct platform_driver sh_mobile_ceu_driver = { | ||
2193 | .driver = { | ||
2194 | .name = "sh_mobile_ceu", | ||
2195 | .pm = &sh_mobile_ceu_dev_pm_ops, | ||
2196 | }, | ||
2197 | .probe = sh_mobile_ceu_probe, | ||
2198 | .remove = __devexit_p(sh_mobile_ceu_remove), | ||
2199 | }; | ||
2200 | |||
2201 | static int __init sh_mobile_ceu_init(void) | ||
2202 | { | ||
2203 | /* Whatever return code */ | ||
2204 | request_module("sh_mobile_csi2"); | ||
2205 | return platform_driver_register(&sh_mobile_ceu_driver); | ||
2206 | } | ||
2207 | |||
2208 | static void __exit sh_mobile_ceu_exit(void) | ||
2209 | { | ||
2210 | platform_driver_unregister(&sh_mobile_ceu_driver); | ||
2211 | } | ||
2212 | |||
2213 | module_init(sh_mobile_ceu_init); | ||
2214 | module_exit(sh_mobile_ceu_exit); | ||
2215 | |||
2216 | MODULE_DESCRIPTION("SuperH Mobile CEU driver"); | ||
2217 | MODULE_AUTHOR("Magnus Damm"); | ||
2218 | MODULE_LICENSE("GPL"); | ||
2219 | MODULE_VERSION("0.0.6"); | ||
2220 | MODULE_ALIAS("platform:sh_mobile_ceu"); | ||