diff options
Diffstat (limited to 'drivers/media/video/ov9740.c')
-rw-r--r-- | drivers/media/video/ov9740.c | 210 |
1 files changed, 114 insertions, 96 deletions
diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c index 72c6ac1d767..decd70650ea 100644 --- a/drivers/media/video/ov9740.c +++ b/drivers/media/video/ov9740.c | |||
@@ -181,27 +181,8 @@ | |||
181 | #define OV9740_MIPI_CTRL_3012 0x3012 | 181 | #define OV9740_MIPI_CTRL_3012 0x3012 |
182 | #define OV9740_SC_CMMM_MIPI_CTR 0x3014 | 182 | #define OV9740_SC_CMMM_MIPI_CTR 0x3014 |
183 | 183 | ||
184 | /* supported resolutions */ | 184 | #define OV9740_MAX_WIDTH 1280 |
185 | enum { | 185 | #define OV9740_MAX_HEIGHT 720 |
186 | OV9740_VGA, | ||
187 | OV9740_720P, | ||
188 | }; | ||
189 | |||
190 | struct ov9740_resolution { | ||
191 | unsigned int width; | ||
192 | unsigned int height; | ||
193 | }; | ||
194 | |||
195 | static struct ov9740_resolution ov9740_resolutions[] = { | ||
196 | [OV9740_VGA] = { | ||
197 | .width = 640, | ||
198 | .height = 480, | ||
199 | }, | ||
200 | [OV9740_720P] = { | ||
201 | .width = 1280, | ||
202 | .height = 720, | ||
203 | }, | ||
204 | }; | ||
205 | 186 | ||
206 | /* Misc. structures */ | 187 | /* Misc. structures */ |
207 | struct ov9740_reg { | 188 | struct ov9740_reg { |
@@ -403,54 +384,6 @@ static const struct ov9740_reg ov9740_defaults[] = { | |||
403 | { OV9740_ISP_CTRL19, 0x02 }, | 384 | { OV9740_ISP_CTRL19, 0x02 }, |
404 | }; | 385 | }; |
405 | 386 | ||
406 | static const struct ov9740_reg ov9740_regs_vga[] = { | ||
407 | { OV9740_X_ADDR_START_HI, 0x00 }, | ||
408 | { OV9740_X_ADDR_START_LO, 0xa0 }, | ||
409 | { OV9740_Y_ADDR_START_HI, 0x00 }, | ||
410 | { OV9740_Y_ADDR_START_LO, 0x00 }, | ||
411 | { OV9740_X_ADDR_END_HI, 0x04 }, | ||
412 | { OV9740_X_ADDR_END_LO, 0x63 }, | ||
413 | { OV9740_Y_ADDR_END_HI, 0x02 }, | ||
414 | { OV9740_Y_ADDR_END_LO, 0xd3 }, | ||
415 | { OV9740_X_OUTPUT_SIZE_HI, 0x02 }, | ||
416 | { OV9740_X_OUTPUT_SIZE_LO, 0x80 }, | ||
417 | { OV9740_Y_OUTPUT_SIZE_HI, 0x01 }, | ||
418 | { OV9740_Y_OUTPUT_SIZE_LO, 0xe0 }, | ||
419 | { OV9740_ISP_CTRL1E, 0x03 }, | ||
420 | { OV9740_ISP_CTRL1F, 0xc0 }, | ||
421 | { OV9740_ISP_CTRL20, 0x02 }, | ||
422 | { OV9740_ISP_CTRL21, 0xd0 }, | ||
423 | { OV9740_VFIFO_READ_START_HI, 0x01 }, | ||
424 | { OV9740_VFIFO_READ_START_LO, 0x40 }, | ||
425 | { OV9740_ISP_CTRL00, 0xff }, | ||
426 | { OV9740_ISP_CTRL01, 0xff }, | ||
427 | { OV9740_ISP_CTRL03, 0xff }, | ||
428 | }; | ||
429 | |||
430 | static const struct ov9740_reg ov9740_regs_720p[] = { | ||
431 | { OV9740_X_ADDR_START_HI, 0x00 }, | ||
432 | { OV9740_X_ADDR_START_LO, 0x00 }, | ||
433 | { OV9740_Y_ADDR_START_HI, 0x00 }, | ||
434 | { OV9740_Y_ADDR_START_LO, 0x00 }, | ||
435 | { OV9740_X_ADDR_END_HI, 0x05 }, | ||
436 | { OV9740_X_ADDR_END_LO, 0x03 }, | ||
437 | { OV9740_Y_ADDR_END_HI, 0x02 }, | ||
438 | { OV9740_Y_ADDR_END_LO, 0xd3 }, | ||
439 | { OV9740_X_OUTPUT_SIZE_HI, 0x05 }, | ||
440 | { OV9740_X_OUTPUT_SIZE_LO, 0x00 }, | ||
441 | { OV9740_Y_OUTPUT_SIZE_HI, 0x02 }, | ||
442 | { OV9740_Y_OUTPUT_SIZE_LO, 0xd0 }, | ||
443 | { OV9740_ISP_CTRL1E, 0x05 }, | ||
444 | { OV9740_ISP_CTRL1F, 0x00 }, | ||
445 | { OV9740_ISP_CTRL20, 0x02 }, | ||
446 | { OV9740_ISP_CTRL21, 0xd0 }, | ||
447 | { OV9740_VFIFO_READ_START_HI, 0x02 }, | ||
448 | { OV9740_VFIFO_READ_START_LO, 0x30 }, | ||
449 | { OV9740_ISP_CTRL00, 0xff }, | ||
450 | { OV9740_ISP_CTRL01, 0xef }, | ||
451 | { OV9740_ISP_CTRL03, 0xff }, | ||
452 | }; | ||
453 | |||
454 | static enum v4l2_mbus_pixelcode ov9740_codes[] = { | 387 | static enum v4l2_mbus_pixelcode ov9740_codes[] = { |
455 | V4L2_MBUS_FMT_YUYV8_2X8, | 388 | V4L2_MBUS_FMT_YUYV8_2X8, |
456 | }; | 389 | }; |
@@ -727,39 +660,124 @@ static int ov9740_set_register(struct v4l2_subdev *sd, | |||
727 | /* select nearest higher resolution for capture */ | 660 | /* select nearest higher resolution for capture */ |
728 | static void ov9740_res_roundup(u32 *width, u32 *height) | 661 | static void ov9740_res_roundup(u32 *width, u32 *height) |
729 | { | 662 | { |
730 | int i; | 663 | /* Width must be a multiple of 4 pixels. */ |
664 | *width = ALIGN(*width, 4); | ||
731 | 665 | ||
732 | for (i = 0; i < ARRAY_SIZE(ov9740_resolutions); i++) | 666 | /* Max resolution is 1280x720 (720p). */ |
733 | if ((ov9740_resolutions[i].width >= *width) && | 667 | if (*width > OV9740_MAX_WIDTH) |
734 | (ov9740_resolutions[i].height >= *height)) { | 668 | *width = OV9740_MAX_WIDTH; |
735 | *width = ov9740_resolutions[i].width; | ||
736 | *height = ov9740_resolutions[i].height; | ||
737 | return; | ||
738 | } | ||
739 | 669 | ||
740 | *width = ov9740_resolutions[OV9740_720P].width; | 670 | if (*height > OV9740_MAX_HEIGHT) |
741 | *height = ov9740_resolutions[OV9740_720P].height; | 671 | *height = OV9740_MAX_HEIGHT; |
742 | } | 672 | } |
743 | 673 | ||
744 | /* Setup registers according to resolution and color encoding */ | 674 | /* Setup registers according to resolution and color encoding */ |
745 | static int ov9740_set_res(struct i2c_client *client, u32 width) | 675 | static int ov9740_set_res(struct i2c_client *client, u32 width, u32 height) |
746 | { | 676 | { |
677 | u32 x_start; | ||
678 | u32 y_start; | ||
679 | u32 x_end; | ||
680 | u32 y_end; | ||
681 | bool scaling = 0; | ||
682 | u32 scale_input_x; | ||
683 | u32 scale_input_y; | ||
747 | int ret; | 684 | int ret; |
748 | 685 | ||
749 | /* select register configuration for given resolution */ | 686 | if ((width != OV9740_MAX_WIDTH) || (height != OV9740_MAX_HEIGHT)) |
750 | if (width == ov9740_resolutions[OV9740_VGA].width) { | 687 | scaling = 1; |
751 | dev_dbg(&client->dev, "Setting image size to 640x480\n"); | 688 | |
752 | ret = ov9740_reg_write_array(client, ov9740_regs_vga, | 689 | /* |
753 | ARRAY_SIZE(ov9740_regs_vga)); | 690 | * Try to use as much of the sensor area as possible when supporting |
754 | } else if (width == ov9740_resolutions[OV9740_720P].width) { | 691 | * smaller resolutions. Depending on the aspect ratio of the |
755 | dev_dbg(&client->dev, "Setting image size to 1280x720\n"); | 692 | * chosen resolution, we can either use the full width of the sensor, |
756 | ret = ov9740_reg_write_array(client, ov9740_regs_720p, | 693 | * or the full height of the sensor (or both if the aspect ratio is |
757 | ARRAY_SIZE(ov9740_regs_720p)); | 694 | * the same as 1280x720. |
695 | */ | ||
696 | if ((OV9740_MAX_WIDTH * height) > (OV9740_MAX_HEIGHT * width)) { | ||
697 | scale_input_x = (OV9740_MAX_HEIGHT * width) / height; | ||
698 | scale_input_y = OV9740_MAX_HEIGHT; | ||
758 | } else { | 699 | } else { |
759 | dev_err(&client->dev, "Failed to select resolution!\n"); | 700 | scale_input_x = OV9740_MAX_WIDTH; |
760 | return -EINVAL; | 701 | scale_input_y = (OV9740_MAX_WIDTH * height) / width; |
761 | } | 702 | } |
762 | 703 | ||
704 | /* These describe the area of the sensor to use. */ | ||
705 | x_start = (OV9740_MAX_WIDTH - scale_input_x) / 2; | ||
706 | y_start = (OV9740_MAX_HEIGHT - scale_input_y) / 2; | ||
707 | x_end = x_start + scale_input_x - 1; | ||
708 | y_end = y_start + scale_input_y - 1; | ||
709 | |||
710 | ret = ov9740_reg_write(client, OV9740_X_ADDR_START_HI, x_start >> 8); | ||
711 | if (ret) | ||
712 | goto done; | ||
713 | ret = ov9740_reg_write(client, OV9740_X_ADDR_START_LO, x_start & 0xff); | ||
714 | if (ret) | ||
715 | goto done; | ||
716 | ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_HI, y_start >> 8); | ||
717 | if (ret) | ||
718 | goto done; | ||
719 | ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_LO, y_start & 0xff); | ||
720 | if (ret) | ||
721 | goto done; | ||
722 | |||
723 | ret = ov9740_reg_write(client, OV9740_X_ADDR_END_HI, x_end >> 8); | ||
724 | if (ret) | ||
725 | goto done; | ||
726 | ret = ov9740_reg_write(client, OV9740_X_ADDR_END_LO, x_end & 0xff); | ||
727 | if (ret) | ||
728 | goto done; | ||
729 | ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_HI, y_end >> 8); | ||
730 | if (ret) | ||
731 | goto done; | ||
732 | ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_LO, y_end & 0xff); | ||
733 | if (ret) | ||
734 | goto done; | ||
735 | |||
736 | ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_HI, width >> 8); | ||
737 | if (ret) | ||
738 | goto done; | ||
739 | ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_LO, width & 0xff); | ||
740 | if (ret) | ||
741 | goto done; | ||
742 | ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_HI, height >> 8); | ||
743 | if (ret) | ||
744 | goto done; | ||
745 | ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_LO, height & 0xff); | ||
746 | if (ret) | ||
747 | goto done; | ||
748 | |||
749 | ret = ov9740_reg_write(client, OV9740_ISP_CTRL1E, scale_input_x >> 8); | ||
750 | if (ret) | ||
751 | goto done; | ||
752 | ret = ov9740_reg_write(client, OV9740_ISP_CTRL1F, scale_input_x & 0xff); | ||
753 | if (ret) | ||
754 | goto done; | ||
755 | ret = ov9740_reg_write(client, OV9740_ISP_CTRL20, scale_input_y >> 8); | ||
756 | if (ret) | ||
757 | goto done; | ||
758 | ret = ov9740_reg_write(client, OV9740_ISP_CTRL21, scale_input_y & 0xff); | ||
759 | if (ret) | ||
760 | goto done; | ||
761 | |||
762 | ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_HI, | ||
763 | (scale_input_x - width) >> 8); | ||
764 | if (ret) | ||
765 | goto done; | ||
766 | ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_LO, | ||
767 | (scale_input_x - width) & 0xff); | ||
768 | if (ret) | ||
769 | goto done; | ||
770 | |||
771 | ret = ov9740_reg_write(client, OV9740_ISP_CTRL00, 0xff); | ||
772 | if (ret) | ||
773 | goto done; | ||
774 | ret = ov9740_reg_write(client, OV9740_ISP_CTRL01, 0xef | | ||
775 | (scaling << 4)); | ||
776 | if (ret) | ||
777 | goto done; | ||
778 | ret = ov9740_reg_write(client, OV9740_ISP_CTRL03, 0xff); | ||
779 | |||
780 | done: | ||
763 | return ret; | 781 | return ret; |
764 | } | 782 | } |
765 | 783 | ||
@@ -787,7 +805,7 @@ static int ov9740_s_fmt(struct v4l2_subdev *sd, | |||
787 | if (ret < 0) | 805 | if (ret < 0) |
788 | return ret; | 806 | return ret; |
789 | 807 | ||
790 | ret = ov9740_set_res(client, mf->width); | 808 | ret = ov9740_set_res(client, mf->width, mf->height); |
791 | if (ret < 0) | 809 | if (ret < 0) |
792 | return ret; | 810 | return ret; |
793 | 811 | ||
@@ -824,8 +842,8 @@ static int ov9740_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) | |||
824 | { | 842 | { |
825 | a->bounds.left = 0; | 843 | a->bounds.left = 0; |
826 | a->bounds.top = 0; | 844 | a->bounds.top = 0; |
827 | a->bounds.width = ov9740_resolutions[OV9740_720P].width; | 845 | a->bounds.width = OV9740_MAX_WIDTH; |
828 | a->bounds.height = ov9740_resolutions[OV9740_720P].height; | 846 | a->bounds.height = OV9740_MAX_HEIGHT; |
829 | a->defrect = a->bounds; | 847 | a->defrect = a->bounds; |
830 | a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 848 | a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
831 | a->pixelaspect.numerator = 1; | 849 | a->pixelaspect.numerator = 1; |
@@ -838,8 +856,8 @@ static int ov9740_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) | |||
838 | { | 856 | { |
839 | a->c.left = 0; | 857 | a->c.left = 0; |
840 | a->c.top = 0; | 858 | a->c.top = 0; |
841 | a->c.width = ov9740_resolutions[OV9740_720P].width; | 859 | a->c.width = OV9740_MAX_WIDTH; |
842 | a->c.height = ov9740_resolutions[OV9740_720P].height; | 860 | a->c.height = OV9740_MAX_HEIGHT; |
843 | a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 861 | a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
844 | 862 | ||
845 | return 0; | 863 | return 0; |