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, ®); 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