aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/ov9740.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/ov9740.c')
-rw-r--r--drivers/media/video/ov9740.c210
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
185enum { 185#define OV9740_MAX_HEIGHT 720
186 OV9740_VGA,
187 OV9740_720P,
188};
189
190struct ov9740_resolution {
191 unsigned int width;
192 unsigned int height;
193};
194
195static 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 */
207struct ov9740_reg { 188struct 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
406static 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
430static 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
454static enum v4l2_mbus_pixelcode ov9740_codes[] = { 387static 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 */
728static void ov9740_res_roundup(u32 *width, u32 *height) 661static 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 */
745static int ov9740_set_res(struct i2c_client *client, u32 width) 675static 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
780done:
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;