Commit 05fed816 authored by Libin Yang's avatar Libin Yang Committed by Mauro Carvalho Chehab

[media] marvell-ccic: add MIPI support for marvell-ccic driver

This patch adds the MIPI support for marvell-ccic.
Board driver should determine whether using MIPI or not.
Signed-off-by: default avatarAlbert Wang <twang13@marvell.com>
Signed-off-by: default avatarLibin Yang <lbyang@marvell.com>
Acked-by: default avatarJonathan Corbet <corbet@lwn.net>
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <m.chehab@samsung.com>
parent 187d42d6
...@@ -399,7 +399,7 @@ static void cafe_ctlr_init(struct mcam_camera *mcam) ...@@ -399,7 +399,7 @@ static void cafe_ctlr_init(struct mcam_camera *mcam)
} }
static void cafe_ctlr_power_up(struct mcam_camera *mcam) static int cafe_ctlr_power_up(struct mcam_camera *mcam)
{ {
/* /*
* Part one of the sensor dance: turn the global * Part one of the sensor dance: turn the global
...@@ -414,6 +414,8 @@ static void cafe_ctlr_power_up(struct mcam_camera *mcam) ...@@ -414,6 +414,8 @@ static void cafe_ctlr_power_up(struct mcam_camera *mcam)
*/ */
mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */ mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0); mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
return 0;
} }
static void cafe_ctlr_power_down(struct mcam_camera *mcam) static void cafe_ctlr_power_down(struct mcam_camera *mcam)
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/clk.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
#include <media/v4l2-device.h> #include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
...@@ -253,6 +254,45 @@ static void mcam_ctlr_stop(struct mcam_camera *cam) ...@@ -253,6 +254,45 @@ static void mcam_ctlr_stop(struct mcam_camera *cam)
mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE); mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
} }
static void mcam_enable_mipi(struct mcam_camera *mcam)
{
/* Using MIPI mode and enable MIPI */
cam_dbg(mcam, "camera: DPHY3=0x%x, DPHY5=0x%x, DPHY6=0x%x\n",
mcam->dphy[0], mcam->dphy[1], mcam->dphy[2]);
mcam_reg_write(mcam, REG_CSI2_DPHY3, mcam->dphy[0]);
mcam_reg_write(mcam, REG_CSI2_DPHY5, mcam->dphy[1]);
mcam_reg_write(mcam, REG_CSI2_DPHY6, mcam->dphy[2]);
if (!mcam->mipi_enabled) {
if (mcam->lane > 4 || mcam->lane <= 0) {
cam_warn(mcam, "lane number error\n");
mcam->lane = 1; /* set the default value */
}
/*
* 0x41 actives 1 lane
* 0x43 actives 2 lanes
* 0x45 actives 3 lanes (never happen)
* 0x47 actives 4 lanes
*/
mcam_reg_write(mcam, REG_CSI2_CTRL0,
CSI2_C0_MIPI_EN | CSI2_C0_ACT_LANE(mcam->lane));
mcam_reg_write(mcam, REG_CLKCTRL,
(mcam->mclk_src << 29) | mcam->mclk_div);
mcam->mipi_enabled = true;
}
}
static void mcam_disable_mipi(struct mcam_camera *mcam)
{
/* Using Parallel mode or disable MIPI */
mcam_reg_write(mcam, REG_CSI2_CTRL0, 0x0);
mcam_reg_write(mcam, REG_CSI2_DPHY3, 0x0);
mcam_reg_write(mcam, REG_CSI2_DPHY5, 0x0);
mcam_reg_write(mcam, REG_CSI2_DPHY6, 0x0);
mcam->mipi_enabled = false;
}
/* ------------------------------------------------------------------- */ /* ------------------------------------------------------------------- */
#ifdef MCAM_MODE_VMALLOC #ifdef MCAM_MODE_VMALLOC
...@@ -656,6 +696,13 @@ static void mcam_ctlr_image(struct mcam_camera *cam) ...@@ -656,6 +696,13 @@ static void mcam_ctlr_image(struct mcam_camera *cam)
*/ */
mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC, mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC,
C0_SIFM_MASK); C0_SIFM_MASK);
/*
* This field controls the generation of EOF(DVP only)
*/
if (cam->bus_type != V4L2_MBUS_CSI2)
mcam_reg_set_bit(cam, REG_CTRL0,
C0_EOF_VSYNC | C0_VEDGE_CTRL);
} }
...@@ -753,15 +800,21 @@ static void mcam_ctlr_stop_dma(struct mcam_camera *cam) ...@@ -753,15 +800,21 @@ static void mcam_ctlr_stop_dma(struct mcam_camera *cam)
/* /*
* Power up and down. * Power up and down.
*/ */
static void mcam_ctlr_power_up(struct mcam_camera *cam) static int mcam_ctlr_power_up(struct mcam_camera *cam)
{ {
unsigned long flags; unsigned long flags;
int ret;
spin_lock_irqsave(&cam->dev_lock, flags); spin_lock_irqsave(&cam->dev_lock, flags);
cam->plat_power_up(cam); ret = cam->plat_power_up(cam);
if (ret) {
spin_unlock_irqrestore(&cam->dev_lock, flags);
return ret;
}
mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
spin_unlock_irqrestore(&cam->dev_lock, flags); spin_unlock_irqrestore(&cam->dev_lock, flags);
msleep(5); /* Just to be sure */ msleep(5); /* Just to be sure */
return 0;
} }
static void mcam_ctlr_power_down(struct mcam_camera *cam) static void mcam_ctlr_power_down(struct mcam_camera *cam)
...@@ -869,6 +922,17 @@ static int mcam_read_setup(struct mcam_camera *cam) ...@@ -869,6 +922,17 @@ static int mcam_read_setup(struct mcam_camera *cam)
spin_lock_irqsave(&cam->dev_lock, flags); spin_lock_irqsave(&cam->dev_lock, flags);
clear_bit(CF_DMA_ACTIVE, &cam->flags); clear_bit(CF_DMA_ACTIVE, &cam->flags);
mcam_reset_buffers(cam); mcam_reset_buffers(cam);
/*
* Update CSI2_DPHY value
*/
if (cam->calc_dphy)
cam->calc_dphy(cam);
cam_dbg(cam, "camera: DPHY sets: dphy3=0x%x, dphy5=0x%x, dphy6=0x%x\n",
cam->dphy[0], cam->dphy[1], cam->dphy[2]);
if (cam->bus_type == V4L2_MBUS_CSI2)
mcam_enable_mipi(cam);
else
mcam_disable_mipi(cam);
mcam_ctlr_irq_enable(cam); mcam_ctlr_irq_enable(cam);
cam->state = S_STREAMING; cam->state = S_STREAMING;
if (!test_bit(CF_SG_RESTART, &cam->flags)) if (!test_bit(CF_SG_RESTART, &cam->flags))
...@@ -1475,7 +1539,9 @@ static int mcam_v4l_open(struct file *filp) ...@@ -1475,7 +1539,9 @@ static int mcam_v4l_open(struct file *filp)
ret = mcam_setup_vb2(cam); ret = mcam_setup_vb2(cam);
if (ret) if (ret)
goto out; goto out;
mcam_ctlr_power_up(cam); ret = mcam_ctlr_power_up(cam);
if (ret)
goto out;
__mcam_cam_reset(cam); __mcam_cam_reset(cam);
mcam_set_config_needed(cam, 1); mcam_set_config_needed(cam, 1);
} }
...@@ -1498,10 +1564,12 @@ static int mcam_v4l_release(struct file *filp) ...@@ -1498,10 +1564,12 @@ static int mcam_v4l_release(struct file *filp)
if (cam->users == 0) { if (cam->users == 0) {
mcam_ctlr_stop_dma(cam); mcam_ctlr_stop_dma(cam);
mcam_cleanup_vb2(cam); mcam_cleanup_vb2(cam);
mcam_disable_mipi(cam);
mcam_ctlr_power_down(cam); mcam_ctlr_power_down(cam);
if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read) if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read)
mcam_free_dma_bufs(cam); mcam_free_dma_bufs(cam);
} }
mutex_unlock(&cam->s_mutex); mutex_unlock(&cam->s_mutex);
return 0; return 0;
} }
...@@ -1787,7 +1855,11 @@ int mccic_resume(struct mcam_camera *cam) ...@@ -1787,7 +1855,11 @@ int mccic_resume(struct mcam_camera *cam)
mutex_lock(&cam->s_mutex); mutex_lock(&cam->s_mutex);
if (cam->users > 0) { if (cam->users > 0) {
mcam_ctlr_power_up(cam); ret = mcam_ctlr_power_up(cam);
if (ret) {
mutex_unlock(&cam->s_mutex);
return ret;
}
__mcam_cam_reset(cam); __mcam_cam_reset(cam);
} else { } else {
mcam_ctlr_power_down(cam); mcam_ctlr_power_down(cam);
......
...@@ -108,11 +108,28 @@ struct mcam_camera { ...@@ -108,11 +108,28 @@ struct mcam_camera {
short int clock_speed; /* Sensor clock speed, default 30 */ short int clock_speed; /* Sensor clock speed, default 30 */
short int use_smbus; /* SMBUS or straight I2c? */ short int use_smbus; /* SMBUS or straight I2c? */
enum mcam_buffer_mode buffer_mode; enum mcam_buffer_mode buffer_mode;
int mclk_min; /* The minimal value of mclk */
int mclk_src; /* which clock source the mclk derives from */
int mclk_div; /* Clock Divider Value for MCLK */
enum v4l2_mbus_type bus_type;
/* MIPI support */
/* The dphy config value, allocated in board file
* dphy[0]: DPHY3
* dphy[1]: DPHY5
* dphy[2]: DPHY6
*/
int *dphy;
bool mipi_enabled; /* flag whether mipi is enabled already */
int lane; /* lane number */
/* /*
* Callbacks from the core to the platform code. * Callbacks from the core to the platform code.
*/ */
void (*plat_power_up) (struct mcam_camera *cam); int (*plat_power_up) (struct mcam_camera *cam);
void (*plat_power_down) (struct mcam_camera *cam); void (*plat_power_down) (struct mcam_camera *cam);
void (*calc_dphy) (struct mcam_camera *cam);
/* /*
* Everything below here is private to the mcam core and * Everything below here is private to the mcam core and
...@@ -225,6 +242,17 @@ int mccic_resume(struct mcam_camera *cam); ...@@ -225,6 +242,17 @@ int mccic_resume(struct mcam_camera *cam);
#define REG_Y0BAR 0x00 #define REG_Y0BAR 0x00
#define REG_Y1BAR 0x04 #define REG_Y1BAR 0x04
#define REG_Y2BAR 0x08 #define REG_Y2BAR 0x08
/*
* register definitions for MIPI support
*/
#define REG_CSI2_CTRL0 0x100
#define CSI2_C0_MIPI_EN (0x1 << 0)
#define CSI2_C0_ACT_LANE(n) ((n-1) << 1)
#define REG_CSI2_DPHY3 0x12c
#define REG_CSI2_DPHY5 0x134
#define REG_CSI2_DPHY6 0x138
/* ... */ /* ... */
#define REG_IMGPITCH 0x24 /* Image pitch register */ #define REG_IMGPITCH 0x24 /* Image pitch register */
...@@ -293,13 +321,16 @@ int mccic_resume(struct mcam_camera *cam); ...@@ -293,13 +321,16 @@ int mccic_resume(struct mcam_camera *cam);
#define C0_YUVE_XUVY 0x00020000 /* 420: .UVY */ #define C0_YUVE_XUVY 0x00020000 /* 420: .UVY */
#define C0_YUVE_XVUY 0x00030000 /* 420: .VUY */ #define C0_YUVE_XVUY 0x00030000 /* 420: .VUY */
/* Bayer bits 18,19 if needed */ /* Bayer bits 18,19 if needed */
#define C0_EOF_VSYNC 0x00400000 /* Generate EOF by VSYNC */
#define C0_VEDGE_CTRL 0x00800000 /* Detect falling edge of VSYNC */
#define C0_HPOL_LOW 0x01000000 /* HSYNC polarity active low */ #define C0_HPOL_LOW 0x01000000 /* HSYNC polarity active low */
#define C0_VPOL_LOW 0x02000000 /* VSYNC polarity active low */ #define C0_VPOL_LOW 0x02000000 /* VSYNC polarity active low */
#define C0_VCLK_LOW 0x04000000 /* VCLK on falling edge */ #define C0_VCLK_LOW 0x04000000 /* VCLK on falling edge */
#define C0_DOWNSCALE 0x08000000 /* Enable downscaler */ #define C0_DOWNSCALE 0x08000000 /* Enable downscaler */
#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */ /* SIFMODE */
#define C0_SIF_HVSYNC 0x00000000 /* Use H/VSYNC */ #define C0_SIF_HVSYNC 0x00000000 /* Use H/VSYNC */
#define CO_SOF_NOSYNC 0x40000000 /* Use inband active signaling */ #define C0_SOF_NOSYNC 0x40000000 /* Use inband active signaling */
#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */
/* Bits below C1_444ALPHA are not present in Cafe */ /* Bits below C1_444ALPHA are not present in Cafe */
#define REG_CTRL1 0x40 /* Control 1 */ #define REG_CTRL1 0x40 /* Control 1 */
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/clk.h>
#include "mcam-core.h" #include "mcam-core.h"
...@@ -38,6 +39,7 @@ struct mmp_camera { ...@@ -38,6 +39,7 @@ struct mmp_camera {
struct platform_device *pdev; struct platform_device *pdev;
struct mcam_camera mcam; struct mcam_camera mcam;
struct list_head devlist; struct list_head devlist;
struct clk *mipi_clk;
int irq; int irq;
}; };
...@@ -112,10 +114,17 @@ static void mmpcam_power_up_ctlr(struct mmp_camera *cam) ...@@ -112,10 +114,17 @@ static void mmpcam_power_up_ctlr(struct mmp_camera *cam)
mdelay(1); mdelay(1);
} }
static void mmpcam_power_up(struct mcam_camera *mcam) static int mmpcam_power_up(struct mcam_camera *mcam)
{ {
struct mmp_camera *cam = mcam_to_cam(mcam); struct mmp_camera *cam = mcam_to_cam(mcam);
struct mmp_camera_platform_data *pdata; struct mmp_camera_platform_data *pdata;
if (mcam->bus_type == V4L2_MBUS_CSI2) {
cam->mipi_clk = devm_clk_get(mcam->dev, "mipi");
if ((IS_ERR(cam->mipi_clk) && mcam->dphy[2] == 0))
return PTR_ERR(cam->mipi_clk);
}
/* /*
* Turn on power and clocks to the controller. * Turn on power and clocks to the controller.
*/ */
...@@ -132,6 +141,7 @@ static void mmpcam_power_up(struct mcam_camera *mcam) ...@@ -132,6 +141,7 @@ static void mmpcam_power_up(struct mcam_camera *mcam)
mdelay(5); mdelay(5);
gpio_set_value(pdata->sensor_reset_gpio, 1); /* reset is active low */ gpio_set_value(pdata->sensor_reset_gpio, 1); /* reset is active low */
mdelay(5); mdelay(5);
return 0;
} }
static void mmpcam_power_down(struct mcam_camera *mcam) static void mmpcam_power_down(struct mcam_camera *mcam)
...@@ -149,8 +159,109 @@ static void mmpcam_power_down(struct mcam_camera *mcam) ...@@ -149,8 +159,109 @@ static void mmpcam_power_down(struct mcam_camera *mcam)
pdata = cam->pdev->dev.platform_data; pdata = cam->pdev->dev.platform_data;
gpio_set_value(pdata->sensor_power_gpio, 0); gpio_set_value(pdata->sensor_power_gpio, 0);
gpio_set_value(pdata->sensor_reset_gpio, 0); gpio_set_value(pdata->sensor_reset_gpio, 0);
if (mcam->bus_type == V4L2_MBUS_CSI2 && !IS_ERR(cam->mipi_clk)) {
if (cam->mipi_clk)
devm_clk_put(mcam->dev, cam->mipi_clk);
cam->mipi_clk = NULL;
}
} }
/*
* calc the dphy register values
* There are three dphy registers being used.
* dphy[0] - CSI2_DPHY3
* dphy[1] - CSI2_DPHY5
* dphy[2] - CSI2_DPHY6
* CSI2_DPHY3 and CSI2_DPHY6 can be set with a default value
* or be calculated dynamically
*/
void mmpcam_calc_dphy(struct mcam_camera *mcam)
{
struct mmp_camera *cam = mcam_to_cam(mcam);
struct mmp_camera_platform_data *pdata = cam->pdev->dev.platform_data;
struct device *dev = &cam->pdev->dev;
unsigned long tx_clk_esc;
/*
* If CSI2_DPHY3 is calculated dynamically,
* pdata->lane_clk should be already set
* either in the board driver statically
* or in the sensor driver dynamically.
*/
/*
* dphy[0] - CSI2_DPHY3:
* bit 0 ~ bit 7: HS Term Enable.
* defines the time that the DPHY
* wait before enabling the data
* lane termination after detecting
* that the sensor has driven the data
* lanes to the LP00 bridge state.
* The value is calculated by:
* (Max T(D_TERM_EN)/Period(DDR)) - 1
* bit 8 ~ bit 15: HS_SETTLE
* Time interval during which the HS
* receiver shall ignore any Data Lane
* HS transistions.
* The vaule has been calibrated on
* different boards. It seems to work well.
*
* More detail please refer
* MIPI Alliance Spectification for D-PHY
* document for explanation of HS-SETTLE
* and D-TERM-EN.
*/
switch (pdata->dphy3_algo) {
case DPHY3_ALGO_PXA910:
/*
* Calculate CSI2_DPHY3 algo for PXA910
*/
pdata->dphy[0] =
(((1 + (pdata->lane_clk * 80) / 1000) & 0xff) << 8)
| (1 + pdata->lane_clk * 35 / 1000);
break;
case DPHY3_ALGO_PXA2128:
/*
* Calculate CSI2_DPHY3 algo for PXA2128
*/
pdata->dphy[0] =
(((2 + (pdata->lane_clk * 110) / 1000) & 0xff) << 8)
| (1 + pdata->lane_clk * 35 / 1000);
break;
default:
/*
* Use default CSI2_DPHY3 value for PXA688/PXA988
*/
dev_dbg(dev, "camera: use the default CSI2_DPHY3 value\n");
}
/*
* mipi_clk will never be changed, it is a fixed value on MMP
*/
if (IS_ERR(cam->mipi_clk))
return;
/* get the escape clk, this is hard coded */
tx_clk_esc = (clk_get_rate(cam->mipi_clk) / 1000000) / 12;
/*
* dphy[2] - CSI2_DPHY6:
* bit 0 ~ bit 7: CK Term Enable
* Time for the Clock Lane receiver to enable the HS line
* termination. The value is calculated similarly with
* HS Term Enable
* bit 8 ~ bit 15: CK Settle
* Time interval during which the HS receiver shall ignore
* any Clock Lane HS transitions.
* The value is calibrated on the boards.
*/
pdata->dphy[2] =
((((534 * tx_clk_esc) / 2000 - 1) & 0xff) << 8)
| (((38 * tx_clk_esc) / 1000 - 1) & 0xff);
dev_dbg(dev, "camera: DPHY sets: dphy3=0x%x, dphy5=0x%x, dphy6=0x%x\n",
pdata->dphy[0], pdata->dphy[1], pdata->dphy[2]);
}
static irqreturn_t mmpcam_irq(int irq, void *data) static irqreturn_t mmpcam_irq(int irq, void *data)
{ {
...@@ -173,17 +284,30 @@ static int mmpcam_probe(struct platform_device *pdev) ...@@ -173,17 +284,30 @@ static int mmpcam_probe(struct platform_device *pdev)
struct mmp_camera_platform_data *pdata; struct mmp_camera_platform_data *pdata;
int ret; int ret;
pdata = pdev->dev.platform_data;
if (!pdata)
return -ENODEV;
cam = kzalloc(sizeof(*cam), GFP_KERNEL); cam = kzalloc(sizeof(*cam), GFP_KERNEL);
if (cam == NULL) if (cam == NULL)
return -ENOMEM; return -ENOMEM;
cam->pdev = pdev; cam->pdev = pdev;
cam->mipi_clk = NULL;
INIT_LIST_HEAD(&cam->devlist); INIT_LIST_HEAD(&cam->devlist);
mcam = &cam->mcam; mcam = &cam->mcam;
mcam->plat_power_up = mmpcam_power_up; mcam->plat_power_up = mmpcam_power_up;
mcam->plat_power_down = mmpcam_power_down; mcam->plat_power_down = mmpcam_power_down;
mcam->calc_dphy = mmpcam_calc_dphy;
mcam->dev = &pdev->dev; mcam->dev = &pdev->dev;
mcam->use_smbus = 0; mcam->use_smbus = 0;
mcam->mclk_min = pdata->mclk_min;
mcam->mclk_src = pdata->mclk_src;
mcam->mclk_div = pdata->mclk_div;
mcam->bus_type = pdata->bus_type;
mcam->dphy = pdata->dphy;
mcam->mipi_enabled = false;
mcam->lane = pdata->lane;
mcam->chip_id = MCAM_ARMADA610; mcam->chip_id = MCAM_ARMADA610;
mcam->buffer_mode = B_DMA_sg; mcam->buffer_mode = B_DMA_sg;
spin_lock_init(&mcam->dev_lock); spin_lock_init(&mcam->dev_lock);
...@@ -223,7 +347,6 @@ static int mmpcam_probe(struct platform_device *pdev) ...@@ -223,7 +347,6 @@ static int mmpcam_probe(struct platform_device *pdev)
* Find the i2c adapter. This assumes, of course, that the * Find the i2c adapter. This assumes, of course, that the
* i2c bus is already up and functioning. * i2c bus is already up and functioning.
*/ */
pdata = pdev->dev.platform_data;
mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device); mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device);
if (mcam->i2c_adapter == NULL) { if (mcam->i2c_adapter == NULL) {
ret = -ENODEV; ret = -ENODEV;
...@@ -250,10 +373,12 @@ static int mmpcam_probe(struct platform_device *pdev) ...@@ -250,10 +373,12 @@ static int mmpcam_probe(struct platform_device *pdev)
/* /*
* Power the device up and hand it off to the core. * Power the device up and hand it off to the core.
*/ */
mmpcam_power_up(mcam); ret = mmpcam_power_up(mcam);
ret = mccic_register(mcam);
if (ret) if (ret)
goto out_gpio2; goto out_gpio2;
ret = mccic_register(mcam);
if (ret)
goto out_pwdn;
/* /*
* Finally, set up our IRQ now that the core is ready to * Finally, set up our IRQ now that the core is ready to
* deal with it. * deal with it.
...@@ -273,8 +398,9 @@ static int mmpcam_probe(struct platform_device *pdev) ...@@ -273,8 +398,9 @@ static int mmpcam_probe(struct platform_device *pdev)
out_unregister: out_unregister:
mccic_shutdown(mcam); mccic_shutdown(mcam);
out_gpio2: out_pwdn:
mmpcam_power_down(mcam); mmpcam_power_down(mcam);
out_gpio2:
gpio_free(pdata->sensor_reset_gpio); gpio_free(pdata->sensor_reset_gpio);
out_gpio: out_gpio:
gpio_free(pdata->sensor_power_gpio); gpio_free(pdata->sensor_power_gpio);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment