[1/7] marvell-ccic: add MIPI support for marvell-ccic driver

Message ID 1370324380.26072.19.camel@younglee-desktop (mailing list archive)
State Superseded, archived
Delegated to: Hans Verkuil
Headers

Commit Message

Libin Yang June 4, 2013, 5:39 a.m. UTC
  From: Libin Yang <lbyang@marvell.com>

This patch adds the MIPI support for marvell-ccic.
Board driver should determine whether using MIPI or not.

Signed-off-by: Albert Wang <twang13@marvell.com>
Signed-off-by: Libin Yang <lbyang@marvell.com>
---
 drivers/media/platform/marvell-ccic/cafe-driver.c |    4 +-
 drivers/media/platform/marvell-ccic/mcam-core.c   |   75 +++++++++++-
 drivers/media/platform/marvell-ccic/mcam-core.h   |   32 +++++-
 drivers/media/platform/marvell-ccic/mmp-driver.c  |  126 ++++++++++++++++++++-
 include/media/mmp-camera.h                        |   19 ++++
 5 files changed, 245 insertions(+), 11 deletions(-)
  

Comments

Jonathan Corbet June 21, 2013, 4:59 p.m. UTC | #1
Better late than never, I hope...in response to Hans's poke, I'm going
to try to do a quick review.  I am allegedly in vacation, so this may
not be as thorough as we might like...

On Tue, 4 Jun 2013 13:39:40 +0800
lbyang <lbyang@marvell.com> wrote:

> From: Libin Yang <lbyang@marvell.com>
> 
> This patch adds the MIPI support for marvell-ccic.
> Board driver should determine whether using MIPI or not.
> 
> Signed-off-by: Albert Wang <twang13@marvell.com>
> Signed-off-by: Libin Yang <lbyang@marvell.com>
> ---
>  drivers/media/platform/marvell-ccic/cafe-driver.c |    4 +-
>  drivers/media/platform/marvell-ccic/mcam-core.c   |   75 +++++++++++-
>  drivers/media/platform/marvell-ccic/mcam-core.h   |   32 +++++-
>  drivers/media/platform/marvell-ccic/mmp-driver.c  |  126 ++++++++++++++++++++-
>  include/media/mmp-camera.h                        |   19 ++++
>  5 files changed, 245 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c
> index d030f9b..68e82fb 100644
> --- a/drivers/media/platform/marvell-ccic/cafe-driver.c
> +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c
> @@ -400,7 +400,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
> @@ -415,6 +415,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|GPR_C0);
> +
> +	return 0;
>  }

Curious: why add the return value when it never changes?  Do I assume that
some future patch adds some complexity here?  Not opposed to this, but it
seems like the wrong time.

>  static void cafe_ctlr_power_down(struct mcam_camera *mcam)
> diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
> index 64ab91e..bb3de1f 100644
> --- a/drivers/media/platform/marvell-ccic/mcam-core.c
> +++ b/drivers/media/platform/marvell-ccic/mcam-core.c
> @@ -19,6 +19,7 @@
>  #include <linux/delay.h>
>  #include <linux/vmalloc.h>
>  #include <linux/io.h>
> +#include <linux/clk.h>
>  #include <linux/videodev2.h>
>  #include <media/v4l2-device.h>
>  #include <media/v4l2-ioctl.h>
> @@ -254,6 +255,45 @@ static void mcam_ctlr_stop(struct mcam_camera *cam)
>  	mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
>  }
>  
> +static int mcam_config_mipi(struct mcam_camera *mcam, bool enable)
> +{
> +	if (enable) {
> +		/* 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;
> +		}
> +	} else {
> +		/* 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;
> +	}
> +	return 0;
> +}

I think I said this before...having separate enable/disable functions seems
better to me than a multiplexed function with an overall flag.  Is there a
reason it's done this way?

[...]
>  /*
>   * 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;
> +	int ret;
>  
>  	spin_lock_irqsave(&cam->dev_lock, flags);
> -	cam->plat_power_up(cam);
> +	ret = cam->plat_power_up(cam);
> +	if (ret)
> +		return ret;

You just returned with the lock held - that's a big mistake.

>  	mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
>  	spin_unlock_irqrestore(&cam->dev_lock, flags);
>  	msleep(5); /* Just to be sure */
> +	return 0;
>  }
>  
>  static void mcam_ctlr_power_down(struct mcam_camera *cam)
> @@ -887,6 +938,16 @@ static int mcam_read_setup(struct mcam_camera *cam)
>  	spin_lock_irqsave(&cam->dev_lock, flags);
>  	clear_bit(CF_DMA_ACTIVE, &cam->flags);
>  	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]);
> +	ret = mcam_config_mipi(cam, cam->bus_type == V4L2_MBUS_CSI2);
> +	if (ret < 0)
> +		return ret;

Once again - you're holding a spinlock here.

[...]

> @@ -1816,7 +1881,9 @@ int mccic_resume(struct mcam_camera *cam)
>  
>  	mutex_lock(&cam->s_mutex);
>  	if (cam->users > 0) {
> -		mcam_ctlr_power_up(cam);
> +		ret = mcam_ctlr_power_up(cam);
> +		if (ret)
> +			return ret;

...and here you're holding a mutex.  There's a reason so much kernel code
uses the "goto out" pattern; you really have to be careful about returning
from the middle of a function.

>  		__mcam_cam_reset(cam);
>  	} else {
>  		mcam_ctlr_power_down(cam);
> diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h
> index 01dec9e..be271b3 100644
> --- a/drivers/media/platform/marvell-ccic/mcam-core.h
> +++ b/drivers/media/platform/marvell-ccic/mcam-core.h
> @@ -102,11 +102,23 @@ struct mcam_camera {
>  	short int clock_speed;	/* Sensor clock speed, default 30 */
>  	short int use_smbus;	/* SMBUS or straight I2c? */
>  	enum mcam_buffer_mode buffer_mode;
> +
> +	int mclk_min;
> +	int mclk_src;
> +	int mclk_div;
> +
> +	enum v4l2_mbus_type bus_type;
> +	/* MIPI support */
> +	int *dphy;
> +	bool mipi_enabled;
> +	int lane;			/* lane number */

Can we document these new fields a bit better?

>  	/*
>  	 * 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 (*calc_dphy) (struct mcam_camera *cam);
>  
>  	/*
>  	 * Everything below here is private to the mcam core and
> @@ -220,6 +232,17 @@ int mccic_resume(struct mcam_camera *cam);
>  #define REG_Y0BAR	0x00
>  #define REG_Y1BAR	0x04
>  #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 */
> @@ -288,13 +311,16 @@ int mccic_resume(struct mcam_camera *cam);
>  #define	  C0_YUVE_XUVY	  0x00020000	/* 420: .UVY		*/
>  #define	  C0_YUVE_XVUY	  0x00030000	/* 420: .VUY		*/
>  /* 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_VPOL_LOW	  0x02000000	/* VSYNC polarity active low */
>  #define	  C0_VCLK_LOW	  0x04000000	/* VCLK on falling edge */
>  #define	  C0_DOWNSCALE	  0x08000000	/* Enable downscaler */
> -#define	  C0_SIFM_MASK	  0xc0000000	/* SIF mode bits */
> +/* SIFMODE */

What's this change for?

> diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c
> index c4c17fe..3dad182 100644
> --- a/drivers/media/platform/marvell-ccic/mmp-driver.c
> +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c
[...]
> +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);

There's enough operators here that some parentheses would really help to
make the code more readable.  Lots of places in this file.

[...]
>  static irqreturn_t mmpcam_irq(int irq, void *data)
>  {
> @@ -174,17 +280,30 @@ static int mmpcam_probe(struct platform_device *pdev)
>  	struct mmp_camera_platform_data *pdata;
>  	int ret;
>  
> +	pdata = pdev->dev.platform_data;
> +	if (!pdata)
> +		return -ENODEV;
> +
>  	cam = kzalloc(sizeof(*cam), GFP_KERNEL);
>  	if (cam == NULL)
>  		return -ENOMEM;
>  	cam->pdev = pdev;
> +	cam->mipi_clk = ERR_PTR(-EINVAL);

The use of ERR_PTR here (and in related places) seems a bit weird; is there
a reason you can't just use NULL?

In summary, I'm not really familiar with the MIPI interface, and I don't
have any hardware with it, so I'll have to take your word that the code
works.  I've pointed out a bunch of nits that are worth fixing.  The
locking mistakes are fatal, though, and need attention.  They should be
quick to fix, though; this code should be ready to merge in short order.

Thanks,

jon
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
  
Libin Yang June 25, 2013, 7:21 a.m. UTC | #2
Hi Jonathan,

Sorry for delay reply. Please see the below comments.

Regards,
Libin  

>-----Original Message-----
>From: Jonathan Corbet [mailto:corbet@lwn.net] 
>Sent: Saturday, June 22, 2013 1:00 AM
>To: Libin Yang
>Cc: g.liakhovetski@gmx.de; mchehab@redhat.com; 
>linux-media@vger.kernel.org; albert.v.wang@gmail.com
>Subject: Re: [PATCH 1/7] marvell-ccic: add MIPI support for 
>marvell-ccic driver
>
>Better late than never, I hope...in response to Hans's poke, 
>I'm going to try to do a quick review.  I am allegedly in 
>vacation, so this may not be as thorough as we might like...
>
>On Tue, 4 Jun 2013 13:39:40 +0800
>lbyang <lbyang@marvell.com> wrote:
>
>> From: Libin Yang <lbyang@marvell.com>
>> 
>> This patch adds the MIPI support for marvell-ccic.
>> Board driver should determine whether using MIPI or not.
>> 
>> Signed-off-by: Albert Wang <twang13@marvell.com>
>> Signed-off-by: Libin Yang <lbyang@marvell.com>
>> ---
>>  drivers/media/platform/marvell-ccic/cafe-driver.c |    4 +-
>>  drivers/media/platform/marvell-ccic/mcam-core.c   |   75 
>+++++++++++-
>>  drivers/media/platform/marvell-ccic/mcam-core.h   |   32 +++++-
>>  drivers/media/platform/marvell-ccic/mmp-driver.c  |  126 
>++++++++++++++++++++-
>>  include/media/mmp-camera.h                        |   19 ++++
>>  5 files changed, 245 insertions(+), 11 deletions(-)
>> 
>> diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c 
>> b/drivers/media/platform/marvell-ccic/cafe-driver.c
>> index d030f9b..68e82fb 100644
>> --- a/drivers/media/platform/marvell-ccic/cafe-driver.c
>> +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c
>> @@ -400,7 +400,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 @@ 
>-415,6 +415,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|GPR_C0);
>> +
>> +	return 0;
>>  }
>
>Curious: why add the return value when it never changes?  Do I 
>assume that some future patch adds some complexity here?  Not 
>opposed to this, but it seems like the wrong time.

[Libin] This function will be set to mcam->plat_power_up eventually. The callback plat_power_up is changed to return int type because of mmp-driver.c

>
>>  static void cafe_ctlr_power_down(struct mcam_camera *mcam) 
>diff --git 
>> a/drivers/media/platform/marvell-ccic/mcam-core.c 
>> b/drivers/media/platform/marvell-ccic/mcam-core.c
>> index 64ab91e..bb3de1f 100644
>> --- a/drivers/media/platform/marvell-ccic/mcam-core.c
>> +++ b/drivers/media/platform/marvell-ccic/mcam-core.c
>> @@ -19,6 +19,7 @@
>>  #include <linux/delay.h>
>>  #include <linux/vmalloc.h>
>>  #include <linux/io.h>
>> +#include <linux/clk.h>
>>  #include <linux/videodev2.h>
>>  #include <media/v4l2-device.h>
>>  #include <media/v4l2-ioctl.h>
>> @@ -254,6 +255,45 @@ static void mcam_ctlr_stop(struct 
>mcam_camera *cam)
>>  	mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);  }
>>  
>> +static int mcam_config_mipi(struct mcam_camera *mcam, bool enable) {
>> +	if (enable) {
>> +		/* 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;
>> +		}
>> +	} else {
>> +		/* 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;
>> +	}
>> +	return 0;
>> +}
>
>I think I said this before...having separate enable/disable 
>functions seems better to me than a multiplexed function with 
>an overall flag.  Is there a reason it's done this way?

[LIbin] OK, I will split it into 2 functions.

>
>[...]
>>  /*
>>   * 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;
>> +	int ret;
>>  
>>  	spin_lock_irqsave(&cam->dev_lock, flags);
>> -	cam->plat_power_up(cam);
>> +	ret = cam->plat_power_up(cam);
>> +	if (ret)
>> +		return ret;
>
>You just returned with the lock held - that's a big mistake.

[LIbin] Yes. I will correct it. Thanks for point it out.

>
>>  	mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
>>  	spin_unlock_irqrestore(&cam->dev_lock, flags);
>>  	msleep(5); /* Just to be sure */
>> +	return 0;
>>  }
>>  
>>  static void mcam_ctlr_power_down(struct mcam_camera *cam) @@ -887,6 
>> +938,16 @@ static int mcam_read_setup(struct mcam_camera *cam)
>>  	spin_lock_irqsave(&cam->dev_lock, flags);
>>  	clear_bit(CF_DMA_ACTIVE, &cam->flags);
>>  	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]);
>> +	ret = mcam_config_mipi(cam, cam->bus_type == V4L2_MBUS_CSI2);
>> +	if (ret < 0)
>> +		return ret;
>
>Once again - you're holding a spinlock here.

[Libin] Yes.

>
>[...]
>
>> @@ -1816,7 +1881,9 @@ int mccic_resume(struct mcam_camera *cam)
>>  
>>  	mutex_lock(&cam->s_mutex);
>>  	if (cam->users > 0) {
>> -		mcam_ctlr_power_up(cam);
>> +		ret = mcam_ctlr_power_up(cam);
>> +		if (ret)
>> +			return ret;
>
>...and here you're holding a mutex.  There's a reason so much 
>kernel code uses the "goto out" pattern; you really have to be 
>careful about returning from the middle of a function.

[Libin] Yes. I will correct it.

>
>>  		__mcam_cam_reset(cam);
>>  	} else {
>>  		mcam_ctlr_power_down(cam);
>> diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h 
>> b/drivers/media/platform/marvell-ccic/mcam-core.h
>> index 01dec9e..be271b3 100644
>> --- a/drivers/media/platform/marvell-ccic/mcam-core.h
>> +++ b/drivers/media/platform/marvell-ccic/mcam-core.h
>> @@ -102,11 +102,23 @@ struct mcam_camera {
>>  	short int clock_speed;	/* Sensor clock speed, default 30 */
>>  	short int use_smbus;	/* SMBUS or straight I2c? */
>>  	enum mcam_buffer_mode buffer_mode;
>> +
>> +	int mclk_min;
>> +	int mclk_src;
>> +	int mclk_div;
>> +
>> +	enum v4l2_mbus_type bus_type;
>> +	/* MIPI support */
>> +	int *dphy;
>> +	bool mipi_enabled;
>> +	int lane;			/* lane number */
>
>Can we document these new fields a bit better?

[Libin] OK.

>
>>  	/*
>>  	 * 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 (*calc_dphy) (struct mcam_camera *cam);
>>  
>>  	/*
>>  	 * Everything below here is private to the mcam core 
>and @@ -220,6 
>> +232,17 @@ int mccic_resume(struct mcam_camera *cam);
>>  #define REG_Y0BAR	0x00
>>  #define REG_Y1BAR	0x04
>>  #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 */
>> @@ -288,13 +311,16 @@ int mccic_resume(struct mcam_camera *cam);
>>  #define	  C0_YUVE_XUVY	  0x00020000	/* 420: .UVY	
>	*/
>>  #define	  C0_YUVE_XVUY	  0x00030000	/* 420: .VUY	
>	*/
>>  /* 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_VPOL_LOW	  0x02000000	/* VSYNC 
>polarity active low */
>>  #define	  C0_VCLK_LOW	  0x04000000	/* VCLK on 
>falling edge */
>>  #define	  C0_DOWNSCALE	  0x08000000	/* Enable downscaler */
>> -#define	  C0_SIFM_MASK	  0xc0000000	/* SIF mode bits */
>> +/* SIFMODE */
>
>What's this change for?
>

[LIbin] Mostly, the code is to fix the typo: CO_SOF_NOSYNC -> C0_SOF_NOSYNC.
And it also re-sorts the code by the value to improve the readability.

>> diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c 
>> b/drivers/media/platform/marvell-ccic/mmp-driver.c
>> index c4c17fe..3dad182 100644
>> --- a/drivers/media/platform/marvell-ccic/mmp-driver.c
>> +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c
>[...]
>> +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);
>
>There's enough operators here that some parentheses would 
>really help to make the code more readable.  Lots of places in 
>this file.

[Libin] OK. I will add some parentheses to help it readable.

>
>[...]
>>  static irqreturn_t mmpcam_irq(int irq, void *data)  { @@ -174,17 
>> +280,30 @@ static int mmpcam_probe(struct platform_device *pdev)
>>  	struct mmp_camera_platform_data *pdata;
>>  	int ret;
>>  
>> +	pdata = pdev->dev.platform_data;
>> +	if (!pdata)
>> +		return -ENODEV;
>> +
>>  	cam = kzalloc(sizeof(*cam), GFP_KERNEL);
>>  	if (cam == NULL)
>>  		return -ENOMEM;
>>  	cam->pdev = pdev;
>> +	cam->mipi_clk = ERR_PTR(-EINVAL);
>
>The use of ERR_PTR here (and in related places) seems a bit 
>weird; is there a reason you can't just use NULL?

[Libin] OK, I will use NULL in next version.

>
>In summary, I'm not really familiar with the MIPI interface, 
>and I don't have any hardware with it, so I'll have to take 
>your word that the code works.  I've pointed out a bunch of 
>nits that are worth fixing.  The locking mistakes are fatal, 
>though, and need attention.  They should be quick to fix, 
>though; this code should be ready to merge in short order.
>
>Thanks,
>
>jon
>--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
  

Patch

diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c
index d030f9b..68e82fb 100644
--- a/drivers/media/platform/marvell-ccic/cafe-driver.c
+++ b/drivers/media/platform/marvell-ccic/cafe-driver.c
@@ -400,7 +400,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
@@ -415,6 +415,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|GPR_C0);
+
+	return 0;
 }
 
 static void cafe_ctlr_power_down(struct mcam_camera *mcam)
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index 64ab91e..bb3de1f 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -19,6 +19,7 @@ 
 #include <linux/delay.h>
 #include <linux/vmalloc.h>
 #include <linux/io.h>
+#include <linux/clk.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
@@ -254,6 +255,45 @@  static void mcam_ctlr_stop(struct mcam_camera *cam)
 	mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
 }
 
+static int mcam_config_mipi(struct mcam_camera *mcam, bool enable)
+{
+	if (enable) {
+		/* 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;
+		}
+	} else {
+		/* 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;
+	}
+	return 0;
+}
+
 /* ------------------------------------------------------------------- */
 
 #ifdef MCAM_MODE_VMALLOC
@@ -657,6 +697,13 @@  static void mcam_ctlr_image(struct mcam_camera *cam)
 	 */
 	mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC,
 			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);
 }
 
 
@@ -754,15 +801,19 @@  static void mcam_ctlr_stop_dma(struct mcam_camera *cam)
 /*
  * 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;
+	int ret;
 
 	spin_lock_irqsave(&cam->dev_lock, flags);
-	cam->plat_power_up(cam);
+	ret = cam->plat_power_up(cam);
+	if (ret)
+		return ret;
 	mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
 	spin_unlock_irqrestore(&cam->dev_lock, flags);
 	msleep(5); /* Just to be sure */
+	return 0;
 }
 
 static void mcam_ctlr_power_down(struct mcam_camera *cam)
@@ -887,6 +938,16 @@  static int mcam_read_setup(struct mcam_camera *cam)
 	spin_lock_irqsave(&cam->dev_lock, flags);
 	clear_bit(CF_DMA_ACTIVE, &cam->flags);
 	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]);
+	ret = mcam_config_mipi(cam, cam->bus_type == V4L2_MBUS_CSI2);
+	if (ret < 0)
+		return ret;
 	mcam_ctlr_irq_enable(cam);
 	cam->state = S_STREAMING;
 	if (!test_bit(CF_SG_RESTART, &cam->flags))
@@ -1503,7 +1564,9 @@  static int mcam_v4l_open(struct file *filp)
 		ret = mcam_setup_vb2(cam);
 		if (ret)
 			goto out;
-		mcam_ctlr_power_up(cam);
+		ret = mcam_ctlr_power_up(cam);
+		if (ret)
+			return ret;
 		__mcam_cam_reset(cam);
 		mcam_set_config_needed(cam, 1);
 	}
@@ -1526,10 +1589,12 @@  static int mcam_v4l_release(struct file *filp)
 	if (cam->users == 0) {
 		mcam_ctlr_stop_dma(cam);
 		mcam_cleanup_vb2(cam);
+		mcam_config_mipi(cam, false);
 		mcam_ctlr_power_down(cam);
 		if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read)
 			mcam_free_dma_bufs(cam);
 	}
+
 	mutex_unlock(&cam->s_mutex);
 	return 0;
 }
@@ -1816,7 +1881,9 @@  int mccic_resume(struct mcam_camera *cam)
 
 	mutex_lock(&cam->s_mutex);
 	if (cam->users > 0) {
-		mcam_ctlr_power_up(cam);
+		ret = mcam_ctlr_power_up(cam);
+		if (ret)
+			return ret;
 		__mcam_cam_reset(cam);
 	} else {
 		mcam_ctlr_power_down(cam);
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h
index 01dec9e..be271b3 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.h
+++ b/drivers/media/platform/marvell-ccic/mcam-core.h
@@ -102,11 +102,23 @@  struct mcam_camera {
 	short int clock_speed;	/* Sensor clock speed, default 30 */
 	short int use_smbus;	/* SMBUS or straight I2c? */
 	enum mcam_buffer_mode buffer_mode;
+
+	int mclk_min;
+	int mclk_src;
+	int mclk_div;
+
+	enum v4l2_mbus_type bus_type;
+	/* MIPI support */
+	int *dphy;
+	bool mipi_enabled;
+	int lane;			/* lane number */
+
 	/*
 	 * 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 (*calc_dphy) (struct mcam_camera *cam);
 
 	/*
 	 * Everything below here is private to the mcam core and
@@ -220,6 +232,17 @@  int mccic_resume(struct mcam_camera *cam);
 #define REG_Y0BAR	0x00
 #define REG_Y1BAR	0x04
 #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 */
@@ -288,13 +311,16 @@  int mccic_resume(struct mcam_camera *cam);
 #define	  C0_YUVE_XUVY	  0x00020000	/* 420: .UVY		*/
 #define	  C0_YUVE_XVUY	  0x00030000	/* 420: .VUY		*/
 /* 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_VPOL_LOW	  0x02000000	/* VSYNC polarity active low */
 #define	  C0_VCLK_LOW	  0x04000000	/* VCLK on falling edge */
 #define	  C0_DOWNSCALE	  0x08000000	/* Enable downscaler */
-#define	  C0_SIFM_MASK	  0xc0000000	/* SIF mode bits */
+/* SIFMODE */
 #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 */
 #define REG_CTRL1	0x40	/* Control 1 */
diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c
index c4c17fe..3dad182 100644
--- a/drivers/media/platform/marvell-ccic/mmp-driver.c
+++ b/drivers/media/platform/marvell-ccic/mmp-driver.c
@@ -27,6 +27,7 @@ 
 #include <linux/delay.h>
 #include <linux/list.h>
 #include <linux/pm.h>
+#include <linux/clk.h>
 
 #include "mcam-core.h"
 
@@ -39,6 +40,7 @@  struct mmp_camera {
 	struct platform_device *pdev;
 	struct mcam_camera mcam;
 	struct list_head devlist;
+	struct clk *mipi_clk;
 	int irq;
 };
 
@@ -113,7 +115,7 @@  static void mmpcam_power_up_ctlr(struct mmp_camera *cam)
 	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_platform_data *pdata;
@@ -133,6 +135,12 @@  static void mmpcam_power_up(struct mcam_camera *mcam)
 	mdelay(5);
 	gpio_set_value(pdata->sensor_reset_gpio, 1); /* reset is active low */
 	mdelay(5);
+	if (mcam->bus_type == V4L2_MBUS_CSI2 && IS_ERR(cam->mipi_clk)) {
+		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);
+	}
+	return 0;
 }
 
 static void mmpcam_power_down(struct mcam_camera *mcam)
@@ -150,8 +158,106 @@  static void mmpcam_power_down(struct mcam_camera *mcam)
 	pdata = cam->pdev->dev.platform_data;
 	gpio_set_value(pdata->sensor_power_gpio, 0);
 	gpio_set_value(pdata->sensor_reset_gpio, 0);
+
+	if (mcam->bus_type == V4L2_MBUS_CSI2 && !IS_ERR(cam->mipi_clk)) {
+		devm_clk_put(mcam->dev, cam->mipi_clk);
+		cam->mipi_clk = ERR_PTR(-EINVAL);
+	}
 }
 
+/*
+ * 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)
 {
@@ -174,17 +280,30 @@  static int mmpcam_probe(struct platform_device *pdev)
 	struct mmp_camera_platform_data *pdata;
 	int ret;
 
+	pdata = pdev->dev.platform_data;
+	if (!pdata)
+		return -ENODEV;
+
 	cam = kzalloc(sizeof(*cam), GFP_KERNEL);
 	if (cam == NULL)
 		return -ENOMEM;
 	cam->pdev = pdev;
+	cam->mipi_clk = ERR_PTR(-EINVAL);
 	INIT_LIST_HEAD(&cam->devlist);
 
 	mcam = &cam->mcam;
 	mcam->plat_power_up = mmpcam_power_up;
 	mcam->plat_power_down = mmpcam_power_down;
+	mcam->calc_dphy = mmpcam_calc_dphy;
 	mcam->dev = &pdev->dev;
 	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 = V4L2_IDENT_ARMADA610;
 	mcam->buffer_mode = B_DMA_sg;
 	spin_lock_init(&mcam->dev_lock);
@@ -223,7 +342,6 @@  static int mmpcam_probe(struct platform_device *pdev)
 	 * Find the i2c adapter.  This assumes, of course, that the
 	 * i2c bus is already up and functioning.
 	 */
-	pdata = pdev->dev.platform_data;
 	mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device);
 	if (mcam->i2c_adapter == NULL) {
 		ret = -ENODEV;
@@ -250,7 +368,9 @@  static int mmpcam_probe(struct platform_device *pdev)
 	/*
 	 * Power the device up and hand it off to the core.
 	 */
-	mmpcam_power_up(mcam);
+	ret = mmpcam_power_up(mcam);
+	if (ret)
+		goto out_gpio2;
 	ret = mccic_register(mcam);
 	if (ret)
 		goto out_gpio2;
diff --git a/include/media/mmp-camera.h b/include/media/mmp-camera.h
index 7611963..b099d4b 100644
--- a/include/media/mmp-camera.h
+++ b/include/media/mmp-camera.h
@@ -1,9 +1,28 @@ 
+#include <media/v4l2-mediabus.h>
 /*
  * Information for the Marvell Armada MMP camera
  */
 
+enum dphy3_algo {
+	DPHY3_ALGO_DEFAULT = 0,
+	DPHY3_ALGO_PXA910,
+	DPHY3_ALGO_PXA2128
+};
+
 struct mmp_camera_platform_data {
 	struct platform_device *i2c_device;
 	int sensor_power_gpio;
 	int sensor_reset_gpio;
+	enum v4l2_mbus_type bus_type;
+	int mclk_min;
+	int mclk_src;
+	int mclk_div;
+	/*
+	 * MIPI support
+	 */
+	int dphy[3];		/* DPHY: CSI2_DPHY3, CSI2_DPHY5, CSI2_DPHY6 */
+	enum dphy3_algo dphy3_algo;	/* algos for calculate CSI2_DPHY3 */
+	int mipi_enabled;	/* MIPI enabled flag */
+	int lane;		/* ccic used lane number; 0 means DVP mode */
+	int lane_clk;
 };