aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c41
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
242static 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 */
248static 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
286static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, 307static 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);