162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ispcsiphy.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * TI OMAP3 ISP - CSI PHY module
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2010 Nokia Corporation
862306a36Sopenharmony_ci * Copyright (C) 2009 Texas Instruments, Inc.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
1162306a36Sopenharmony_ci *	     Sakari Ailus <sakari.ailus@iki.fi>
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/device.h>
1662306a36Sopenharmony_ci#include <linux/regmap.h>
1762306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "isp.h"
2062306a36Sopenharmony_ci#include "ispreg.h"
2162306a36Sopenharmony_ci#include "ispcsiphy.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic void csiphy_routing_cfg_3630(struct isp_csiphy *phy,
2462306a36Sopenharmony_ci				    enum isp_interface_type iface,
2562306a36Sopenharmony_ci				    bool ccp2_strobe)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	u32 reg;
2862306a36Sopenharmony_ci	u32 shift, mode;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	regmap_read(phy->isp->syscon, phy->isp->syscon_offset, &reg);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	switch (iface) {
3362306a36Sopenharmony_ci	default:
3462306a36Sopenharmony_ci		/* Should not happen in practice, but let's keep the compiler happy. */
3562306a36Sopenharmony_ci		return;
3662306a36Sopenharmony_ci	case ISP_INTERFACE_CCP2B_PHY1:
3762306a36Sopenharmony_ci		reg &= ~OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2;
3862306a36Sopenharmony_ci		shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT;
3962306a36Sopenharmony_ci		break;
4062306a36Sopenharmony_ci	case ISP_INTERFACE_CSI2C_PHY1:
4162306a36Sopenharmony_ci		shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT;
4262306a36Sopenharmony_ci		mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_DPHY;
4362306a36Sopenharmony_ci		break;
4462306a36Sopenharmony_ci	case ISP_INTERFACE_CCP2B_PHY2:
4562306a36Sopenharmony_ci		reg |= OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2;
4662306a36Sopenharmony_ci		shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY2_SHIFT;
4762306a36Sopenharmony_ci		break;
4862306a36Sopenharmony_ci	case ISP_INTERFACE_CSI2A_PHY2:
4962306a36Sopenharmony_ci		shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY2_SHIFT;
5062306a36Sopenharmony_ci		mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_DPHY;
5162306a36Sopenharmony_ci		break;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* Select data/clock or data/strobe mode for CCP2 */
5562306a36Sopenharmony_ci	if (iface == ISP_INTERFACE_CCP2B_PHY1 ||
5662306a36Sopenharmony_ci	    iface == ISP_INTERFACE_CCP2B_PHY2) {
5762306a36Sopenharmony_ci		if (ccp2_strobe)
5862306a36Sopenharmony_ci			mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_STROBE;
5962306a36Sopenharmony_ci		else
6062306a36Sopenharmony_ci			mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_CLOCK;
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	reg &= ~(OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_MASK << shift);
6462306a36Sopenharmony_ci	reg |= mode << shift;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	regmap_write(phy->isp->syscon, phy->isp->syscon_offset, reg);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void csiphy_routing_cfg_3430(struct isp_csiphy *phy, u32 iface, bool on,
7062306a36Sopenharmony_ci				    bool ccp2_strobe)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	u32 csirxfe = OMAP343X_CONTROL_CSIRXFE_PWRDNZ
7362306a36Sopenharmony_ci		| OMAP343X_CONTROL_CSIRXFE_RESET;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/* Only the CCP2B on PHY1 is configurable. */
7662306a36Sopenharmony_ci	if (iface != ISP_INTERFACE_CCP2B_PHY1)
7762306a36Sopenharmony_ci		return;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (!on) {
8062306a36Sopenharmony_ci		regmap_write(phy->isp->syscon, phy->isp->syscon_offset, 0);
8162306a36Sopenharmony_ci		return;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (ccp2_strobe)
8562306a36Sopenharmony_ci		csirxfe |= OMAP343X_CONTROL_CSIRXFE_SELFORM;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	regmap_write(phy->isp->syscon, phy->isp->syscon_offset, csirxfe);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/*
9162306a36Sopenharmony_ci * Configure OMAP 3 CSI PHY routing.
9262306a36Sopenharmony_ci * @phy: relevant phy device
9362306a36Sopenharmony_ci * @iface: ISP_INTERFACE_*
9462306a36Sopenharmony_ci * @on: power on or off
9562306a36Sopenharmony_ci * @ccp2_strobe: false: data/clock, true: data/strobe
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci * Note that the underlying routing configuration registers are part of the
9862306a36Sopenharmony_ci * control (SCM) register space and part of the CORE power domain on both 3430
9962306a36Sopenharmony_ci * and 3630, so they will not hold their contents in off-mode. This isn't an
10062306a36Sopenharmony_ci * issue since the MPU power domain is forced on whilst the ISP is in use.
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_cistatic void csiphy_routing_cfg(struct isp_csiphy *phy,
10362306a36Sopenharmony_ci			       enum isp_interface_type iface, bool on,
10462306a36Sopenharmony_ci			       bool ccp2_strobe)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	if (phy->isp->phy_type == ISP_PHY_TYPE_3630 && on)
10762306a36Sopenharmony_ci		return csiphy_routing_cfg_3630(phy, iface, ccp2_strobe);
10862306a36Sopenharmony_ci	if (phy->isp->phy_type == ISP_PHY_TYPE_3430)
10962306a36Sopenharmony_ci		return csiphy_routing_cfg_3430(phy, iface, on, ccp2_strobe);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/*
11362306a36Sopenharmony_ci * csiphy_power_autoswitch_enable
11462306a36Sopenharmony_ci * @enable: Sets or clears the autoswitch function enable flag.
11562306a36Sopenharmony_ci */
11662306a36Sopenharmony_cistatic void csiphy_power_autoswitch_enable(struct isp_csiphy *phy, bool enable)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG,
11962306a36Sopenharmony_ci			ISPCSI2_PHY_CFG_PWR_AUTO,
12062306a36Sopenharmony_ci			enable ? ISPCSI2_PHY_CFG_PWR_AUTO : 0);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci/*
12462306a36Sopenharmony_ci * csiphy_set_power
12562306a36Sopenharmony_ci * @power: Power state to be set.
12662306a36Sopenharmony_ci *
12762306a36Sopenharmony_ci * Returns 0 if successful, or -EBUSY if the retry count is exceeded.
12862306a36Sopenharmony_ci */
12962306a36Sopenharmony_cistatic int csiphy_set_power(struct isp_csiphy *phy, u32 power)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	u32 reg;
13262306a36Sopenharmony_ci	u8 retry_count;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG,
13562306a36Sopenharmony_ci			ISPCSI2_PHY_CFG_PWR_CMD_MASK, power);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	retry_count = 0;
13862306a36Sopenharmony_ci	do {
13962306a36Sopenharmony_ci		udelay(50);
14062306a36Sopenharmony_ci		reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG) &
14162306a36Sopenharmony_ci				    ISPCSI2_PHY_CFG_PWR_STATUS_MASK;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		if (reg != power >> 2)
14462306a36Sopenharmony_ci			retry_count++;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	} while ((reg != power >> 2) && (retry_count < 100));
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (retry_count == 100) {
14962306a36Sopenharmony_ci		dev_err(phy->isp->dev, "CSI2 CIO set power failed!\n");
15062306a36Sopenharmony_ci		return -EBUSY;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/*
15762306a36Sopenharmony_ci * TCLK values are OK at their reset values
15862306a36Sopenharmony_ci */
15962306a36Sopenharmony_ci#define TCLK_TERM	0
16062306a36Sopenharmony_ci#define TCLK_MISS	1
16162306a36Sopenharmony_ci#define TCLK_SETTLE	14
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic int omap3isp_csiphy_config(struct isp_csiphy *phy)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct isp_pipeline *pipe = to_isp_pipeline(phy->entity);
16662306a36Sopenharmony_ci	struct isp_bus_cfg *buscfg;
16762306a36Sopenharmony_ci	struct isp_csiphy_lanes_cfg *lanes;
16862306a36Sopenharmony_ci	int csi2_ddrclk_khz;
16962306a36Sopenharmony_ci	unsigned int num_data_lanes, used_lanes = 0;
17062306a36Sopenharmony_ci	unsigned int i;
17162306a36Sopenharmony_ci	u32 reg;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	buscfg = v4l2_subdev_to_bus_cfg(pipe->external);
17462306a36Sopenharmony_ci	if (WARN_ON(!buscfg))
17562306a36Sopenharmony_ci		return -EPIPE;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (buscfg->interface == ISP_INTERFACE_CCP2B_PHY1
17862306a36Sopenharmony_ci	    || buscfg->interface == ISP_INTERFACE_CCP2B_PHY2) {
17962306a36Sopenharmony_ci		lanes = &buscfg->bus.ccp2.lanecfg;
18062306a36Sopenharmony_ci		num_data_lanes = 1;
18162306a36Sopenharmony_ci	} else {
18262306a36Sopenharmony_ci		lanes = &buscfg->bus.csi2.lanecfg;
18362306a36Sopenharmony_ci		num_data_lanes = buscfg->bus.csi2.num_data_lanes;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (num_data_lanes > phy->num_data_lanes)
18762306a36Sopenharmony_ci		return -EINVAL;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* Clock and data lanes verification */
19062306a36Sopenharmony_ci	for (i = 0; i < num_data_lanes; i++) {
19162306a36Sopenharmony_ci		if (lanes->data[i].pol > 1 || lanes->data[i].pos > 3)
19262306a36Sopenharmony_ci			return -EINVAL;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci		if (used_lanes & (1 << lanes->data[i].pos))
19562306a36Sopenharmony_ci			return -EINVAL;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		used_lanes |= 1 << lanes->data[i].pos;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (lanes->clk.pol > 1 || lanes->clk.pos > 3)
20162306a36Sopenharmony_ci		return -EINVAL;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos))
20462306a36Sopenharmony_ci		return -EINVAL;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/*
20762306a36Sopenharmony_ci	 * The PHY configuration is lost in off mode, that's not an
20862306a36Sopenharmony_ci	 * issue since the MPU power domain is forced on whilst the
20962306a36Sopenharmony_ci	 * ISP is in use.
21062306a36Sopenharmony_ci	 */
21162306a36Sopenharmony_ci	csiphy_routing_cfg(phy, buscfg->interface, true,
21262306a36Sopenharmony_ci			   buscfg->bus.ccp2.phy_layer);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* DPHY timing configuration */
21562306a36Sopenharmony_ci	/* CSI-2 is DDR and we only count used lanes. */
21662306a36Sopenharmony_ci	csi2_ddrclk_khz = pipe->external_rate / 1000
21762306a36Sopenharmony_ci		/ (2 * hweight32(used_lanes)) * pipe->external_width;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG0);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	reg &= ~(ISPCSIPHY_REG0_THS_TERM_MASK |
22262306a36Sopenharmony_ci		 ISPCSIPHY_REG0_THS_SETTLE_MASK);
22362306a36Sopenharmony_ci	/* THS_TERM: Programmed value = ceil(12.5 ns/DDRClk period) - 1. */
22462306a36Sopenharmony_ci	reg |= (DIV_ROUND_UP(25 * csi2_ddrclk_khz, 2000000) - 1)
22562306a36Sopenharmony_ci		<< ISPCSIPHY_REG0_THS_TERM_SHIFT;
22662306a36Sopenharmony_ci	/* THS_SETTLE: Programmed value = ceil(90 ns/DDRClk period) + 3. */
22762306a36Sopenharmony_ci	reg |= (DIV_ROUND_UP(90 * csi2_ddrclk_khz, 1000000) + 3)
22862306a36Sopenharmony_ci		<< ISPCSIPHY_REG0_THS_SETTLE_SHIFT;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG0);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG1);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	reg &= ~(ISPCSIPHY_REG1_TCLK_TERM_MASK |
23562306a36Sopenharmony_ci		 ISPCSIPHY_REG1_TCLK_MISS_MASK |
23662306a36Sopenharmony_ci		 ISPCSIPHY_REG1_TCLK_SETTLE_MASK);
23762306a36Sopenharmony_ci	reg |= TCLK_TERM << ISPCSIPHY_REG1_TCLK_TERM_SHIFT;
23862306a36Sopenharmony_ci	reg |= TCLK_MISS << ISPCSIPHY_REG1_TCLK_MISS_SHIFT;
23962306a36Sopenharmony_ci	reg |= TCLK_SETTLE << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG1);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/* DPHY lane configuration */
24462306a36Sopenharmony_ci	reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	for (i = 0; i < num_data_lanes; i++) {
24762306a36Sopenharmony_ci		reg &= ~(ISPCSI2_PHY_CFG_DATA_POL_MASK(i + 1) |
24862306a36Sopenharmony_ci			 ISPCSI2_PHY_CFG_DATA_POSITION_MASK(i + 1));
24962306a36Sopenharmony_ci		reg |= (lanes->data[i].pol <<
25062306a36Sopenharmony_ci			ISPCSI2_PHY_CFG_DATA_POL_SHIFT(i + 1));
25162306a36Sopenharmony_ci		reg |= (lanes->data[i].pos <<
25262306a36Sopenharmony_ci			ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(i + 1));
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	reg &= ~(ISPCSI2_PHY_CFG_CLOCK_POL_MASK |
25662306a36Sopenharmony_ci		 ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK);
25762306a36Sopenharmony_ci	reg |= lanes->clk.pol << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT;
25862306a36Sopenharmony_ci	reg |= lanes->clk.pos << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	isp_reg_writel(phy->isp, reg, phy->cfg_regs, ISPCSI2_PHY_CFG);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	return 0;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ciint omap3isp_csiphy_acquire(struct isp_csiphy *phy, struct media_entity *entity)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	int rval;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (phy->vdd == NULL) {
27062306a36Sopenharmony_ci		dev_err(phy->isp->dev,
27162306a36Sopenharmony_ci			"Power regulator for CSI PHY not available\n");
27262306a36Sopenharmony_ci		return -ENODEV;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	mutex_lock(&phy->mutex);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	rval = regulator_enable(phy->vdd);
27862306a36Sopenharmony_ci	if (rval < 0)
27962306a36Sopenharmony_ci		goto done;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	rval = omap3isp_csi2_reset(phy->csi2);
28262306a36Sopenharmony_ci	if (rval < 0)
28362306a36Sopenharmony_ci		goto done;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	phy->entity = entity;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	rval = omap3isp_csiphy_config(phy);
28862306a36Sopenharmony_ci	if (rval < 0)
28962306a36Sopenharmony_ci		goto done;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (phy->isp->revision == ISP_REVISION_15_0) {
29262306a36Sopenharmony_ci		rval = csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_ON);
29362306a36Sopenharmony_ci		if (rval) {
29462306a36Sopenharmony_ci			regulator_disable(phy->vdd);
29562306a36Sopenharmony_ci			goto done;
29662306a36Sopenharmony_ci		}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci		csiphy_power_autoswitch_enable(phy, true);
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_cidone:
30162306a36Sopenharmony_ci	if (rval < 0)
30262306a36Sopenharmony_ci		phy->entity = NULL;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	mutex_unlock(&phy->mutex);
30562306a36Sopenharmony_ci	return rval;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_civoid omap3isp_csiphy_release(struct isp_csiphy *phy)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	mutex_lock(&phy->mutex);
31162306a36Sopenharmony_ci	if (phy->entity) {
31262306a36Sopenharmony_ci		struct isp_pipeline *pipe = to_isp_pipeline(phy->entity);
31362306a36Sopenharmony_ci		struct isp_bus_cfg *buscfg;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		buscfg = v4l2_subdev_to_bus_cfg(pipe->external);
31662306a36Sopenharmony_ci		if (WARN_ON(!buscfg)) {
31762306a36Sopenharmony_ci			mutex_unlock(&phy->mutex);
31862306a36Sopenharmony_ci			return;
31962306a36Sopenharmony_ci		}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci		csiphy_routing_cfg(phy, buscfg->interface, false,
32262306a36Sopenharmony_ci				   buscfg->bus.ccp2.phy_layer);
32362306a36Sopenharmony_ci		if (phy->isp->revision == ISP_REVISION_15_0) {
32462306a36Sopenharmony_ci			csiphy_power_autoswitch_enable(phy, false);
32562306a36Sopenharmony_ci			csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_OFF);
32662306a36Sopenharmony_ci		}
32762306a36Sopenharmony_ci		regulator_disable(phy->vdd);
32862306a36Sopenharmony_ci		phy->entity = NULL;
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci	mutex_unlock(&phy->mutex);
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci/*
33462306a36Sopenharmony_ci * omap3isp_csiphy_init - Initialize the CSI PHY frontends
33562306a36Sopenharmony_ci */
33662306a36Sopenharmony_ciint omap3isp_csiphy_init(struct isp_device *isp)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct isp_csiphy *phy1 = &isp->isp_csiphy1;
33962306a36Sopenharmony_ci	struct isp_csiphy *phy2 = &isp->isp_csiphy2;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	phy2->isp = isp;
34262306a36Sopenharmony_ci	phy2->csi2 = &isp->isp_csi2a;
34362306a36Sopenharmony_ci	phy2->num_data_lanes = ISP_CSIPHY2_NUM_DATA_LANES;
34462306a36Sopenharmony_ci	phy2->cfg_regs = OMAP3_ISP_IOMEM_CSI2A_REGS1;
34562306a36Sopenharmony_ci	phy2->phy_regs = OMAP3_ISP_IOMEM_CSIPHY2;
34662306a36Sopenharmony_ci	mutex_init(&phy2->mutex);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	phy1->isp = isp;
34962306a36Sopenharmony_ci	mutex_init(&phy1->mutex);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (isp->revision == ISP_REVISION_15_0) {
35262306a36Sopenharmony_ci		phy1->csi2 = &isp->isp_csi2c;
35362306a36Sopenharmony_ci		phy1->num_data_lanes = ISP_CSIPHY1_NUM_DATA_LANES;
35462306a36Sopenharmony_ci		phy1->cfg_regs = OMAP3_ISP_IOMEM_CSI2C_REGS1;
35562306a36Sopenharmony_ci		phy1->phy_regs = OMAP3_ISP_IOMEM_CSIPHY1;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	return 0;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_civoid omap3isp_csiphy_cleanup(struct isp_device *isp)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	mutex_destroy(&isp->isp_csiphy1.mutex);
36462306a36Sopenharmony_ci	mutex_destroy(&isp->isp_csiphy2.mutex);
36562306a36Sopenharmony_ci}
366