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