18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ispcsiphy.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * TI OMAP3 ISP - CSI PHY module 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2010 Nokia Corporation 88c2ecf20Sopenharmony_ci * Copyright (C) 2009 Texas Instruments, Inc. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> 118c2ecf20Sopenharmony_ci * Sakari Ailus <sakari.ailus@iki.fi> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/device.h> 168c2ecf20Sopenharmony_ci#include <linux/regmap.h> 178c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "isp.h" 208c2ecf20Sopenharmony_ci#include "ispreg.h" 218c2ecf20Sopenharmony_ci#include "ispcsiphy.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic void csiphy_routing_cfg_3630(struct isp_csiphy *phy, 248c2ecf20Sopenharmony_ci enum isp_interface_type iface, 258c2ecf20Sopenharmony_ci bool ccp2_strobe) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci u32 reg; 288c2ecf20Sopenharmony_ci u32 shift, mode; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci regmap_read(phy->isp->syscon, phy->isp->syscon_offset, ®); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci switch (iface) { 338c2ecf20Sopenharmony_ci default: 348c2ecf20Sopenharmony_ci /* Should not happen in practice, but let's keep the compiler happy. */ 358c2ecf20Sopenharmony_ci case ISP_INTERFACE_CCP2B_PHY1: 368c2ecf20Sopenharmony_ci reg &= ~OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2; 378c2ecf20Sopenharmony_ci shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT; 388c2ecf20Sopenharmony_ci break; 398c2ecf20Sopenharmony_ci case ISP_INTERFACE_CSI2C_PHY1: 408c2ecf20Sopenharmony_ci shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT; 418c2ecf20Sopenharmony_ci mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_DPHY; 428c2ecf20Sopenharmony_ci break; 438c2ecf20Sopenharmony_ci case ISP_INTERFACE_CCP2B_PHY2: 448c2ecf20Sopenharmony_ci reg |= OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2; 458c2ecf20Sopenharmony_ci shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY2_SHIFT; 468c2ecf20Sopenharmony_ci break; 478c2ecf20Sopenharmony_ci case ISP_INTERFACE_CSI2A_PHY2: 488c2ecf20Sopenharmony_ci shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY2_SHIFT; 498c2ecf20Sopenharmony_ci mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_DPHY; 508c2ecf20Sopenharmony_ci break; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* Select data/clock or data/strobe mode for CCP2 */ 548c2ecf20Sopenharmony_ci if (iface == ISP_INTERFACE_CCP2B_PHY1 || 558c2ecf20Sopenharmony_ci iface == ISP_INTERFACE_CCP2B_PHY2) { 568c2ecf20Sopenharmony_ci if (ccp2_strobe) 578c2ecf20Sopenharmony_ci mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_STROBE; 588c2ecf20Sopenharmony_ci else 598c2ecf20Sopenharmony_ci mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_CLOCK; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci reg &= ~(OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_MASK << shift); 638c2ecf20Sopenharmony_ci reg |= mode << shift; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci regmap_write(phy->isp->syscon, phy->isp->syscon_offset, reg); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void csiphy_routing_cfg_3430(struct isp_csiphy *phy, u32 iface, bool on, 698c2ecf20Sopenharmony_ci bool ccp2_strobe) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci u32 csirxfe = OMAP343X_CONTROL_CSIRXFE_PWRDNZ 728c2ecf20Sopenharmony_ci | OMAP343X_CONTROL_CSIRXFE_RESET; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* Only the CCP2B on PHY1 is configurable. */ 758c2ecf20Sopenharmony_ci if (iface != ISP_INTERFACE_CCP2B_PHY1) 768c2ecf20Sopenharmony_ci return; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (!on) { 798c2ecf20Sopenharmony_ci regmap_write(phy->isp->syscon, phy->isp->syscon_offset, 0); 808c2ecf20Sopenharmony_ci return; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (ccp2_strobe) 848c2ecf20Sopenharmony_ci csirxfe |= OMAP343X_CONTROL_CSIRXFE_SELFORM; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci regmap_write(phy->isp->syscon, phy->isp->syscon_offset, csirxfe); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* 908c2ecf20Sopenharmony_ci * Configure OMAP 3 CSI PHY routing. 918c2ecf20Sopenharmony_ci * @phy: relevant phy device 928c2ecf20Sopenharmony_ci * @iface: ISP_INTERFACE_* 938c2ecf20Sopenharmony_ci * @on: power on or off 948c2ecf20Sopenharmony_ci * @ccp2_strobe: false: data/clock, true: data/strobe 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * Note that the underlying routing configuration registers are part of the 978c2ecf20Sopenharmony_ci * control (SCM) register space and part of the CORE power domain on both 3430 988c2ecf20Sopenharmony_ci * and 3630, so they will not hold their contents in off-mode. This isn't an 998c2ecf20Sopenharmony_ci * issue since the MPU power domain is forced on whilst the ISP is in use. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_cistatic void csiphy_routing_cfg(struct isp_csiphy *phy, 1028c2ecf20Sopenharmony_ci enum isp_interface_type iface, bool on, 1038c2ecf20Sopenharmony_ci bool ccp2_strobe) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci if (phy->isp->phy_type == ISP_PHY_TYPE_3630 && on) 1068c2ecf20Sopenharmony_ci return csiphy_routing_cfg_3630(phy, iface, ccp2_strobe); 1078c2ecf20Sopenharmony_ci if (phy->isp->phy_type == ISP_PHY_TYPE_3430) 1088c2ecf20Sopenharmony_ci return csiphy_routing_cfg_3430(phy, iface, on, ccp2_strobe); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * csiphy_power_autoswitch_enable 1138c2ecf20Sopenharmony_ci * @enable: Sets or clears the autoswitch function enable flag. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_cistatic void csiphy_power_autoswitch_enable(struct isp_csiphy *phy, bool enable) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG, 1188c2ecf20Sopenharmony_ci ISPCSI2_PHY_CFG_PWR_AUTO, 1198c2ecf20Sopenharmony_ci enable ? ISPCSI2_PHY_CFG_PWR_AUTO : 0); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* 1238c2ecf20Sopenharmony_ci * csiphy_set_power 1248c2ecf20Sopenharmony_ci * @power: Power state to be set. 1258c2ecf20Sopenharmony_ci * 1268c2ecf20Sopenharmony_ci * Returns 0 if successful, or -EBUSY if the retry count is exceeded. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_cistatic int csiphy_set_power(struct isp_csiphy *phy, u32 power) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci u32 reg; 1318c2ecf20Sopenharmony_ci u8 retry_count; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG, 1348c2ecf20Sopenharmony_ci ISPCSI2_PHY_CFG_PWR_CMD_MASK, power); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci retry_count = 0; 1378c2ecf20Sopenharmony_ci do { 1388c2ecf20Sopenharmony_ci udelay(50); 1398c2ecf20Sopenharmony_ci reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG) & 1408c2ecf20Sopenharmony_ci ISPCSI2_PHY_CFG_PWR_STATUS_MASK; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (reg != power >> 2) 1438c2ecf20Sopenharmony_ci retry_count++; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci } while ((reg != power >> 2) && (retry_count < 100)); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (retry_count == 100) { 1488c2ecf20Sopenharmony_ci dev_err(phy->isp->dev, "CSI2 CIO set power failed!\n"); 1498c2ecf20Sopenharmony_ci return -EBUSY; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* 1568c2ecf20Sopenharmony_ci * TCLK values are OK at their reset values 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ci#define TCLK_TERM 0 1598c2ecf20Sopenharmony_ci#define TCLK_MISS 1 1608c2ecf20Sopenharmony_ci#define TCLK_SETTLE 14 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int omap3isp_csiphy_config(struct isp_csiphy *phy) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct isp_pipeline *pipe = to_isp_pipeline(phy->entity); 1658c2ecf20Sopenharmony_ci struct isp_bus_cfg *buscfg = v4l2_subdev_to_bus_cfg(pipe->external); 1668c2ecf20Sopenharmony_ci struct isp_csiphy_lanes_cfg *lanes; 1678c2ecf20Sopenharmony_ci int csi2_ddrclk_khz; 1688c2ecf20Sopenharmony_ci unsigned int num_data_lanes, used_lanes = 0; 1698c2ecf20Sopenharmony_ci unsigned int i; 1708c2ecf20Sopenharmony_ci u32 reg; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (buscfg->interface == ISP_INTERFACE_CCP2B_PHY1 1738c2ecf20Sopenharmony_ci || buscfg->interface == ISP_INTERFACE_CCP2B_PHY2) { 1748c2ecf20Sopenharmony_ci lanes = &buscfg->bus.ccp2.lanecfg; 1758c2ecf20Sopenharmony_ci num_data_lanes = 1; 1768c2ecf20Sopenharmony_ci } else { 1778c2ecf20Sopenharmony_ci lanes = &buscfg->bus.csi2.lanecfg; 1788c2ecf20Sopenharmony_ci num_data_lanes = buscfg->bus.csi2.num_data_lanes; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (num_data_lanes > phy->num_data_lanes) 1828c2ecf20Sopenharmony_ci return -EINVAL; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* Clock and data lanes verification */ 1858c2ecf20Sopenharmony_ci for (i = 0; i < num_data_lanes; i++) { 1868c2ecf20Sopenharmony_ci if (lanes->data[i].pol > 1 || lanes->data[i].pos > 3) 1878c2ecf20Sopenharmony_ci return -EINVAL; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (used_lanes & (1 << lanes->data[i].pos)) 1908c2ecf20Sopenharmony_ci return -EINVAL; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci used_lanes |= 1 << lanes->data[i].pos; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (lanes->clk.pol > 1 || lanes->clk.pos > 3) 1968c2ecf20Sopenharmony_ci return -EINVAL; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos)) 1998c2ecf20Sopenharmony_ci return -EINVAL; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* 2028c2ecf20Sopenharmony_ci * The PHY configuration is lost in off mode, that's not an 2038c2ecf20Sopenharmony_ci * issue since the MPU power domain is forced on whilst the 2048c2ecf20Sopenharmony_ci * ISP is in use. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci csiphy_routing_cfg(phy, buscfg->interface, true, 2078c2ecf20Sopenharmony_ci buscfg->bus.ccp2.phy_layer); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* DPHY timing configuration */ 2108c2ecf20Sopenharmony_ci /* CSI-2 is DDR and we only count used lanes. */ 2118c2ecf20Sopenharmony_ci csi2_ddrclk_khz = pipe->external_rate / 1000 2128c2ecf20Sopenharmony_ci / (2 * hweight32(used_lanes)) * pipe->external_width; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG0); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci reg &= ~(ISPCSIPHY_REG0_THS_TERM_MASK | 2178c2ecf20Sopenharmony_ci ISPCSIPHY_REG0_THS_SETTLE_MASK); 2188c2ecf20Sopenharmony_ci /* THS_TERM: Programmed value = ceil(12.5 ns/DDRClk period) - 1. */ 2198c2ecf20Sopenharmony_ci reg |= (DIV_ROUND_UP(25 * csi2_ddrclk_khz, 2000000) - 1) 2208c2ecf20Sopenharmony_ci << ISPCSIPHY_REG0_THS_TERM_SHIFT; 2218c2ecf20Sopenharmony_ci /* THS_SETTLE: Programmed value = ceil(90 ns/DDRClk period) + 3. */ 2228c2ecf20Sopenharmony_ci reg |= (DIV_ROUND_UP(90 * csi2_ddrclk_khz, 1000000) + 3) 2238c2ecf20Sopenharmony_ci << ISPCSIPHY_REG0_THS_SETTLE_SHIFT; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG0); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG1); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci reg &= ~(ISPCSIPHY_REG1_TCLK_TERM_MASK | 2308c2ecf20Sopenharmony_ci ISPCSIPHY_REG1_TCLK_MISS_MASK | 2318c2ecf20Sopenharmony_ci ISPCSIPHY_REG1_TCLK_SETTLE_MASK); 2328c2ecf20Sopenharmony_ci reg |= TCLK_TERM << ISPCSIPHY_REG1_TCLK_TERM_SHIFT; 2338c2ecf20Sopenharmony_ci reg |= TCLK_MISS << ISPCSIPHY_REG1_TCLK_MISS_SHIFT; 2348c2ecf20Sopenharmony_ci reg |= TCLK_SETTLE << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG1); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* DPHY lane configuration */ 2398c2ecf20Sopenharmony_ci reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci for (i = 0; i < num_data_lanes; i++) { 2428c2ecf20Sopenharmony_ci reg &= ~(ISPCSI2_PHY_CFG_DATA_POL_MASK(i + 1) | 2438c2ecf20Sopenharmony_ci ISPCSI2_PHY_CFG_DATA_POSITION_MASK(i + 1)); 2448c2ecf20Sopenharmony_ci reg |= (lanes->data[i].pol << 2458c2ecf20Sopenharmony_ci ISPCSI2_PHY_CFG_DATA_POL_SHIFT(i + 1)); 2468c2ecf20Sopenharmony_ci reg |= (lanes->data[i].pos << 2478c2ecf20Sopenharmony_ci ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(i + 1)); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci reg &= ~(ISPCSI2_PHY_CFG_CLOCK_POL_MASK | 2518c2ecf20Sopenharmony_ci ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK); 2528c2ecf20Sopenharmony_ci reg |= lanes->clk.pol << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT; 2538c2ecf20Sopenharmony_ci reg |= lanes->clk.pos << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci isp_reg_writel(phy->isp, reg, phy->cfg_regs, ISPCSI2_PHY_CFG); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return 0; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ciint omap3isp_csiphy_acquire(struct isp_csiphy *phy, struct media_entity *entity) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci int rval; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (phy->vdd == NULL) { 2658c2ecf20Sopenharmony_ci dev_err(phy->isp->dev, 2668c2ecf20Sopenharmony_ci "Power regulator for CSI PHY not available\n"); 2678c2ecf20Sopenharmony_ci return -ENODEV; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci mutex_lock(&phy->mutex); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci rval = regulator_enable(phy->vdd); 2738c2ecf20Sopenharmony_ci if (rval < 0) 2748c2ecf20Sopenharmony_ci goto done; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci rval = omap3isp_csi2_reset(phy->csi2); 2778c2ecf20Sopenharmony_ci if (rval < 0) 2788c2ecf20Sopenharmony_ci goto done; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci phy->entity = entity; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci rval = omap3isp_csiphy_config(phy); 2838c2ecf20Sopenharmony_ci if (rval < 0) 2848c2ecf20Sopenharmony_ci goto done; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (phy->isp->revision == ISP_REVISION_15_0) { 2878c2ecf20Sopenharmony_ci rval = csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_ON); 2888c2ecf20Sopenharmony_ci if (rval) { 2898c2ecf20Sopenharmony_ci regulator_disable(phy->vdd); 2908c2ecf20Sopenharmony_ci goto done; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci csiphy_power_autoswitch_enable(phy, true); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_cidone: 2968c2ecf20Sopenharmony_ci if (rval < 0) 2978c2ecf20Sopenharmony_ci phy->entity = NULL; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci mutex_unlock(&phy->mutex); 3008c2ecf20Sopenharmony_ci return rval; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_civoid omap3isp_csiphy_release(struct isp_csiphy *phy) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci mutex_lock(&phy->mutex); 3068c2ecf20Sopenharmony_ci if (phy->entity) { 3078c2ecf20Sopenharmony_ci struct isp_pipeline *pipe = to_isp_pipeline(phy->entity); 3088c2ecf20Sopenharmony_ci struct isp_bus_cfg *buscfg = 3098c2ecf20Sopenharmony_ci v4l2_subdev_to_bus_cfg(pipe->external); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci csiphy_routing_cfg(phy, buscfg->interface, false, 3128c2ecf20Sopenharmony_ci buscfg->bus.ccp2.phy_layer); 3138c2ecf20Sopenharmony_ci if (phy->isp->revision == ISP_REVISION_15_0) { 3148c2ecf20Sopenharmony_ci csiphy_power_autoswitch_enable(phy, false); 3158c2ecf20Sopenharmony_ci csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_OFF); 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci regulator_disable(phy->vdd); 3188c2ecf20Sopenharmony_ci phy->entity = NULL; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci mutex_unlock(&phy->mutex); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci/* 3248c2ecf20Sopenharmony_ci * omap3isp_csiphy_init - Initialize the CSI PHY frontends 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_ciint omap3isp_csiphy_init(struct isp_device *isp) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct isp_csiphy *phy1 = &isp->isp_csiphy1; 3298c2ecf20Sopenharmony_ci struct isp_csiphy *phy2 = &isp->isp_csiphy2; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci phy2->isp = isp; 3328c2ecf20Sopenharmony_ci phy2->csi2 = &isp->isp_csi2a; 3338c2ecf20Sopenharmony_ci phy2->num_data_lanes = ISP_CSIPHY2_NUM_DATA_LANES; 3348c2ecf20Sopenharmony_ci phy2->cfg_regs = OMAP3_ISP_IOMEM_CSI2A_REGS1; 3358c2ecf20Sopenharmony_ci phy2->phy_regs = OMAP3_ISP_IOMEM_CSIPHY2; 3368c2ecf20Sopenharmony_ci mutex_init(&phy2->mutex); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci phy1->isp = isp; 3398c2ecf20Sopenharmony_ci mutex_init(&phy1->mutex); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (isp->revision == ISP_REVISION_15_0) { 3428c2ecf20Sopenharmony_ci phy1->csi2 = &isp->isp_csi2c; 3438c2ecf20Sopenharmony_ci phy1->num_data_lanes = ISP_CSIPHY1_NUM_DATA_LANES; 3448c2ecf20Sopenharmony_ci phy1->cfg_regs = OMAP3_ISP_IOMEM_CSI2C_REGS1; 3458c2ecf20Sopenharmony_ci phy1->phy_regs = OMAP3_ISP_IOMEM_CSIPHY1; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_civoid omap3isp_csiphy_cleanup(struct isp_device *isp) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci mutex_destroy(&isp->isp_csiphy1.mutex); 3548c2ecf20Sopenharmony_ci mutex_destroy(&isp->isp_csiphy2.mutex); 3558c2ecf20Sopenharmony_ci} 356