diff options
-rw-r--r-- | drivers/media/video/sh_mobile_ceu_camera.c | 41 |
1 files changed, 33 insertions, 8 deletions
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index edb6ec3d2bcd..a4f3472d4db8 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c | |||
@@ -236,26 +236,45 @@ static void free_buffer(struct videobuf_queue *vq, | |||
236 | #define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */ | 236 | #define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */ |
237 | #define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */ | 237 | #define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */ |
238 | #define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */ | 238 | #define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */ |
239 | #define CEU_CEIER_VBP (1 << 20) /* vbp error */ | ||
239 | #define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */ | 240 | #define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */ |
241 | #define CEU_CEIER_MASK (CEU_CEIER_CPEIE | CEU_CEIER_VBP) | ||
240 | 242 | ||
241 | 243 | ||
242 | static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) | 244 | /* |
245 | * return value doesn't reflex the success/failure to queue the new buffer, | ||
246 | * but rather the status of the previous buffer. | ||
247 | */ | ||
248 | static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) | ||
243 | { | 249 | { |
244 | struct soc_camera_device *icd = pcdev->icd; | 250 | struct soc_camera_device *icd = pcdev->icd; |
245 | dma_addr_t phys_addr_top, phys_addr_bottom; | 251 | dma_addr_t phys_addr_top, phys_addr_bottom; |
252 | u32 status; | ||
253 | int ret = 0; | ||
246 | 254 | ||
247 | /* The hardware is _very_ picky about this sequence. Especially | 255 | /* The hardware is _very_ picky about this sequence. Especially |
248 | * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge | 256 | * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge |
249 | * several not-so-well documented interrupt sources in CETCR. | 257 | * several not-so-well documented interrupt sources in CETCR. |
250 | */ | 258 | */ |
251 | ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_CPEIE); | 259 | ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK); |
252 | ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & CEU_CETCR_MAGIC); | 260 | status = ceu_read(pcdev, CETCR); |
253 | ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_CPEIE); | 261 | ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC); |
262 | ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK); | ||
254 | ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP); | 263 | ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP); |
255 | ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW); | 264 | ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW); |
256 | 265 | ||
266 | /* | ||
267 | * When a VBP interrupt occurs, a capture end interrupt does not occur | ||
268 | * and the image of that frame is not captured correctly. So, soft reset | ||
269 | * is needed here. | ||
270 | */ | ||
271 | if (status & CEU_CEIER_VBP) { | ||
272 | sh_mobile_ceu_soft_reset(pcdev); | ||
273 | ret = -EIO; | ||
274 | } | ||
275 | |||
257 | if (!pcdev->active) | 276 | if (!pcdev->active) |
258 | return; | 277 | return ret; |
259 | 278 | ||
260 | phys_addr_top = videobuf_to_dma_contig(pcdev->active); | 279 | phys_addr_top = videobuf_to_dma_contig(pcdev->active); |
261 | ceu_write(pcdev, CDAYR, phys_addr_top); | 280 | ceu_write(pcdev, CDAYR, phys_addr_top); |
@@ -281,6 +300,8 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) | |||
281 | 300 | ||
282 | pcdev->active->state = VIDEOBUF_ACTIVE; | 301 | pcdev->active->state = VIDEOBUF_ACTIVE; |
283 | ceu_write(pcdev, CAPSR, 0x1); /* start capture */ | 302 | ceu_write(pcdev, CAPSR, 0x1); /* start capture */ |
303 | |||
304 | return ret; | ||
284 | } | 305 | } |
285 | 306 | ||
286 | static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, | 307 | static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, |
@@ -353,6 +374,11 @@ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq, | |||
353 | list_add_tail(&vb->queue, &pcdev->capture); | 374 | list_add_tail(&vb->queue, &pcdev->capture); |
354 | 375 | ||
355 | if (!pcdev->active) { | 376 | if (!pcdev->active) { |
377 | /* | ||
378 | * Because there were no active buffer at this moment, | ||
379 | * we are not interested in the return value of | ||
380 | * sh_mobile_ceu_capture here. | ||
381 | */ | ||
356 | pcdev->active = vb; | 382 | pcdev->active = vb; |
357 | sh_mobile_ceu_capture(pcdev); | 383 | sh_mobile_ceu_capture(pcdev); |
358 | } | 384 | } |
@@ -413,9 +439,8 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) | |||
413 | else | 439 | else |
414 | pcdev->active = NULL; | 440 | pcdev->active = NULL; |
415 | 441 | ||
416 | sh_mobile_ceu_capture(pcdev); | 442 | vb->state = (sh_mobile_ceu_capture(pcdev) < 0) ? |
417 | 443 | VIDEOBUF_ERROR : VIDEOBUF_DONE; | |
418 | vb->state = VIDEOBUF_DONE; | ||
419 | do_gettimeofday(&vb->ts); | 444 | do_gettimeofday(&vb->ts); |
420 | vb->field_count++; | 445 | vb->field_count++; |
421 | wake_up(&vb->done); | 446 | wake_up(&vb->done); |