diff options
-rw-r--r-- | drivers/media/video/sh_mobile_ceu_camera.c | 114 |
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 | ||
101 | static void ceu_write(struct sh_mobile_ceu_dev *priv, | 103 | static void ceu_write(struct sh_mobile_ceu_dev *priv, |
@@ -155,6 +157,9 @@ static void free_buffer(struct videobuf_queue *vq, | |||
155 | 157 | ||
156 | static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) | 158 | static 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 | ||
173 | static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, | 188 | static 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 | ||
499 | static 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 | |||
447 | static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, | 514 | static 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; |