MLK-17741-2: media: add ov5640 mipi driver
authorGuoniu.Zhou <guoniu.zhou@nxp.com>
Fri, 9 Mar 2018 08:19:37 +0000 (16:19 +0800)
committerHaibo Chen <haibo.chen@nxp.com>
Thu, 12 Apr 2018 10:45:43 +0000 (18:45 +0800)
Add driver for ov5640 mipi csi interface.

Reviewed-by: Sandor.Yu <sandor.yu@nxp.com>
Signed-off-by: Guoniu.Zhou <guoniu.zhou@nxp.com>
(cherry picked from commit 860dab368735dcbf248da7b74163ce6048a57514)

drivers/media/platform/imx8/mxc-isi-cap.c
drivers/media/platform/imx8/mxc-media-dev.c
drivers/media/platform/imx8/mxc-mipi-csi2.c
drivers/media/platform/imx8/ov5640_v3.c

index 6baeb5c..c2419c1 100644 (file)
@@ -166,7 +166,8 @@ struct mxc_isi_fmt *mxc_isi_get_src_fmt(struct v4l2_subdev_format *sd_fmt)
 
        /* two fmt RGB32 and YUV444 from pixellink */
        if (sd_fmt->format.code == MEDIA_BUS_FMT_YUYV8_1X16 ||
-               sd_fmt->format.code == MEDIA_BUS_FMT_YVYU8_2X8)
+               sd_fmt->format.code == MEDIA_BUS_FMT_YVYU8_2X8 ||
+               sd_fmt->format.code == MEDIA_BUS_FMT_UYVY8_2X8)
                index = 1;
        else
                index = 0;
index fe021dd..fb1faa5 100644 (file)
@@ -240,7 +240,7 @@ static int mxc_md_create_links(struct mxc_md *mxc_md)
                        if (mipi_csi2->vchannel == true)
                                mipi_vc = 4;
                        else
-                               mipi_vc = 0;
+                               mipi_vc = 1;
 
                        for (j = 0; j < mipi_vc; j++) {
                                ret = media_create_pad_link(source, source_pad + j, sink, sink_pad + j,
index 3d46b62..7972b06 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 NXP
+ * Copyright 2017-2018 NXP
  */
 /*
  * The code contained herein is licensed under the GNU General Public
@@ -219,7 +219,8 @@ static void mxc_mipi_csi2_csr_config(struct mxc_mipi_csi2_dev *csi2dev)
                CSI2SS_PHY_CTRL_CONT_CLK_MODE << CSI2SS_PHY_CTRL_CONT_CLK_MODE_OFFSET |
                csi2dev->hs_settle << CSI2SS_PHY_CTRL_RX_HS_SETTLE_OFFSET |
                CSI2SS_PHY_CTRL_PD << CSI2SS_PHY_CTRL_PD_OFFSET |
-               CSI2SS_PHY_CTRL_RTERM_SEL << CSI2SS_PHY_CTRL_RTERM_SEL_OFFSET;
+               CSI2SS_PHY_CTRL_RTERM_SEL << CSI2SS_PHY_CTRL_RTERM_SEL_OFFSET |
+               CSI2SS_PHY_CTRL_AUTO_PD_EN << CSI2SS_PHY_CTRL_AUTO_PD_EN_OFFSET;
 
        writel(val, csi2dev->csr_regs + CSI2SS_PHY_CTRL);
 }
@@ -309,6 +310,8 @@ static int mxc_csi2_get_sensor_fmt(struct mxc_mipi_csi2_dev *csi2dev)
        /* Get rxhs settle */
        if (src_fmt.format.reserved[0] != 0)
                csi2dev->hs_settle = calc_hs_settle(csi2dev, src_fmt.format.reserved[0]);
+       else if (src_fmt.format.reserved[1] != 0)
+               csi2dev->hs_settle = src_fmt.format.reserved[1];
        else {
                if (src_fmt.format.height * src_fmt.format.width > 1024 * 768)
                        csi2dev->hs_settle = rxhs_settle[2];
index 106cf8c..1371ab8 100644 (file)
@@ -18,7 +18,6 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
-#define DEBUG
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/device.h>
@@ -96,6 +95,13 @@ struct ov5640_pll_info {
        u32 init_data_size;
 };
 
+struct ov5640_hs_info {
+       u32 width;
+       u32 height;
+       u32 frame_rate;
+       u32 val;
+};
+
 struct ov5640 {
        struct v4l2_subdev              subdev;
        struct i2c_client *i2c_client;
@@ -104,6 +110,7 @@ struct ov5640 {
        struct v4l2_captureparm streamcap;
        struct media_pad pads[OV5640_SENS_PADS_NUM];
        bool on;
+       bool mipi_csi;
 
        /* control settings */
        int brightness;
@@ -343,7 +350,6 @@ static struct reg_value ov5640_init_parm[] = {
     {0x583d, 0xce, 0, 0},
 };
 
-/*static struct reg_value ov5640_resolution_parm[] = {*/
 static struct reg_value ov5640_setting_VGA_640_480[] = {
        {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0},
        {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0},
@@ -399,6 +405,7 @@ static struct reg_value ov5640_setting_1080P_1920_1080[] = {
        {0x3815, 0x11, 0, 0},
 };
 
+/* DVP */
 static struct reg_value ov5640_pll_VGA_30fps_640_480[] = {
        {0x3035, 0x11, 0, 0}, {0x3036, 0x46, 0, 0}, {0x460c, 0x22, 0, 0},
        {0x3824, 0x02, 0, 0}, {0x4837, 0x22, 0, 0},
@@ -444,6 +451,52 @@ static struct reg_value ov5640_pll_1080P_15fps_1920_1080[] = {
        {0x3824, 0x04, 0, 0}, {0x4837, 0x16, 0, 0},
 };
 
+/* MIPI */
+static struct reg_value ov5640_mipi_pll_VGA_30fps_640_480[] = {
+       {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x460c, 0x22, 0, 0},
+       {0x3824, 0x02, 0, 0}, {0x4837, 0x0a, 0, 0},
+};
+
+static struct reg_value ov5640_mipi_pll_VGA_15fps_640_480[] = {
+       {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x460c, 0x22, 0, 0},
+       {0x3824, 0x02, 0, 0}, {0x4837, 0x0a, 0, 0},
+};
+
+static struct reg_value ov5640_mipi_pll_QVGA_30fps_320_240[] = {
+       {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x460c, 0x22, 0, 0},
+       {0x3824, 0x02, 0, 0}, {0x4837, 0x22, 0, 0},
+};
+
+static struct reg_value ov5640_mipi_pll_QVGA_15fps_320_240[] = {
+       {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x460c, 0x22, 0, 0},
+       {0x3824, 0x02, 0, 0}, {0x4837, 0x0a, 0, 0},
+};
+
+static struct reg_value ov5640_mipi_pll_30fps_480_272[] = {
+       {0x3035, 0x21, 0, 0}, {0x3036, 0x69, 0, 0}, {0x460c, 0x20, 0, 0},
+       {0x3824, 0x04, 0, 0}, {0x4837, 0x16, 0, 0},
+};
+
+static struct reg_value ov5640_mipi_pll_15fps_480_272[] = {
+       {0x3035, 0x41, 0, 0}, {0x3036, 0x69, 0, 0}, {0x460c, 0x20, 0, 0},
+       {0x3824, 0x04, 0, 0}, {0x4837, 0x16, 0, 0},
+};
+
+static struct reg_value ov5640_mipi_pll_720P_30fps_1280_720[] = {
+       {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x460c, 0x20, 0, 0},
+       {0x3824, 0x04, 0, 0}, {0x4837, 0x0a, 0, 0},
+};
+
+static struct reg_value ov5640_mipi_pll_720P_15fps_1280_720[] = {
+       {0x3035, 0x41, 0, 0}, {0x3036, 0x54, 0, 0}, {0x460c, 0x20, 0, 0},
+       {0x3824, 0x04, 0, 0}, {0x4837, 0x0a, 0, 0},
+};
+
+static struct reg_value ov5640_mipi_pll_1080P_15fps_1920_1080[] = {
+       {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x460c, 0x20, 0, 0},
+       {0x3824, 0x04, 0, 0}, {0x4837, 0x0a, 0, 0},
+};
+
 static struct reg_value ov5640_config[] = {
        {0x302C, 0xc2, 0, 0}, /* Driver Capability */
 
@@ -459,7 +512,36 @@ static struct reg_value ov5640_config[] = {
                                             1: hight level
                                                 0: low level valid*/
        {0x5000, 0xa7, 0, 0},
-       /*{0x3008, 0x02, 0, 0},*/
+};
+
+static struct reg_value ov5640_mipi_config[] = {
+       {0x302C, 0xc2, 0, 0}, /* Driver Capability */
+
+       {0x4300, 0x3F, 0, 0}, /* YUV422 YVYU */
+       {0x501f, 0x00, 0, 0}, /* YUV422 YVYU */
+
+       {0x3034, 0x18, 0, 0}, /* MIPI 8bits mode */
+       {0x3017, 0x00, 0, 0},
+       {0x3018, 0x00, 0, 0},
+
+       {0x300e, 0x45, 0, 0}, /* MIPI mode */
+       {0x4800, 0x04, 0, 0},
+
+       {0x4740, 0x23, 0, 0}, /* BIT5: Pixel BIT1: HSYNC BIT0: VSYNC
+                                            1: hight level
+                                                0: low level valid*/
+       {0x5000, 0xa7, 0, 0},
+};
+
+static struct ov5640_hs_info hs_setting[] = {
+       {1920, 1080, 30, 0x0B},
+       {1920, 1080, 15, 0x10},
+       {1280, 720,  30, 0x11},
+       {1280, 720,  15, 0x16},
+       {640,  480,  30, 0x1E},
+       {640,  480,  15, 0x23},
+       {320,  240,  30, 0x1E},
+       {320,  240,  15, 0x23},
 };
 
 static struct ov5640_mode_info ov5640_mode_info_data[ov5640_mode_MAX + 1] = {
@@ -512,6 +594,39 @@ static struct ov5640_pll_info ov5640_pll_info_data[2][ov5640_mode_MAX + 1] = {
        },
 };
 
+static struct ov5640_pll_info ov5640_mipi_pll_info_data[2][ov5640_mode_MAX + 1] = {
+       {
+               {ov5640_mode_VGA_640_480, ov5640_mipi_pll_VGA_15fps_640_480,
+               ARRAY_SIZE(ov5640_mipi_pll_VGA_15fps_640_480)},
+
+               {ov5640_mode_QVGA_320_240, ov5640_mipi_pll_QVGA_15fps_320_240,
+               ARRAY_SIZE(ov5640_mipi_pll_QVGA_15fps_320_240)},
+
+               {ov5640_mode_480_272, ov5640_mipi_pll_15fps_480_272,
+               ARRAY_SIZE(ov5640_mipi_pll_15fps_480_272)},
+
+               {ov5640_mode_720P_1280_720, ov5640_mipi_pll_720P_15fps_1280_720,
+               ARRAY_SIZE(ov5640_mipi_pll_720P_15fps_1280_720)},
+
+               {ov5640_mode_1080P_1920_1080, ov5640_mipi_pll_1080P_15fps_1920_1080,
+               ARRAY_SIZE(ov5640_mipi_pll_1080P_15fps_1920_1080)},
+       },
+
+       {
+               {ov5640_mode_VGA_640_480, ov5640_mipi_pll_VGA_30fps_640_480,
+               ARRAY_SIZE(ov5640_mipi_pll_VGA_30fps_640_480)},
+
+               {ov5640_mode_QVGA_320_240, ov5640_mipi_pll_QVGA_30fps_320_240,
+               ARRAY_SIZE(ov5640_mipi_pll_QVGA_30fps_320_240)},
+
+               {ov5640_mode_480_272, ov5640_mipi_pll_30fps_480_272,
+               ARRAY_SIZE(ov5640_mipi_pll_30fps_480_272)},
+
+               {ov5640_mode_720P_1280_720, ov5640_mipi_pll_720P_30fps_1280_720,
+               ARRAY_SIZE(ov5640_mipi_pll_720P_30fps_1280_720)},
+       },
+};
+
 static struct regulator *io_regulator;
 static struct regulator *core_regulator;
 static struct regulator *analog_regulator;
@@ -542,6 +657,7 @@ static struct i2c_driver ov5640_i2c_driver = {
 
 static const struct ov5640_datafmt ov5640_colour_fmts[] = {
        {MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG},
+       {MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
 };
 
 static struct ov5640 *to_ov5640(const struct i2c_client *client)
@@ -549,6 +665,27 @@ static struct ov5640 *to_ov5640(const struct i2c_client *client)
        return container_of(i2c_get_clientdata(client), struct ov5640, subdev);
 }
 
+static uint16_t find_hs_configure(struct ov5640 *sensor)
+{
+       struct device *dev = &sensor->i2c_client->dev;
+       struct v4l2_fract *timeperframe = &sensor->streamcap.timeperframe;
+       struct v4l2_pix_format *pix = &sensor->pix;
+       u32 frame_rate = timeperframe->denominator / timeperframe->numerator;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(hs_setting); i++) {
+               if (hs_setting[i].width == pix->width &&
+                       hs_setting[i].height == pix->height &&
+                       hs_setting[i].frame_rate == frame_rate)
+                 return hs_setting[i].val;
+       }
+
+       if (i == ARRAY_SIZE(hs_setting))
+               dev_err(dev, "%s can not find hs configure\n", __func__);
+
+       return -EINVAL;
+}
+
 /* Find a data format by a pixel code in an array */
 static const struct ov5640_datafmt
                        *ov5640_find_datafmt(u32 code)
@@ -778,6 +915,11 @@ static int ov5640_config_resolution(enum ov5640_mode mode)
                ov5640_write_reg(0x3821, 0x06);
        }
 
+       if (ov5640_data.mipi_csi && mode == ov5640_mode_480_272) {
+               mode = ov5640_mode_VGA_640_480;
+               pr_warn("Not support 480*272, change to 640*480\n");
+       }
+
        /* Configure ov5640 initial parm */
        pModeSetting = ov5640_mode_info_data[mode].init_data_ptr;
        ArySize = ov5640_mode_info_data[mode].init_data_size;
@@ -798,15 +940,27 @@ static int ov5640_config_others(enum ov5640_frame_rate rate,
        if (mode < ov5640_mode_MIN || mode > ov5640_mode_MAX)
                return -EINVAL;
 
-       pModeSetting = ov5640_pll_info_data[rate][mode].init_data_ptr;
-       ArySize = ov5640_pll_info_data[rate][mode].init_data_size;
+       if (ov5640_data.mipi_csi) {
+               mode = (mode == ov5640_mode_480_272) ?
+                               ov5640_mode_VGA_640_480 : mode;
+               pModeSetting = ov5640_mipi_pll_info_data[rate][mode].init_data_ptr;
+               ArySize = ov5640_mipi_pll_info_data[rate][mode].init_data_size;
+       } else {
+               pModeSetting = ov5640_pll_info_data[rate][mode].init_data_ptr;
+               ArySize = ov5640_pll_info_data[rate][mode].init_data_size;
+       }
        retval = ov5640_download_firmware(pModeSetting, ArySize);
        if (retval < 0)
                return retval;
 
        /* Configure ov5640 initial parm */
-       pModeSetting = ov5640_config;
-       ArySize = ARRAY_SIZE(ov5640_config);
+       if (ov5640_data.mipi_csi) {
+               pModeSetting = ov5640_mipi_config;
+               ArySize = ARRAY_SIZE(ov5640_mipi_config);
+       } else {
+               pModeSetting = ov5640_config;
+               ArySize = ARRAY_SIZE(ov5640_config);
+       }
 
        retval = ov5640_download_firmware(pModeSetting, ArySize);
        if (retval < 0)
@@ -1063,8 +1217,13 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
                return -EINVAL;
 
        if (!fmt) {
-               mf->code        = ov5640_colour_fmts[0].code;
-               mf->colorspace  = ov5640_colour_fmts[0].colorspace;
+               if (sensor->mipi_csi) {
+                       mf->code        = ov5640_colour_fmts[1].code;
+                       mf->colorspace  = ov5640_colour_fmts[1].colorspace;
+               } else {
+                       mf->code        = ov5640_colour_fmts[0].code;
+                       mf->colorspace  = ov5640_colour_fmts[0].colorspace;
+               }
        }
 
        mf->field       = V4L2_FIELD_NONE;
@@ -1089,11 +1248,19 @@ static int ov5640_get_fmt(struct v4l2_subdev *sd,
        if (format->pad)
                return -EINVAL;
 
-       mf->code = ov5640_colour_fmts[0].code;
+       memset(mf, 0, sizeof(struct v4l2_mbus_framefmt));
+
+       if (sensor->mipi_csi) {
+               mf->code = ov5640_colour_fmts[1].code;
+               mf->colorspace = ov5640_colour_fmts[1].colorspace;
+       } else {
+               mf->code = ov5640_colour_fmts[0].code;
+               mf->colorspace = ov5640_colour_fmts[0].colorspace;
+       }
        mf->width = sensor->pix.width;
        mf->height = sensor->pix.height;
-       mf->colorspace = ov5640_colour_fmts[0].colorspace;
        mf->field = V4L2_FIELD_NONE;
+       mf->reserved[1] = (sensor->mipi_csi) ? find_hs_configure(sensor) : 0;
 
        dev_dbg(&client->dev, "%s code=0x%x, w/h=(%d,%d), colorspace=%d, field=%d\n",
                __func__, mf->code, mf->width, mf->height, mf->colorspace, mf->field);
@@ -1288,6 +1455,8 @@ static int ov5640_probe(struct i2c_client *client,
                return retval;
        }
 
+       ov5640_data.mipi_csi  = of_property_read_bool(dev->of_node, "mipi_csi");
+
        /* Set mclk rate before clk on */
        ov5640_set_clk_rate();
 
@@ -1300,7 +1469,8 @@ static int ov5640_probe(struct i2c_client *client,
        ov5640_data.io_init = ov5640_reset;
        ov5640_data.i2c_client = client;
 
-       ov5640_data.pix.pixelformat = V4L2_PIX_FMT_YVYU;
+       ov5640_data.pix.pixelformat = (ov5640_data.mipi_csi) ?
+               V4L2_PIX_FMT_UYVY : V4L2_PIX_FMT_YVYU;
        ov5640_data.pix.width = ov5640_mode_info_data[0].width;
        ov5640_data.pix.height =  ov5640_mode_info_data[0].height;
        ov5640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY |