aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBastian Hecht <hechtb@googlemail.com>2011-09-08 12:15:24 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2011-11-03 16:29:05 -0400
commit171f1a48bb3f95e3ecb37ecd6e8577118d601460 (patch)
tree1e3dcbd0ea5dd18ac3df7f56d5c68425c00c6834
parent95d20109ad6478ecea5e93ba191270fb645d52c7 (diff)
[media] media: ov5642: Add support for arbitrary resolution
This patch adds the ability to get arbitrary resolutions with a width up to 2592 and a height up to 720 pixels instead of the standard 1280x720 only. Signed-off-by: Bastian Hecht <hechtb@gmail.com> Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/media/video/ov5642.c240
1 files changed, 167 insertions, 73 deletions
diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c
index 54178cbeabb4..bb37ec80f274 100644
--- a/drivers/media/video/ov5642.c
+++ b/drivers/media/video/ov5642.c
@@ -14,8 +14,10 @@
14 * published by the Free Software Foundation. 14 * published by the Free Software Foundation.
15 */ 15 */
16 16
17#include <linux/bitops.h>
17#include <linux/delay.h> 18#include <linux/delay.h>
18#include <linux/i2c.h> 19#include <linux/i2c.h>
20#include <linux/kernel.h>
19#include <linux/slab.h> 21#include <linux/slab.h>
20#include <linux/videodev2.h> 22#include <linux/videodev2.h>
21#include <linux/module.h> 23#include <linux/module.h>
@@ -35,7 +37,7 @@
35#define REG_WINDOW_START_Y_LOW 0x3803 37#define REG_WINDOW_START_Y_LOW 0x3803
36#define REG_WINDOW_WIDTH_HIGH 0x3804 38#define REG_WINDOW_WIDTH_HIGH 0x3804
37#define REG_WINDOW_WIDTH_LOW 0x3805 39#define REG_WINDOW_WIDTH_LOW 0x3805
38#define REG_WINDOW_HEIGHT_HIGH 0x3806 40#define REG_WINDOW_HEIGHT_HIGH 0x3806
39#define REG_WINDOW_HEIGHT_LOW 0x3807 41#define REG_WINDOW_HEIGHT_LOW 0x3807
40#define REG_OUT_WIDTH_HIGH 0x3808 42#define REG_OUT_WIDTH_HIGH 0x3808
41#define REG_OUT_WIDTH_LOW 0x3809 43#define REG_OUT_WIDTH_LOW 0x3809
@@ -45,19 +47,44 @@
45#define REG_OUT_TOTAL_WIDTH_LOW 0x380d 47#define REG_OUT_TOTAL_WIDTH_LOW 0x380d
46#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e 48#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e
47#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f 49#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f
50#define REG_OUTPUT_FORMAT 0x4300
51#define REG_ISP_CTRL_01 0x5001
52#define REG_AVG_WINDOW_END_X_HIGH 0x5682
53#define REG_AVG_WINDOW_END_X_LOW 0x5683
54#define REG_AVG_WINDOW_END_Y_HIGH 0x5686
55#define REG_AVG_WINDOW_END_Y_LOW 0x5687
56
57/* active pixel array size */
58#define OV5642_SENSOR_SIZE_X 2592
59#define OV5642_SENSOR_SIZE_Y 1944
48 60
49/* 61/*
50 * define standard resolution. 62 * About OV5642 resolution, cropping and binning:
51 * Works currently only for up to 720 lines 63 * This sensor supports it all, at least in the feature description.
52 * eg. 320x240, 640x480, 800x600, 1280x720, 2048x720 64 * Unfortunately, no combination of appropriate registers settings could make
65 * the chip work the intended way. As it works with predefined register lists,
66 * some undocumented registers are presumably changed there to achieve their
67 * goals.
68 * This driver currently only works for resolutions up to 720 lines with a
69 * 1:1 scale. Hopefully these restrictions will be removed in the future.
53 */ 70 */
71#define OV5642_MAX_WIDTH OV5642_SENSOR_SIZE_X
72#define OV5642_MAX_HEIGHT 720
54 73
55#define OV5642_WIDTH 1280 74/* default sizes */
56#define OV5642_HEIGHT 720 75#define OV5642_DEFAULT_WIDTH 1280
57#define OV5642_TOTAL_WIDTH 3200 76#define OV5642_DEFAULT_HEIGHT OV5642_MAX_HEIGHT
58#define OV5642_TOTAL_HEIGHT 2000 77
59#define OV5642_SENSOR_SIZE_X 2592 78/* minimum extra blanking */
60#define OV5642_SENSOR_SIZE_Y 1944 79#define BLANKING_EXTRA_WIDTH 500
80#define BLANKING_EXTRA_HEIGHT 20
81
82/*
83 * the sensor's autoexposure is buggy when setting total_height low.
84 * It tries to expose longer than 1 frame period without taking care of it
85 * and this leads to weird output. So we set 1000 lines as minimum.
86 */
87#define BLANKING_MIN_HEIGHT 1000
61 88
62struct regval_list { 89struct regval_list {
63 u16 reg_num; 90 u16 reg_num;
@@ -582,6 +609,11 @@ struct ov5642_datafmt {
582struct ov5642 { 609struct ov5642 {
583 struct v4l2_subdev subdev; 610 struct v4l2_subdev subdev;
584 const struct ov5642_datafmt *fmt; 611 const struct ov5642_datafmt *fmt;
612 struct v4l2_rect crop_rect;
613
614 /* blanking information */
615 int total_width;
616 int total_height;
585}; 617};
586 618
587static const struct ov5642_datafmt ov5642_colour_fmts[] = { 619static const struct ov5642_datafmt ov5642_colour_fmts[] = {
@@ -642,6 +674,21 @@ static int reg_write(struct i2c_client *client, u16 reg, u8 val)
642 674
643 return 0; 675 return 0;
644} 676}
677
678/*
679 * convenience function to write 16 bit register values that are split up
680 * into two consecutive high and low parts
681 */
682static int reg_write16(struct i2c_client *client, u16 reg, u16 val16)
683{
684 int ret;
685
686 ret = reg_write(client, reg, val16 >> 8);
687 if (ret)
688 return ret;
689 return reg_write(client, reg + 1, val16 & 0x00ff);
690}
691
645#ifdef CONFIG_VIDEO_ADV_DEBUG 692#ifdef CONFIG_VIDEO_ADV_DEBUG
646static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) 693static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
647{ 694{
@@ -685,58 +732,55 @@ static int ov5642_write_array(struct i2c_client *client,
685 return 0; 732 return 0;
686} 733}
687 734
688static int ov5642_set_resolution(struct i2c_client *client) 735static int ov5642_set_resolution(struct v4l2_subdev *sd)
689{ 736{
737 struct i2c_client *client = v4l2_get_subdevdata(sd);
738 struct ov5642 *priv = to_ov5642(client);
739 int width = priv->crop_rect.width;
740 int height = priv->crop_rect.height;
741 int total_width = priv->total_width;
742 int total_height = priv->total_height;
743 int start_x = (OV5642_SENSOR_SIZE_X - width) / 2;
744 int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2;
690 int ret; 745 int ret;
691 u8 start_x_high = ((OV5642_SENSOR_SIZE_X - OV5642_WIDTH) / 2) >> 8;
692 u8 start_x_low = ((OV5642_SENSOR_SIZE_X - OV5642_WIDTH) / 2) & 0xff;
693 u8 start_y_high = ((OV5642_SENSOR_SIZE_Y - OV5642_HEIGHT) / 2) >> 8;
694 u8 start_y_low = ((OV5642_SENSOR_SIZE_Y - OV5642_HEIGHT) / 2) & 0xff;
695
696 u8 width_high = OV5642_WIDTH >> 8;
697 u8 width_low = OV5642_WIDTH & 0xff;
698 u8 height_high = OV5642_HEIGHT >> 8;
699 u8 height_low = OV5642_HEIGHT & 0xff;
700
701 u8 total_width_high = OV5642_TOTAL_WIDTH >> 8;
702 u8 total_width_low = OV5642_TOTAL_WIDTH & 0xff;
703 u8 total_height_high = OV5642_TOTAL_HEIGHT >> 8;
704 u8 total_height_low = OV5642_TOTAL_HEIGHT & 0xff;
705
706 ret = reg_write(client, REG_WINDOW_START_X_HIGH, start_x_high);
707 if (!ret)
708 ret = reg_write(client, REG_WINDOW_START_X_LOW, start_x_low);
709 if (!ret)
710 ret = reg_write(client, REG_WINDOW_START_Y_HIGH, start_y_high);
711 if (!ret)
712 ret = reg_write(client, REG_WINDOW_START_Y_LOW, start_y_low);
713 746
747 /*
748 * This should set the starting point for cropping.
749 * Doesn't work so far.
750 */
751 ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x);
714 if (!ret) 752 if (!ret)
715 ret = reg_write(client, REG_WINDOW_WIDTH_HIGH, width_high); 753 ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y);
716 if (!ret) 754 if (!ret) {
717 ret = reg_write(client, REG_WINDOW_WIDTH_LOW , width_low); 755 priv->crop_rect.left = start_x;
718 if (!ret) 756 priv->crop_rect.top = start_y;
719 ret = reg_write(client, REG_WINDOW_HEIGHT_HIGH, height_high); 757 }
720 if (!ret)
721 ret = reg_write(client, REG_WINDOW_HEIGHT_LOW, height_low);
722 758
723 if (!ret) 759 if (!ret)
724 ret = reg_write(client, REG_OUT_WIDTH_HIGH, width_high); 760 ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width);
725 if (!ret) 761 if (!ret)
726 ret = reg_write(client, REG_OUT_WIDTH_LOW , width_low); 762 ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height);
727 if (!ret) 763 if (ret)
728 ret = reg_write(client, REG_OUT_HEIGHT_HIGH, height_high); 764 return ret;
765 priv->crop_rect.width = width;
766 priv->crop_rect.height = height;
767
768 /* Set the output window size. Only 1:1 scale is supported so far. */
769 ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width);
729 if (!ret) 770 if (!ret)
730 ret = reg_write(client, REG_OUT_HEIGHT_LOW, height_low); 771 ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height);
731 772
773 /* Total width = output size + blanking */
732 if (!ret) 774 if (!ret)
733 ret = reg_write(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width_high); 775 ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width);
734 if (!ret) 776 if (!ret)
735 ret = reg_write(client, REG_OUT_TOTAL_WIDTH_LOW, total_width_low); 777 ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height);
778
779 /* Sets the window for AWB calculations */
736 if (!ret) 780 if (!ret)
737 ret = reg_write(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height_high); 781 ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width);
738 if (!ret) 782 if (!ret)
739 ret = reg_write(client, REG_OUT_TOTAL_HEIGHT_LOW, total_height_low); 783 ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height);
740 784
741 return ret; 785 return ret;
742} 786}
@@ -744,18 +788,18 @@ static int ov5642_set_resolution(struct i2c_client *client)
744static int ov5642_try_fmt(struct v4l2_subdev *sd, 788static int ov5642_try_fmt(struct v4l2_subdev *sd,
745 struct v4l2_mbus_framefmt *mf) 789 struct v4l2_mbus_framefmt *mf)
746{ 790{
791 struct i2c_client *client = v4l2_get_subdevdata(sd);
792 struct ov5642 *priv = to_ov5642(client);
747 const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code); 793 const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code);
748 794
749 dev_dbg(sd->v4l2_dev->dev, "%s(%u) width: %u heigth: %u\n", 795 mf->width = priv->crop_rect.width;
750 __func__, mf->code, mf->width, mf->height); 796 mf->height = priv->crop_rect.height;
751 797
752 if (!fmt) { 798 if (!fmt) {
753 mf->code = ov5642_colour_fmts[0].code; 799 mf->code = ov5642_colour_fmts[0].code;
754 mf->colorspace = ov5642_colour_fmts[0].colorspace; 800 mf->colorspace = ov5642_colour_fmts[0].colorspace;
755 } 801 }
756 802
757 mf->width = OV5642_WIDTH;
758 mf->height = OV5642_HEIGHT;
759 mf->field = V4L2_FIELD_NONE; 803 mf->field = V4L2_FIELD_NONE;
760 804
761 return 0; 805 return 0;
@@ -767,20 +811,13 @@ static int ov5642_s_fmt(struct v4l2_subdev *sd,
767 struct i2c_client *client = v4l2_get_subdevdata(sd); 811 struct i2c_client *client = v4l2_get_subdevdata(sd);
768 struct ov5642 *priv = to_ov5642(client); 812 struct ov5642 *priv = to_ov5642(client);
769 813
770 dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
771
772 /* MIPI CSI could have changed the format, double-check */ 814 /* MIPI CSI could have changed the format, double-check */
773 if (!ov5642_find_datafmt(mf->code)) 815 if (!ov5642_find_datafmt(mf->code))
774 return -EINVAL; 816 return -EINVAL;
775 817
776 ov5642_try_fmt(sd, mf); 818 ov5642_try_fmt(sd, mf);
777
778 priv->fmt = ov5642_find_datafmt(mf->code); 819 priv->fmt = ov5642_find_datafmt(mf->code);
779 820
780 ov5642_write_array(client, ov5642_default_regs_init);
781 ov5642_set_resolution(client);
782 ov5642_write_array(client, ov5642_default_regs_finalise);
783
784 return 0; 821 return 0;
785} 822}
786 823
@@ -794,8 +831,8 @@ static int ov5642_g_fmt(struct v4l2_subdev *sd,
794 831
795 mf->code = fmt->code; 832 mf->code = fmt->code;
796 mf->colorspace = fmt->colorspace; 833 mf->colorspace = fmt->colorspace;
797 mf->width = OV5642_WIDTH; 834 mf->width = priv->crop_rect.width;
798 mf->height = OV5642_HEIGHT; 835 mf->height = priv->crop_rect.height;
799 mf->field = V4L2_FIELD_NONE; 836 mf->field = V4L2_FIELD_NONE;
800 837
801 return 0; 838 return 0;
@@ -828,15 +865,44 @@ static int ov5642_g_chip_ident(struct v4l2_subdev *sd,
828 return 0; 865 return 0;
829} 866}
830 867
868static int ov5642_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
869{
870 struct i2c_client *client = v4l2_get_subdevdata(sd);
871 struct ov5642 *priv = to_ov5642(client);
872 struct v4l2_rect *rect = &a->c;
873 int ret;
874
875 v4l_bound_align_image(&rect->width, 48, OV5642_MAX_WIDTH, 1,
876 &rect->height, 32, OV5642_MAX_HEIGHT, 1, 0);
877
878 priv->crop_rect.width = rect->width;
879 priv->crop_rect.height = rect->height;
880 priv->total_width = rect->width + BLANKING_EXTRA_WIDTH;
881 priv->total_height = max_t(int, rect->height +
882 BLANKING_EXTRA_HEIGHT,
883 BLANKING_MIN_HEIGHT);
884 priv->crop_rect.width = rect->width;
885 priv->crop_rect.height = rect->height;
886
887 ret = ov5642_write_array(client, ov5642_default_regs_init);
888 if (!ret)
889 ret = ov5642_set_resolution(sd);
890 if (!ret)
891 ret = ov5642_write_array(client, ov5642_default_regs_finalise);
892
893 return ret;
894}
895
831static int ov5642_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) 896static int ov5642_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
832{ 897{
898 struct i2c_client *client = v4l2_get_subdevdata(sd);
899 struct ov5642 *priv = to_ov5642(client);
833 struct v4l2_rect *rect = &a->c; 900 struct v4l2_rect *rect = &a->c;
834 901
835 a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 902 if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
836 rect->top = 0; 903 return -EINVAL;
837 rect->left = 0; 904
838 rect->width = OV5642_WIDTH; 905 *rect = priv->crop_rect;
839 rect->height = OV5642_HEIGHT;
840 906
841 return 0; 907 return 0;
842} 908}
@@ -845,8 +911,8 @@ static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
845{ 911{
846 a->bounds.left = 0; 912 a->bounds.left = 0;
847 a->bounds.top = 0; 913 a->bounds.top = 0;
848 a->bounds.width = OV5642_WIDTH; 914 a->bounds.width = OV5642_MAX_WIDTH;
849 a->bounds.height = OV5642_HEIGHT; 915 a->bounds.height = OV5642_MAX_HEIGHT;
850 a->defrect = a->bounds; 916 a->defrect = a->bounds;
851 a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 917 a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
852 a->pixelaspect.numerator = 1; 918 a->pixelaspect.numerator = 1;
@@ -859,24 +925,43 @@ static int ov5642_g_mbus_config(struct v4l2_subdev *sd,
859 struct v4l2_mbus_config *cfg) 925 struct v4l2_mbus_config *cfg)
860{ 926{
861 cfg->type = V4L2_MBUS_CSI2; 927 cfg->type = V4L2_MBUS_CSI2;
862 cfg->flags = V4L2_MBUS_CSI2_2_LANE | 928 cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
863 V4L2_MBUS_CSI2_CHANNEL_0 | 929 V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
864 V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
865 930
866 return 0; 931 return 0;
867} 932}
868 933
934static int ov5642_s_power(struct v4l2_subdev *sd, int on)
935{
936 struct i2c_client *client;
937 int ret;
938
939 if (!on)
940 return 0;
941
942 client = v4l2_get_subdevdata(sd);
943 ret = ov5642_write_array(client, ov5642_default_regs_init);
944 if (!ret)
945 ret = ov5642_set_resolution(sd);
946 if (!ret)
947 ret = ov5642_write_array(client, ov5642_default_regs_finalise);
948
949 return ret;
950}
951
869static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = { 952static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = {
870 .s_mbus_fmt = ov5642_s_fmt, 953 .s_mbus_fmt = ov5642_s_fmt,
871 .g_mbus_fmt = ov5642_g_fmt, 954 .g_mbus_fmt = ov5642_g_fmt,
872 .try_mbus_fmt = ov5642_try_fmt, 955 .try_mbus_fmt = ov5642_try_fmt,
873 .enum_mbus_fmt = ov5642_enum_fmt, 956 .enum_mbus_fmt = ov5642_enum_fmt,
957 .s_crop = ov5642_s_crop,
874 .g_crop = ov5642_g_crop, 958 .g_crop = ov5642_g_crop,
875 .cropcap = ov5642_cropcap, 959 .cropcap = ov5642_cropcap,
876 .g_mbus_config = ov5642_g_mbus_config, 960 .g_mbus_config = ov5642_g_mbus_config,
877}; 961};
878 962
879static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = { 963static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = {
964 .s_power = ov5642_s_power,
880 .g_chip_ident = ov5642_g_chip_ident, 965 .g_chip_ident = ov5642_g_chip_ident,
881#ifdef CONFIG_VIDEO_ADV_DEBUG 966#ifdef CONFIG_VIDEO_ADV_DEBUG
882 .g_register = ov5642_get_register, 967 .g_register = ov5642_get_register,
@@ -934,7 +1019,16 @@ static int ov5642_probe(struct i2c_client *client,
934 1019
935 v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops); 1020 v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops);
936 1021
937 priv->fmt = &ov5642_colour_fmts[0]; 1022 priv->fmt = &ov5642_colour_fmts[0];
1023
1024 priv->crop_rect.width = OV5642_DEFAULT_WIDTH;
1025 priv->crop_rect.height = OV5642_DEFAULT_HEIGHT;
1026 priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2;
1027 priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2;
1028 priv->crop_rect.width = OV5642_DEFAULT_WIDTH;
1029 priv->crop_rect.height = OV5642_DEFAULT_HEIGHT;
1030 priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH;
1031 priv->total_height = BLANKING_MIN_HEIGHT;
938 1032
939 ret = ov5642_video_probe(client); 1033 ret = ov5642_video_probe(client);
940 if (ret < 0) 1034 if (ret < 0)