1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * TI OMAP4 ISS V4L2 Driver - CSI PHY module 4 * 5 * Copyright (C) 2012 Texas Instruments, Inc. 6 * 7 * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> 8 */ 9 10 #include <linux/delay.h> 11 #include <linux/device.h> 12 #include <linux/regmap.h> 13 14 #include "../../../../arch/arm/mach-omap2/control.h" 15 16 #include "iss.h" 17 #include "iss_regs.h" 18 #include "iss_csiphy.h" 19 20 /* 21 * csiphy_lanes_config - Configuration of CSIPHY lanes. 22 * 23 * Updates HW configuration. 24 * Called with phy->mutex taken. 25 */ 26 static void csiphy_lanes_config(struct iss_csiphy *phy) 27 { 28 unsigned int i; 29 u32 reg; 30 31 reg = iss_reg_read(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG); 32 33 for (i = 0; i < phy->max_data_lanes; i++) { 34 reg &= ~(CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) | 35 CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i + 1)); 36 reg |= (phy->lanes.data[i].pol ? 37 CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) : 0); 38 reg |= (phy->lanes.data[i].pos << 39 CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i + 1)); 40 } 41 42 reg &= ~(CSI2_COMPLEXIO_CFG_CLOCK_POL | 43 CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK); 44 reg |= phy->lanes.clk.pol ? CSI2_COMPLEXIO_CFG_CLOCK_POL : 0; 45 reg |= phy->lanes.clk.pos << CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT; 46 47 iss_reg_write(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG, reg); 48 } 49 50 /* 51 * csiphy_set_power 52 * @power: Power state to be set. 53 * 54 * Returns 0 if successful, or -EBUSY if the retry count is exceeded. 55 */ 56 static int csiphy_set_power(struct iss_csiphy *phy, u32 power) 57 { 58 u32 reg; 59 u8 retry_count; 60 61 iss_reg_update(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG, 62 CSI2_COMPLEXIO_CFG_PWD_CMD_MASK, 63 power | CSI2_COMPLEXIO_CFG_PWR_AUTO); 64 65 retry_count = 0; 66 do { 67 udelay(1); 68 reg = iss_reg_read(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG) 69 & CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK; 70 71 if (reg != power >> 2) 72 retry_count++; 73 74 } while ((reg != power >> 2) && (retry_count < 250)); 75 76 if (retry_count == 250) { 77 dev_err(phy->iss->dev, "CSI2 CIO set power failed!\n"); 78 return -EBUSY; 79 } 80 81 return 0; 82 } 83 84 /* 85 * csiphy_dphy_config - Configure CSI2 D-PHY parameters. 86 * 87 * Called with phy->mutex taken. 88 */ 89 static void csiphy_dphy_config(struct iss_csiphy *phy) 90 { 91 u32 reg; 92 93 /* Set up REGISTER0 */ 94 reg = phy->dphy.ths_term << REGISTER0_THS_TERM_SHIFT; 95 reg |= phy->dphy.ths_settle << REGISTER0_THS_SETTLE_SHIFT; 96 97 iss_reg_write(phy->iss, phy->phy_regs, REGISTER0, reg); 98 99 /* Set up REGISTER1 */ 100 reg = phy->dphy.tclk_term << REGISTER1_TCLK_TERM_SHIFT; 101 reg |= phy->dphy.tclk_miss << REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT; 102 reg |= phy->dphy.tclk_settle << REGISTER1_TCLK_SETTLE_SHIFT; 103 reg |= 0xb8 << REGISTER1_DPHY_HS_SYNC_PATTERN_SHIFT; 104 105 iss_reg_write(phy->iss, phy->phy_regs, REGISTER1, reg); 106 } 107 108 /* 109 * TCLK values are OK at their reset values 110 */ 111 #define TCLK_TERM 0 112 #define TCLK_MISS 1 113 #define TCLK_SETTLE 14 114 115 int omap4iss_csiphy_config(struct iss_device *iss, 116 struct v4l2_subdev *csi2_subdev) 117 { 118 struct iss_csi2_device *csi2 = v4l2_get_subdevdata(csi2_subdev); 119 struct iss_pipeline *pipe = to_iss_pipeline(&csi2_subdev->entity); 120 struct iss_v4l2_subdevs_group *subdevs = pipe->external->host_priv; 121 struct iss_csiphy_dphy_cfg csi2phy; 122 int csi2_ddrclk_khz; 123 struct iss_csiphy_lanes_cfg *lanes; 124 unsigned int used_lanes = 0; 125 u32 cam_rx_ctrl; 126 unsigned int i; 127 128 lanes = &subdevs->bus.csi2.lanecfg; 129 130 /* 131 * SCM.CONTROL_CAMERA_RX 132 * - bit [31] : CSIPHY2 lane 2 enable (4460+ only) 133 * - bit [30:29] : CSIPHY2 per-lane enable (1 to 0) 134 * - bit [28:24] : CSIPHY1 per-lane enable (4 to 0) 135 * - bit [21] : CSIPHY2 CTRLCLK enable 136 * - bit [20:19] : CSIPHY2 config: 00 d-phy, 01/10 ccp2 137 * - bit [18] : CSIPHY1 CTRLCLK enable 138 * - bit [17:16] : CSIPHY1 config: 00 d-phy, 01/10 ccp2 139 */ 140 /* 141 * TODO: When implementing DT support specify the CONTROL_CAMERA_RX 142 * register offset in the syscon property instead of hardcoding it. 143 */ 144 regmap_read(iss->syscon, 0x68, &cam_rx_ctrl); 145 146 if (subdevs->interface == ISS_INTERFACE_CSI2A_PHY1) { 147 cam_rx_ctrl &= ~(OMAP4_CAMERARX_CSI21_LANEENABLE_MASK | 148 OMAP4_CAMERARX_CSI21_CAMMODE_MASK); 149 /* NOTE: Leave CSIPHY1 config to 0x0: D-PHY mode */ 150 /* Enable all lanes for now */ 151 cam_rx_ctrl |= 152 0x1f << OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT; 153 /* Enable CTRLCLK */ 154 cam_rx_ctrl |= OMAP4_CAMERARX_CSI21_CTRLCLKEN_MASK; 155 } 156 157 if (subdevs->interface == ISS_INTERFACE_CSI2B_PHY2) { 158 cam_rx_ctrl &= ~(OMAP4_CAMERARX_CSI22_LANEENABLE_MASK | 159 OMAP4_CAMERARX_CSI22_CAMMODE_MASK); 160 /* NOTE: Leave CSIPHY2 config to 0x0: D-PHY mode */ 161 /* Enable all lanes for now */ 162 cam_rx_ctrl |= 163 0x3 << OMAP4_CAMERARX_CSI22_LANEENABLE_SHIFT; 164 /* Enable CTRLCLK */ 165 cam_rx_ctrl |= OMAP4_CAMERARX_CSI22_CTRLCLKEN_MASK; 166 } 167 168 regmap_write(iss->syscon, 0x68, cam_rx_ctrl); 169 170 /* Reset used lane count */ 171 csi2->phy->used_data_lanes = 0; 172 173 /* Clock and data lanes verification */ 174 for (i = 0; i < csi2->phy->max_data_lanes; i++) { 175 if (lanes->data[i].pos == 0) 176 continue; 177 178 if (lanes->data[i].pol > 1 || 179 lanes->data[i].pos > (csi2->phy->max_data_lanes + 1)) 180 return -EINVAL; 181 182 if (used_lanes & (1 << lanes->data[i].pos)) 183 return -EINVAL; 184 185 used_lanes |= 1 << lanes->data[i].pos; 186 csi2->phy->used_data_lanes++; 187 } 188 189 if (lanes->clk.pol > 1 || 190 lanes->clk.pos > (csi2->phy->max_data_lanes + 1)) 191 return -EINVAL; 192 193 if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos)) 194 return -EINVAL; 195 196 csi2_ddrclk_khz = pipe->external_rate / 1000 197 / (2 * csi2->phy->used_data_lanes) 198 * pipe->external_bpp; 199 200 /* 201 * THS_TERM: Programmed value = ceil(12.5 ns/DDRClk period) - 1. 202 * THS_SETTLE: Programmed value = ceil(90 ns/DDRClk period) + 3. 203 */ 204 csi2phy.ths_term = DIV_ROUND_UP(25 * csi2_ddrclk_khz, 2000000) - 1; 205 csi2phy.ths_settle = DIV_ROUND_UP(90 * csi2_ddrclk_khz, 1000000) + 3; 206 csi2phy.tclk_term = TCLK_TERM; 207 csi2phy.tclk_miss = TCLK_MISS; 208 csi2phy.tclk_settle = TCLK_SETTLE; 209 210 mutex_lock(&csi2->phy->mutex); 211 csi2->phy->dphy = csi2phy; 212 csi2->phy->lanes = *lanes; 213 mutex_unlock(&csi2->phy->mutex); 214 215 return 0; 216 } 217 218 int omap4iss_csiphy_acquire(struct iss_csiphy *phy) 219 { 220 int rval; 221 222 mutex_lock(&phy->mutex); 223 224 rval = omap4iss_csi2_reset(phy->csi2); 225 if (rval) 226 goto done; 227 228 csiphy_dphy_config(phy); 229 csiphy_lanes_config(phy); 230 231 rval = csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_ON); 232 if (rval) 233 goto done; 234 235 phy->phy_in_use = 1; 236 237 done: 238 mutex_unlock(&phy->mutex); 239 return rval; 240 } 241 242 void omap4iss_csiphy_release(struct iss_csiphy *phy) 243 { 244 mutex_lock(&phy->mutex); 245 if (phy->phy_in_use) { 246 csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_OFF); 247 phy->phy_in_use = 0; 248 } 249 mutex_unlock(&phy->mutex); 250 } 251 252 /* 253 * omap4iss_csiphy_init - Initialize the CSI PHY frontends 254 */ 255 int omap4iss_csiphy_init(struct iss_device *iss) 256 { 257 struct iss_csiphy *phy1 = &iss->csiphy1; 258 struct iss_csiphy *phy2 = &iss->csiphy2; 259 260 phy1->iss = iss; 261 phy1->csi2 = &iss->csi2a; 262 phy1->max_data_lanes = ISS_CSIPHY1_NUM_DATA_LANES; 263 phy1->used_data_lanes = 0; 264 phy1->cfg_regs = OMAP4_ISS_MEM_CSI2_A_REGS1; 265 phy1->phy_regs = OMAP4_ISS_MEM_CAMERARX_CORE1; 266 mutex_init(&phy1->mutex); 267 268 phy2->iss = iss; 269 phy2->csi2 = &iss->csi2b; 270 phy2->max_data_lanes = ISS_CSIPHY2_NUM_DATA_LANES; 271 phy2->used_data_lanes = 0; 272 phy2->cfg_regs = OMAP4_ISS_MEM_CSI2_B_REGS1; 273 phy2->phy_regs = OMAP4_ISS_MEM_CAMERARX_CORE2; 274 mutex_init(&phy2->mutex); 275 276 return 0; 277 } 278