Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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
@@ -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|GPR_C0);

	return 0;
}

static void cafe_ctlr_power_down(struct mcam_camera *mcam)
+76 −4
Original line number Diff line number Diff line
@@ -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>
@@ -253,6 +254,45 @@ static void mcam_ctlr_stop(struct mcam_camera *cam)
	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
@@ -656,6 +696,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);
}


@@ -753,15 +800,21 @@ 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) {
		spin_unlock_irqrestore(&cam->dev_lock, flags);
		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)
@@ -869,6 +922,17 @@ 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]);
	if (cam->bus_type == V4L2_MBUS_CSI2)
		mcam_enable_mipi(cam);
	else
		mcam_disable_mipi(cam);
	mcam_ctlr_irq_enable(cam);
	cam->state = S_STREAMING;
	if (!test_bit(CF_SG_RESTART, &cam->flags))
@@ -1475,7 +1539,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)
			goto out;
		__mcam_cam_reset(cam);
		mcam_set_config_needed(cam, 1);
	}
@@ -1498,10 +1564,12 @@ static int mcam_v4l_release(struct file *filp)
	if (cam->users == 0) {
		mcam_ctlr_stop_dma(cam);
		mcam_cleanup_vb2(cam);
		mcam_disable_mipi(cam);
		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;
}
@@ -1787,7 +1855,11 @@ 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) {
			mutex_unlock(&cam->s_mutex);
			return ret;
		}
		__mcam_cam_reset(cam);
	} else {
		mcam_ctlr_power_down(cam);
+34 −3
Original line number Diff line number Diff line
@@ -108,11 +108,28 @@ 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;	/* 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.
	 */
	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
@@ -225,6 +242,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 */
@@ -293,13 +321,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 */
+131 −5
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/pm.h>
#include <linux/clk.h>

#include "mcam-core.h"

@@ -38,6 +39,7 @@ struct mmp_camera {
	struct platform_device *pdev;
	struct mcam_camera mcam;
	struct list_head devlist;
	struct clk *mipi_clk;
	int irq;
};

@@ -112,10 +114,17 @@ 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;

	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.
 */
@@ -132,6 +141,7 @@ 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);
	return 0;
}

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;
	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)) {
		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)
{
@@ -173,17 +284,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 = NULL;
	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 = MCAM_ARMADA610;
	mcam->buffer_mode = B_DMA_sg;
	spin_lock_init(&mcam->dev_lock);
@@ -223,7 +347,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,10 +373,12 @@ static int mmpcam_probe(struct platform_device *pdev)
	/*
	 * Power the device up and hand it off to the core.
	 */
	mmpcam_power_up(mcam);
	ret = mccic_register(mcam);
	ret = mmpcam_power_up(mcam);
	if (ret)
		goto out_gpio2;
	ret = mccic_register(mcam);
	if (ret)
		goto out_pwdn;
	/*
	 * Finally, set up our IRQ now that the core is ready to
	 * deal with it.
@@ -273,8 +398,9 @@ static int mmpcam_probe(struct platform_device *pdev)

out_unregister:
	mccic_shutdown(mcam);
out_gpio2:
out_pwdn:
	mmpcam_power_down(mcam);
out_gpio2:
	gpio_free(pdata->sensor_reset_gpio);
out_gpio:
	gpio_free(pdata->sensor_power_gpio);