Commit | Line | Data |
---|---|---|
121e9f1c LP |
1 | /* |
2 | * ispcsiphy.c | |
3 | * | |
4 | * TI OMAP3 ISP - CSI PHY module | |
5 | * | |
6 | * Copyright (C) 2010 Nokia Corporation | |
7 | * Copyright (C) 2009 Texas Instruments, Inc. | |
8 | * | |
9 | * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | |
10 | * Sakari Ailus <sakari.ailus@iki.fi> | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License version 2 as | |
14 | * published by the Free Software Foundation. | |
121e9f1c LP |
15 | */ |
16 | ||
17 | #include <linux/delay.h> | |
18 | #include <linux/device.h> | |
503596a1 | 19 | #include <linux/regmap.h> |
121e9f1c LP |
20 | #include <linux/regulator/consumer.h> |
21 | ||
22 | #include "isp.h" | |
23 | #include "ispreg.h" | |
24 | #include "ispcsiphy.h" | |
25 | ||
f7c3f5ce LP |
26 | static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, |
27 | enum isp_interface_type iface, | |
ec51e960 SA |
28 | bool ccp2_strobe) |
29 | { | |
503596a1 | 30 | u32 reg; |
ec51e960 SA |
31 | u32 shift, mode; |
32 | ||
503596a1 SA |
33 | regmap_read(phy->isp->syscon, phy->isp->syscon_offset, ®); |
34 | ||
ec51e960 | 35 | switch (iface) { |
f7c3f5ce LP |
36 | default: |
37 | /* Should not happen in practice, but let's keep the compiler happy. */ | |
ec51e960 SA |
38 | case ISP_INTERFACE_CCP2B_PHY1: |
39 | reg &= ~OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2; | |
40 | shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT; | |
41 | break; | |
42 | case ISP_INTERFACE_CSI2C_PHY1: | |
43 | shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT; | |
44 | mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_DPHY; | |
45 | break; | |
46 | case ISP_INTERFACE_CCP2B_PHY2: | |
47 | reg |= OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2; | |
48 | shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY2_SHIFT; | |
49 | break; | |
50 | case ISP_INTERFACE_CSI2A_PHY2: | |
51 | shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY2_SHIFT; | |
52 | mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_DPHY; | |
53 | break; | |
54 | } | |
55 | ||
56 | /* Select data/clock or data/strobe mode for CCP2 */ | |
f7c3f5ce LP |
57 | if (iface == ISP_INTERFACE_CCP2B_PHY1 || |
58 | iface == ISP_INTERFACE_CCP2B_PHY2) { | |
ec51e960 SA |
59 | if (ccp2_strobe) |
60 | mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_STROBE; | |
61 | else | |
62 | mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_CLOCK; | |
63 | } | |
64 | ||
65 | reg &= ~(OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_MASK << shift); | |
66 | reg |= mode << shift; | |
67 | ||
503596a1 | 68 | regmap_write(phy->isp->syscon, phy->isp->syscon_offset, reg); |
ec51e960 SA |
69 | } |
70 | ||
71 | static void csiphy_routing_cfg_3430(struct isp_csiphy *phy, u32 iface, bool on, | |
72 | bool ccp2_strobe) | |
73 | { | |
74 | u32 csirxfe = OMAP343X_CONTROL_CSIRXFE_PWRDNZ | |
75 | | OMAP343X_CONTROL_CSIRXFE_RESET; | |
76 | ||
77 | /* Only the CCP2B on PHY1 is configurable. */ | |
78 | if (iface != ISP_INTERFACE_CCP2B_PHY1) | |
79 | return; | |
80 | ||
81 | if (!on) { | |
503596a1 | 82 | regmap_write(phy->isp->syscon, phy->isp->syscon_offset, 0); |
ec51e960 SA |
83 | return; |
84 | } | |
85 | ||
86 | if (ccp2_strobe) | |
87 | csirxfe |= OMAP343X_CONTROL_CSIRXFE_SELFORM; | |
88 | ||
503596a1 | 89 | regmap_write(phy->isp->syscon, phy->isp->syscon_offset, csirxfe); |
ec51e960 SA |
90 | } |
91 | ||
92 | /* | |
93 | * Configure OMAP 3 CSI PHY routing. | |
94 | * @phy: relevant phy device | |
95 | * @iface: ISP_INTERFACE_* | |
96 | * @on: power on or off | |
97 | * @ccp2_strobe: false: data/clock, true: data/strobe | |
98 | * | |
99 | * Note that the underlying routing configuration registers are part of the | |
100 | * control (SCM) register space and part of the CORE power domain on both 3430 | |
101 | * and 3630, so they will not hold their contents in off-mode. This isn't an | |
102 | * issue since the MPU power domain is forced on whilst the ISP is in use. | |
103 | */ | |
f7c3f5ce LP |
104 | static void csiphy_routing_cfg(struct isp_csiphy *phy, |
105 | enum isp_interface_type iface, bool on, | |
ec51e960 SA |
106 | bool ccp2_strobe) |
107 | { | |
503596a1 | 108 | if (phy->isp->phy_type == ISP_PHY_TYPE_3630 && on) |
ec51e960 | 109 | return csiphy_routing_cfg_3630(phy, iface, ccp2_strobe); |
503596a1 | 110 | if (phy->isp->phy_type == ISP_PHY_TYPE_3430) |
ec51e960 SA |
111 | return csiphy_routing_cfg_3430(phy, iface, on, ccp2_strobe); |
112 | } | |
113 | ||
121e9f1c LP |
114 | /* |
115 | * csiphy_power_autoswitch_enable | |
116 | * @enable: Sets or clears the autoswitch function enable flag. | |
117 | */ | |
118 | static void csiphy_power_autoswitch_enable(struct isp_csiphy *phy, bool enable) | |
119 | { | |
120 | isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG, | |
121 | ISPCSI2_PHY_CFG_PWR_AUTO, | |
122 | enable ? ISPCSI2_PHY_CFG_PWR_AUTO : 0); | |
123 | } | |
124 | ||
125 | /* | |
126 | * csiphy_set_power | |
127 | * @power: Power state to be set. | |
128 | * | |
129 | * Returns 0 if successful, or -EBUSY if the retry count is exceeded. | |
130 | */ | |
131 | static int csiphy_set_power(struct isp_csiphy *phy, u32 power) | |
132 | { | |
133 | u32 reg; | |
134 | u8 retry_count; | |
135 | ||
136 | isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG, | |
137 | ISPCSI2_PHY_CFG_PWR_CMD_MASK, power); | |
138 | ||
139 | retry_count = 0; | |
140 | do { | |
141 | udelay(50); | |
142 | reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG) & | |
143 | ISPCSI2_PHY_CFG_PWR_STATUS_MASK; | |
144 | ||
145 | if (reg != power >> 2) | |
146 | retry_count++; | |
147 | ||
148 | } while ((reg != power >> 2) && (retry_count < 100)); | |
149 | ||
150 | if (retry_count == 100) { | |
4feca39b | 151 | dev_err(phy->isp->dev, "CSI2 CIO set power failed!\n"); |
121e9f1c LP |
152 | return -EBUSY; |
153 | } | |
154 | ||
155 | return 0; | |
156 | } | |
157 | ||
158 | /* | |
a7b21061 | 159 | * TCLK values are OK at their reset values |
121e9f1c | 160 | */ |
a7b21061 SA |
161 | #define TCLK_TERM 0 |
162 | #define TCLK_MISS 1 | |
163 | #define TCLK_SETTLE 14 | |
121e9f1c | 164 | |
a7b21061 | 165 | static int omap3isp_csiphy_config(struct isp_csiphy *phy) |
121e9f1c | 166 | { |
a7b21061 SA |
167 | struct isp_csi2_device *csi2 = phy->csi2; |
168 | struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); | |
68908747 | 169 | struct isp_bus_cfg *buscfg = pipe->external->host_priv; |
a7b21061 SA |
170 | struct isp_csiphy_lanes_cfg *lanes; |
171 | int csi2_ddrclk_khz; | |
121e9f1c LP |
172 | unsigned int used_lanes = 0; |
173 | unsigned int i; | |
a7b21061 SA |
174 | u32 reg; |
175 | ||
da7f3843 SA |
176 | if (!buscfg) { |
177 | struct isp_async_subdev *isd = | |
178 | container_of(pipe->external->asd, | |
179 | struct isp_async_subdev, asd); | |
180 | buscfg = &isd->bus; | |
181 | } | |
182 | ||
68908747 SA |
183 | if (buscfg->interface == ISP_INTERFACE_CCP2B_PHY1 |
184 | || buscfg->interface == ISP_INTERFACE_CCP2B_PHY2) | |
185 | lanes = &buscfg->bus.ccp2.lanecfg; | |
a7b21061 | 186 | else |
68908747 | 187 | lanes = &buscfg->bus.csi2.lanecfg; |
121e9f1c LP |
188 | |
189 | /* Clock and data lanes verification */ | |
190 | for (i = 0; i < phy->num_data_lanes; i++) { | |
191 | if (lanes->data[i].pol > 1 || lanes->data[i].pos > 3) | |
192 | return -EINVAL; | |
193 | ||
194 | if (used_lanes & (1 << lanes->data[i].pos)) | |
195 | return -EINVAL; | |
196 | ||
197 | used_lanes |= 1 << lanes->data[i].pos; | |
198 | } | |
199 | ||
200 | if (lanes->clk.pol > 1 || lanes->clk.pos > 3) | |
201 | return -EINVAL; | |
202 | ||
203 | if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos)) | |
204 | return -EINVAL; | |
205 | ||
a7b21061 SA |
206 | /* |
207 | * The PHY configuration is lost in off mode, that's not an | |
208 | * issue since the MPU power domain is forced on whilst the | |
209 | * ISP is in use. | |
210 | */ | |
68908747 SA |
211 | csiphy_routing_cfg(phy, buscfg->interface, true, |
212 | buscfg->bus.ccp2.phy_layer); | |
a7b21061 SA |
213 | |
214 | /* DPHY timing configuration */ | |
215 | /* CSI-2 is DDR and we only count used lanes. */ | |
216 | csi2_ddrclk_khz = pipe->external_rate / 1000 | |
217 | / (2 * hweight32(used_lanes)) * pipe->external_width; | |
218 | ||
219 | reg = isp_reg_readl(csi2->isp, phy->phy_regs, ISPCSIPHY_REG0); | |
220 | ||
221 | reg &= ~(ISPCSIPHY_REG0_THS_TERM_MASK | | |
222 | ISPCSIPHY_REG0_THS_SETTLE_MASK); | |
223 | /* THS_TERM: Programmed value = ceil(12.5 ns/DDRClk period) - 1. */ | |
224 | reg |= (DIV_ROUND_UP(25 * csi2_ddrclk_khz, 2000000) - 1) | |
225 | << ISPCSIPHY_REG0_THS_TERM_SHIFT; | |
226 | /* THS_SETTLE: Programmed value = ceil(90 ns/DDRClk period) + 3. */ | |
227 | reg |= (DIV_ROUND_UP(90 * csi2_ddrclk_khz, 1000000) + 3) | |
228 | << ISPCSIPHY_REG0_THS_SETTLE_SHIFT; | |
229 | ||
230 | isp_reg_writel(csi2->isp, reg, phy->phy_regs, ISPCSIPHY_REG0); | |
231 | ||
232 | reg = isp_reg_readl(csi2->isp, phy->phy_regs, ISPCSIPHY_REG1); | |
233 | ||
234 | reg &= ~(ISPCSIPHY_REG1_TCLK_TERM_MASK | | |
235 | ISPCSIPHY_REG1_TCLK_MISS_MASK | | |
236 | ISPCSIPHY_REG1_TCLK_SETTLE_MASK); | |
237 | reg |= TCLK_TERM << ISPCSIPHY_REG1_TCLK_TERM_SHIFT; | |
238 | reg |= TCLK_MISS << ISPCSIPHY_REG1_TCLK_MISS_SHIFT; | |
239 | reg |= TCLK_SETTLE << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT; | |
240 | ||
241 | isp_reg_writel(csi2->isp, reg, phy->phy_regs, ISPCSIPHY_REG1); | |
242 | ||
243 | /* DPHY lane configuration */ | |
244 | reg = isp_reg_readl(csi2->isp, phy->cfg_regs, ISPCSI2_PHY_CFG); | |
245 | ||
246 | for (i = 0; i < phy->num_data_lanes; i++) { | |
247 | reg &= ~(ISPCSI2_PHY_CFG_DATA_POL_MASK(i + 1) | | |
248 | ISPCSI2_PHY_CFG_DATA_POSITION_MASK(i + 1)); | |
249 | reg |= (lanes->data[i].pol << | |
250 | ISPCSI2_PHY_CFG_DATA_POL_SHIFT(i + 1)); | |
251 | reg |= (lanes->data[i].pos << | |
252 | ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(i + 1)); | |
253 | } | |
254 | ||
255 | reg &= ~(ISPCSI2_PHY_CFG_CLOCK_POL_MASK | | |
256 | ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK); | |
257 | reg |= lanes->clk.pol << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT; | |
258 | reg |= lanes->clk.pos << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT; | |
259 | ||
260 | isp_reg_writel(csi2->isp, reg, phy->cfg_regs, ISPCSI2_PHY_CFG); | |
121e9f1c LP |
261 | |
262 | return 0; | |
263 | } | |
264 | ||
265 | int omap3isp_csiphy_acquire(struct isp_csiphy *phy) | |
266 | { | |
267 | int rval; | |
268 | ||
269 | if (phy->vdd == NULL) { | |
270 | dev_err(phy->isp->dev, "Power regulator for CSI PHY not " | |
271 | "available\n"); | |
272 | return -ENODEV; | |
273 | } | |
274 | ||
275 | mutex_lock(&phy->mutex); | |
276 | ||
277 | rval = regulator_enable(phy->vdd); | |
278 | if (rval < 0) | |
279 | goto done; | |
280 | ||
ca7f4a38 SA |
281 | rval = omap3isp_csi2_reset(phy->csi2); |
282 | if (rval < 0) | |
283 | goto done; | |
121e9f1c | 284 | |
a7b21061 SA |
285 | rval = omap3isp_csiphy_config(phy); |
286 | if (rval < 0) | |
287 | goto done; | |
121e9f1c LP |
288 | |
289 | rval = csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_ON); | |
290 | if (rval) { | |
291 | regulator_disable(phy->vdd); | |
292 | goto done; | |
293 | } | |
294 | ||
295 | csiphy_power_autoswitch_enable(phy, true); | |
296 | phy->phy_in_use = 1; | |
297 | ||
298 | done: | |
299 | mutex_unlock(&phy->mutex); | |
300 | return rval; | |
301 | } | |
302 | ||
303 | void omap3isp_csiphy_release(struct isp_csiphy *phy) | |
304 | { | |
305 | mutex_lock(&phy->mutex); | |
306 | if (phy->phy_in_use) { | |
a7b21061 SA |
307 | struct isp_csi2_device *csi2 = phy->csi2; |
308 | struct isp_pipeline *pipe = | |
309 | to_isp_pipeline(&csi2->subdev.entity); | |
68908747 | 310 | struct isp_bus_cfg *buscfg = pipe->external->host_priv; |
a7b21061 | 311 | |
68908747 SA |
312 | csiphy_routing_cfg(phy, buscfg->interface, false, |
313 | buscfg->bus.ccp2.phy_layer); | |
121e9f1c LP |
314 | csiphy_power_autoswitch_enable(phy, false); |
315 | csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_OFF); | |
316 | regulator_disable(phy->vdd); | |
317 | phy->phy_in_use = 0; | |
318 | } | |
319 | mutex_unlock(&phy->mutex); | |
320 | } | |
321 | ||
322 | /* | |
323 | * omap3isp_csiphy_init - Initialize the CSI PHY frontends | |
324 | */ | |
325 | int omap3isp_csiphy_init(struct isp_device *isp) | |
326 | { | |
327 | struct isp_csiphy *phy1 = &isp->isp_csiphy1; | |
328 | struct isp_csiphy *phy2 = &isp->isp_csiphy2; | |
329 | ||
121e9f1c LP |
330 | phy2->isp = isp; |
331 | phy2->csi2 = &isp->isp_csi2a; | |
332 | phy2->num_data_lanes = ISP_CSIPHY2_NUM_DATA_LANES; | |
333 | phy2->cfg_regs = OMAP3_ISP_IOMEM_CSI2A_REGS1; | |
334 | phy2->phy_regs = OMAP3_ISP_IOMEM_CSIPHY2; | |
335 | mutex_init(&phy2->mutex); | |
336 | ||
337 | if (isp->revision == ISP_REVISION_15_0) { | |
338 | phy1->isp = isp; | |
339 | phy1->csi2 = &isp->isp_csi2c; | |
340 | phy1->num_data_lanes = ISP_CSIPHY1_NUM_DATA_LANES; | |
341 | phy1->cfg_regs = OMAP3_ISP_IOMEM_CSI2C_REGS1; | |
342 | phy1->phy_regs = OMAP3_ISP_IOMEM_CSIPHY1; | |
343 | mutex_init(&phy1->mutex); | |
344 | } | |
345 | ||
346 | return 0; | |
347 | } |