aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c114
1 files changed, 99 insertions, 15 deletions
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index 47ffa441c869..31a0a9769a3f 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -96,6 +96,8 @@ struct sh_mobile_ceu_dev {
96 struct videobuf_buffer *active; 96 struct videobuf_buffer *active;
97 97
98 struct sh_mobile_ceu_info *pdata; 98 struct sh_mobile_ceu_info *pdata;
99
100 const struct soc_camera_data_format *camera_fmt;
99}; 101};
100 102
101static void ceu_write(struct sh_mobile_ceu_dev *priv, 103static void ceu_write(struct sh_mobile_ceu_dev *priv,
@@ -155,6 +157,9 @@ static void free_buffer(struct videobuf_queue *vq,
155 157
156static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) 158static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
157{ 159{
160 struct soc_camera_device *icd = pcdev->icd;
161 unsigned long phys_addr;
162
158 ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~1); 163 ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~1);
159 ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & 0x0317f313); 164 ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & 0x0317f313);
160 ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | 1); 165 ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | 1);
@@ -163,11 +168,21 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
163 168
164 ceu_write(pcdev, CETCR, 0x0317f313 ^ 0x10); 169 ceu_write(pcdev, CETCR, 0x0317f313 ^ 0x10);
165 170
166 if (pcdev->active) { 171 if (!pcdev->active)
167 pcdev->active->state = VIDEOBUF_ACTIVE; 172 return;
168 ceu_write(pcdev, CDAYR, videobuf_to_dma_contig(pcdev->active)); 173
169 ceu_write(pcdev, CAPSR, 0x1); /* start capture */ 174 phys_addr = videobuf_to_dma_contig(pcdev->active);
175 ceu_write(pcdev, CDAYR, phys_addr);
176
177 switch (icd->current_fmt->fourcc) {
178 case V4L2_PIX_FMT_NV12:
179 case V4L2_PIX_FMT_NV21:
180 phys_addr += (icd->width * icd->height);
181 ceu_write(pcdev, CDACR, phys_addr);
170 } 182 }
183
184 pcdev->active->state = VIDEOBUF_ACTIVE;
185 ceu_write(pcdev, CAPSR, 0x1); /* start capture */
171} 186}
172 187
173static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, 188static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq,
@@ -359,6 +374,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
359 struct sh_mobile_ceu_dev *pcdev = ici->priv; 374 struct sh_mobile_ceu_dev *pcdev = ici->priv;
360 int ret, buswidth, width, cfszr_width, cdwdr_width; 375 int ret, buswidth, width, cfszr_width, cdwdr_width;
361 unsigned long camera_flags, common_flags, value; 376 unsigned long camera_flags, common_flags, value;
377 int yuv_mode, yuv_lineskip;
362 378
363 camera_flags = icd->ops->query_bus_param(icd); 379 camera_flags = icd->ops->query_bus_param(icd);
364 common_flags = soc_camera_bus_param_compatible(camera_flags, 380 common_flags = soc_camera_bus_param_compatible(camera_flags,
@@ -384,7 +400,35 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
384 ceu_write(pcdev, CRCNTR, 0); 400 ceu_write(pcdev, CRCNTR, 0);
385 ceu_write(pcdev, CRCMPR, 0); 401 ceu_write(pcdev, CRCMPR, 0);
386 402
387 value = 0x00000010; 403 value = 0x00000010; /* data fetch by default */
404 yuv_mode = yuv_lineskip = 0;
405
406 switch (icd->current_fmt->fourcc) {
407 case V4L2_PIX_FMT_NV12:
408 case V4L2_PIX_FMT_NV21:
409 yuv_lineskip = 1; /* skip for NV12/21, no skip for NV16/61 */
410 yuv_mode = 1;
411 switch (pcdev->camera_fmt->fourcc) {
412 case V4L2_PIX_FMT_UYVY:
413 value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */
414 break;
415 case V4L2_PIX_FMT_VYUY:
416 value = 0x00000100; /* Cr0, Y0, Cb0, Y1 */
417 break;
418 case V4L2_PIX_FMT_YUYV:
419 value = 0x00000200; /* Y0, Cb0, Y1, Cr0 */
420 break;
421 case V4L2_PIX_FMT_YVYU:
422 value = 0x00000300; /* Y0, Cr0, Y1, Cb0 */
423 break;
424 default:
425 BUG();
426 }
427 }
428
429 if (icd->current_fmt->fourcc == V4L2_PIX_FMT_NV21)
430 value ^= 0x00000100; /* swap U, V to change from NV12->NV21 */
431
388 value |= (common_flags & SOCAM_VSYNC_ACTIVE_LOW) ? (1 << 1) : 0; 432 value |= (common_flags & SOCAM_VSYNC_ACTIVE_LOW) ? (1 << 1) : 0;
389 value |= (common_flags & SOCAM_HSYNC_ACTIVE_LOW) ? (1 << 0) : 0; 433 value |= (common_flags & SOCAM_HSYNC_ACTIVE_LOW) ? (1 << 0) : 0;
390 value |= (buswidth == 16) ? (1 << 12) : 0; 434 value |= (buswidth == 16) ? (1 << 12) : 0;
@@ -395,16 +439,22 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
395 439
396 mdelay(1); 440 mdelay(1);
397 441
398 width = icd->width * (icd->current_fmt->depth / 8); 442 if (yuv_mode) {
399 width = (buswidth == 16) ? width / 2 : width; 443 width = icd->width * 2;
400 cfszr_width = (buswidth == 8) ? width / 2 : width; 444 width = (buswidth == 16) ? width / 2 : width;
401 cdwdr_width = (buswidth == 16) ? width * 2 : width; 445 cfszr_width = cdwdr_width = icd->width;
446 } else {
447 width = icd->width * ((icd->current_fmt->depth + 7) >> 3);
448 width = (buswidth == 16) ? width / 2 : width;
449 cfszr_width = (buswidth == 8) ? width / 2 : width;
450 cdwdr_width = (buswidth == 16) ? width * 2 : width;
451 }
402 452
403 ceu_write(pcdev, CAMOR, 0); 453 ceu_write(pcdev, CAMOR, 0);
404 ceu_write(pcdev, CAPWR, (icd->height << 16) | width); 454 ceu_write(pcdev, CAPWR, (icd->height << 16) | width);
405 ceu_write(pcdev, CFLCR, 0); /* data fetch mode - no scaling */ 455 ceu_write(pcdev, CFLCR, 0); /* no scaling */
406 ceu_write(pcdev, CFSZR, (icd->height << 16) | cfszr_width); 456 ceu_write(pcdev, CFSZR, (icd->height << 16) | cfszr_width);
407 ceu_write(pcdev, CLFCR, 0); /* data fetch mode - no lowpass filter */ 457 ceu_write(pcdev, CLFCR, 0); /* no lowpass filter */
408 458
409 /* A few words about byte order (observed in Big Endian mode) 459 /* A few words about byte order (observed in Big Endian mode)
410 * 460 *
@@ -418,14 +468,16 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
418 * using 7 we swap the data bytes to match the incoming order: 468 * using 7 we swap the data bytes to match the incoming order:
419 * D0, D1, D2, D3, D4, D5, D6, D7 469 * D0, D1, D2, D3, D4, D5, D6, D7
420 */ 470 */
421 ceu_write(pcdev, CDOCR, 0x00000017); 471 value = 0x00000017;
472 if (yuv_lineskip)
473 value &= ~0x00000010; /* convert 4:2:2 -> 4:2:0 */
474
475 ceu_write(pcdev, CDOCR, value);
422 476
423 ceu_write(pcdev, CDWDR, cdwdr_width); 477 ceu_write(pcdev, CDWDR, cdwdr_width);
424 ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */ 478 ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
425 479
426 /* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */ 480 /* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */
427 /* in data fetch mode: no need for CDACR, CDBYR, CDBCR */
428
429 return 0; 481 return 0;
430} 482}
431 483
@@ -444,11 +496,26 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd)
444 return 0; 496 return 0;
445} 497}
446 498
499static const struct soc_camera_data_format sh_mobile_ceu_formats[] = {
500 {
501 .name = "NV12",
502 .depth = 12,
503 .fourcc = V4L2_PIX_FMT_NV12,
504 .colorspace = V4L2_COLORSPACE_JPEG,
505 },
506 {
507 .name = "NV21",
508 .depth = 12,
509 .fourcc = V4L2_PIX_FMT_NV21,
510 .colorspace = V4L2_COLORSPACE_JPEG,
511 },
512};
513
447static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, 514static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx,
448 struct soc_camera_format_xlate *xlate) 515 struct soc_camera_format_xlate *xlate)
449{ 516{
450 struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); 517 struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
451 int ret; 518 int ret, k, n;
452 int formats = 0; 519 int formats = 0;
453 520
454 ret = sh_mobile_ceu_try_bus_param(icd); 521 ret = sh_mobile_ceu_try_bus_param(icd);
@@ -456,6 +523,21 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx,
456 return 0; 523 return 0;
457 524
458 switch (icd->formats[idx].fourcc) { 525 switch (icd->formats[idx].fourcc) {
526 case V4L2_PIX_FMT_UYVY:
527 case V4L2_PIX_FMT_VYUY:
528 case V4L2_PIX_FMT_YUYV:
529 case V4L2_PIX_FMT_YVYU:
530 n = ARRAY_SIZE(sh_mobile_ceu_formats);
531 formats += n;
532 for (k = 0; xlate && k < n; k++) {
533 xlate->host_fmt = &sh_mobile_ceu_formats[k];
534 xlate->cam_fmt = icd->formats + idx;
535 xlate->buswidth = icd->formats[idx].depth;
536 xlate++;
537 dev_dbg(&ici->dev, "Providing format %s using %s\n",
538 sh_mobile_ceu_formats[k].name,
539 icd->formats[idx].name);
540 }
459 default: 541 default:
460 /* Generic pass-through */ 542 /* Generic pass-through */
461 formats++; 543 formats++;
@@ -477,6 +559,7 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
477 __u32 pixfmt, struct v4l2_rect *rect) 559 __u32 pixfmt, struct v4l2_rect *rect)
478{ 560{
479 struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); 561 struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
562 struct sh_mobile_ceu_dev *pcdev = ici->priv;
480 const struct soc_camera_format_xlate *xlate; 563 const struct soc_camera_format_xlate *xlate;
481 int ret; 564 int ret;
482 565
@@ -497,6 +580,7 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
497 if (pixfmt && !ret) { 580 if (pixfmt && !ret) {
498 icd->buswidth = xlate->buswidth; 581 icd->buswidth = xlate->buswidth;
499 icd->current_fmt = xlate->host_fmt; 582 icd->current_fmt = xlate->host_fmt;
583 pcdev->camera_fmt = xlate->cam_fmt;
500 } 584 }
501 585
502 return ret; 586 return ret;