diff options
-rw-r--r-- | drivers/media/video/sh_mobile_ceu_camera.c | 56 |
1 files changed, 46 insertions, 10 deletions
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 79eda0fed9f9..6750c49f4c04 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c | |||
@@ -94,6 +94,7 @@ struct sh_mobile_ceu_dev { | |||
94 | spinlock_t lock; | 94 | spinlock_t lock; |
95 | struct list_head capture; | 95 | struct list_head capture; |
96 | struct videobuf_buffer *active; | 96 | struct videobuf_buffer *active; |
97 | int is_interlace; | ||
97 | 98 | ||
98 | struct sh_mobile_ceu_info *pdata; | 99 | struct sh_mobile_ceu_info *pdata; |
99 | 100 | ||
@@ -163,7 +164,7 @@ static void free_buffer(struct videobuf_queue *vq, | |||
163 | static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) | 164 | static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) |
164 | { | 165 | { |
165 | struct soc_camera_device *icd = pcdev->icd; | 166 | struct soc_camera_device *icd = pcdev->icd; |
166 | dma_addr_t phys_addr; | 167 | dma_addr_t phys_addr_top, phys_addr_bottom; |
167 | 168 | ||
168 | /* The hardware is _very_ picky about this sequence. Especially | 169 | /* The hardware is _very_ picky about this sequence. Especially |
169 | * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge | 170 | * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge |
@@ -178,16 +179,24 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) | |||
178 | if (!pcdev->active) | 179 | if (!pcdev->active) |
179 | return; | 180 | return; |
180 | 181 | ||
181 | phys_addr = videobuf_to_dma_contig(pcdev->active); | 182 | phys_addr_top = videobuf_to_dma_contig(pcdev->active); |
182 | ceu_write(pcdev, CDAYR, phys_addr); | 183 | ceu_write(pcdev, CDAYR, phys_addr_top); |
184 | if (pcdev->is_interlace) { | ||
185 | phys_addr_bottom = phys_addr_top + icd->width; | ||
186 | ceu_write(pcdev, CDBYR, phys_addr_bottom); | ||
187 | } | ||
183 | 188 | ||
184 | switch (icd->current_fmt->fourcc) { | 189 | switch (icd->current_fmt->fourcc) { |
185 | case V4L2_PIX_FMT_NV12: | 190 | case V4L2_PIX_FMT_NV12: |
186 | case V4L2_PIX_FMT_NV21: | 191 | case V4L2_PIX_FMT_NV21: |
187 | case V4L2_PIX_FMT_NV16: | 192 | case V4L2_PIX_FMT_NV16: |
188 | case V4L2_PIX_FMT_NV61: | 193 | case V4L2_PIX_FMT_NV61: |
189 | phys_addr += icd->width * icd->height; | 194 | phys_addr_top += icd->width * icd->height; |
190 | ceu_write(pcdev, CDACR, phys_addr); | 195 | ceu_write(pcdev, CDACR, phys_addr_top); |
196 | if (pcdev->is_interlace) { | ||
197 | phys_addr_bottom = phys_addr_top + icd->width; | ||
198 | ceu_write(pcdev, CDBCR, phys_addr_bottom); | ||
199 | } | ||
191 | } | 200 | } |
192 | 201 | ||
193 | pcdev->active->state = VIDEOBUF_ACTIVE; | 202 | pcdev->active->state = VIDEOBUF_ACTIVE; |
@@ -381,7 +390,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, | |||
381 | { | 390 | { |
382 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | 391 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); |
383 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | 392 | struct sh_mobile_ceu_dev *pcdev = ici->priv; |
384 | int ret, buswidth, width, cfszr_width, cdwdr_width; | 393 | int ret, buswidth, width, height, cfszr_width, cdwdr_width; |
385 | unsigned long camera_flags, common_flags, value; | 394 | unsigned long camera_flags, common_flags, value; |
386 | int yuv_mode, yuv_lineskip; | 395 | int yuv_mode, yuv_lineskip; |
387 | 396 | ||
@@ -448,7 +457,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, | |||
448 | ceu_write(pcdev, CAMCR, value); | 457 | ceu_write(pcdev, CAMCR, value); |
449 | 458 | ||
450 | ceu_write(pcdev, CAPCR, 0x00300000); | 459 | ceu_write(pcdev, CAPCR, 0x00300000); |
451 | ceu_write(pcdev, CAIFR, 0); | 460 | ceu_write(pcdev, CAIFR, (pcdev->is_interlace) ? 0x101 : 0); |
452 | 461 | ||
453 | mdelay(1); | 462 | mdelay(1); |
454 | 463 | ||
@@ -463,10 +472,16 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, | |||
463 | cdwdr_width = buswidth == 16 ? width * 2 : width; | 472 | cdwdr_width = buswidth == 16 ? width * 2 : width; |
464 | } | 473 | } |
465 | 474 | ||
475 | height = icd->height; | ||
476 | if (pcdev->is_interlace) { | ||
477 | height /= 2; | ||
478 | cdwdr_width *= 2; | ||
479 | } | ||
480 | |||
466 | ceu_write(pcdev, CAMOR, 0); | 481 | ceu_write(pcdev, CAMOR, 0); |
467 | ceu_write(pcdev, CAPWR, (icd->height << 16) | width); | 482 | ceu_write(pcdev, CAPWR, (height << 16) | width); |
468 | ceu_write(pcdev, CFLCR, 0); /* no scaling */ | 483 | ceu_write(pcdev, CFLCR, 0); /* no scaling */ |
469 | ceu_write(pcdev, CFSZR, (icd->height << 16) | cfszr_width); | 484 | ceu_write(pcdev, CFSZR, (height << 16) | cfszr_width); |
470 | ceu_write(pcdev, CLFCR, 0); /* no lowpass filter */ | 485 | ceu_write(pcdev, CLFCR, 0); /* no lowpass filter */ |
471 | 486 | ||
472 | /* A few words about byte order (observed in Big Endian mode) | 487 | /* A few words about byte order (observed in Big Endian mode) |
@@ -615,8 +630,10 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, | |||
615 | struct v4l2_format *f) | 630 | struct v4l2_format *f) |
616 | { | 631 | { |
617 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | 632 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); |
633 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
618 | const struct soc_camera_format_xlate *xlate; | 634 | const struct soc_camera_format_xlate *xlate; |
619 | __u32 pixfmt = f->fmt.pix.pixelformat; | 635 | __u32 pixfmt = f->fmt.pix.pixelformat; |
636 | int ret; | ||
620 | 637 | ||
621 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); | 638 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); |
622 | if (!xlate) { | 639 | if (!xlate) { |
@@ -642,7 +659,26 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, | |||
642 | f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; | 659 | f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; |
643 | 660 | ||
644 | /* limit to sensor capabilities */ | 661 | /* limit to sensor capabilities */ |
645 | return icd->ops->try_fmt(icd, f); | 662 | ret = icd->ops->try_fmt(icd, f); |
663 | if (ret < 0) | ||
664 | return ret; | ||
665 | |||
666 | switch (f->fmt.pix.field) { | ||
667 | case V4L2_FIELD_INTERLACED: | ||
668 | pcdev->is_interlace = 1; | ||
669 | break; | ||
670 | case V4L2_FIELD_ANY: | ||
671 | f->fmt.pix.field = V4L2_FIELD_NONE; | ||
672 | /* fall-through */ | ||
673 | case V4L2_FIELD_NONE: | ||
674 | pcdev->is_interlace = 0; | ||
675 | break; | ||
676 | default: | ||
677 | ret = -EINVAL; | ||
678 | break; | ||
679 | } | ||
680 | |||
681 | return ret; | ||
646 | } | 682 | } |
647 | 683 | ||
648 | static int sh_mobile_ceu_reqbufs(struct soc_camera_file *icf, | 684 | static int sh_mobile_ceu_reqbufs(struct soc_camera_file *icf, |