diff options
Diffstat (limited to 'drivers/media/video/omap3isp/ispcsiphy.c')
-rw-r--r-- | drivers/media/video/omap3isp/ispcsiphy.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/drivers/media/video/omap3isp/ispcsiphy.c b/drivers/media/video/omap3isp/ispcsiphy.c new file mode 100644 index 00000000000..5be37ce7d0c --- /dev/null +++ b/drivers/media/video/omap3isp/ispcsiphy.c | |||
@@ -0,0 +1,247 @@ | |||
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. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | #include <linux/delay.h> | ||
28 | #include <linux/device.h> | ||
29 | #include <linux/regulator/consumer.h> | ||
30 | |||
31 | #include "isp.h" | ||
32 | #include "ispreg.h" | ||
33 | #include "ispcsiphy.h" | ||
34 | |||
35 | /* | ||
36 | * csiphy_lanes_config - Configuration of CSIPHY lanes. | ||
37 | * | ||
38 | * Updates HW configuration. | ||
39 | * Called with phy->mutex taken. | ||
40 | */ | ||
41 | static void csiphy_lanes_config(struct isp_csiphy *phy) | ||
42 | { | ||
43 | unsigned int i; | ||
44 | u32 reg; | ||
45 | |||
46 | reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG); | ||
47 | |||
48 | for (i = 0; i < phy->num_data_lanes; i++) { | ||
49 | reg &= ~(ISPCSI2_PHY_CFG_DATA_POL_MASK(i + 1) | | ||
50 | ISPCSI2_PHY_CFG_DATA_POSITION_MASK(i + 1)); | ||
51 | reg |= (phy->lanes.data[i].pol << | ||
52 | ISPCSI2_PHY_CFG_DATA_POL_SHIFT(i + 1)); | ||
53 | reg |= (phy->lanes.data[i].pos << | ||
54 | ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(i + 1)); | ||
55 | } | ||
56 | |||
57 | reg &= ~(ISPCSI2_PHY_CFG_CLOCK_POL_MASK | | ||
58 | ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK); | ||
59 | reg |= phy->lanes.clk.pol << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT; | ||
60 | reg |= phy->lanes.clk.pos << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT; | ||
61 | |||
62 | isp_reg_writel(phy->isp, reg, phy->cfg_regs, ISPCSI2_PHY_CFG); | ||
63 | } | ||
64 | |||
65 | /* | ||
66 | * csiphy_power_autoswitch_enable | ||
67 | * @enable: Sets or clears the autoswitch function enable flag. | ||
68 | */ | ||
69 | static void csiphy_power_autoswitch_enable(struct isp_csiphy *phy, bool enable) | ||
70 | { | ||
71 | isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG, | ||
72 | ISPCSI2_PHY_CFG_PWR_AUTO, | ||
73 | enable ? ISPCSI2_PHY_CFG_PWR_AUTO : 0); | ||
74 | } | ||
75 | |||
76 | /* | ||
77 | * csiphy_set_power | ||
78 | * @power: Power state to be set. | ||
79 | * | ||
80 | * Returns 0 if successful, or -EBUSY if the retry count is exceeded. | ||
81 | */ | ||
82 | static int csiphy_set_power(struct isp_csiphy *phy, u32 power) | ||
83 | { | ||
84 | u32 reg; | ||
85 | u8 retry_count; | ||
86 | |||
87 | isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG, | ||
88 | ISPCSI2_PHY_CFG_PWR_CMD_MASK, power); | ||
89 | |||
90 | retry_count = 0; | ||
91 | do { | ||
92 | udelay(50); | ||
93 | reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG) & | ||
94 | ISPCSI2_PHY_CFG_PWR_STATUS_MASK; | ||
95 | |||
96 | if (reg != power >> 2) | ||
97 | retry_count++; | ||
98 | |||
99 | } while ((reg != power >> 2) && (retry_count < 100)); | ||
100 | |||
101 | if (retry_count == 100) { | ||
102 | printk(KERN_ERR "CSI2 CIO set power failed!\n"); | ||
103 | return -EBUSY; | ||
104 | } | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * csiphy_dphy_config - Configure CSI2 D-PHY parameters. | ||
111 | * | ||
112 | * Called with phy->mutex taken. | ||
113 | */ | ||
114 | static void csiphy_dphy_config(struct isp_csiphy *phy) | ||
115 | { | ||
116 | u32 reg; | ||
117 | |||
118 | /* Set up ISPCSIPHY_REG0 */ | ||
119 | reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG0); | ||
120 | |||
121 | reg &= ~(ISPCSIPHY_REG0_THS_TERM_MASK | | ||
122 | ISPCSIPHY_REG0_THS_SETTLE_MASK); | ||
123 | reg |= phy->dphy.ths_term << ISPCSIPHY_REG0_THS_TERM_SHIFT; | ||
124 | reg |= phy->dphy.ths_settle << ISPCSIPHY_REG0_THS_SETTLE_SHIFT; | ||
125 | |||
126 | isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG0); | ||
127 | |||
128 | /* Set up ISPCSIPHY_REG1 */ | ||
129 | reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG1); | ||
130 | |||
131 | reg &= ~(ISPCSIPHY_REG1_TCLK_TERM_MASK | | ||
132 | ISPCSIPHY_REG1_TCLK_MISS_MASK | | ||
133 | ISPCSIPHY_REG1_TCLK_SETTLE_MASK); | ||
134 | reg |= phy->dphy.tclk_term << ISPCSIPHY_REG1_TCLK_TERM_SHIFT; | ||
135 | reg |= phy->dphy.tclk_miss << ISPCSIPHY_REG1_TCLK_MISS_SHIFT; | ||
136 | reg |= phy->dphy.tclk_settle << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT; | ||
137 | |||
138 | isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG1); | ||
139 | } | ||
140 | |||
141 | static int csiphy_config(struct isp_csiphy *phy, | ||
142 | struct isp_csiphy_dphy_cfg *dphy, | ||
143 | struct isp_csiphy_lanes_cfg *lanes) | ||
144 | { | ||
145 | unsigned int used_lanes = 0; | ||
146 | unsigned int i; | ||
147 | |||
148 | /* Clock and data lanes verification */ | ||
149 | for (i = 0; i < phy->num_data_lanes; i++) { | ||
150 | if (lanes->data[i].pol > 1 || lanes->data[i].pos > 3) | ||
151 | return -EINVAL; | ||
152 | |||
153 | if (used_lanes & (1 << lanes->data[i].pos)) | ||
154 | return -EINVAL; | ||
155 | |||
156 | used_lanes |= 1 << lanes->data[i].pos; | ||
157 | } | ||
158 | |||
159 | if (lanes->clk.pol > 1 || lanes->clk.pos > 3) | ||
160 | return -EINVAL; | ||
161 | |||
162 | if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos)) | ||
163 | return -EINVAL; | ||
164 | |||
165 | mutex_lock(&phy->mutex); | ||
166 | phy->dphy = *dphy; | ||
167 | phy->lanes = *lanes; | ||
168 | mutex_unlock(&phy->mutex); | ||
169 | |||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | int omap3isp_csiphy_acquire(struct isp_csiphy *phy) | ||
174 | { | ||
175 | int rval; | ||
176 | |||
177 | if (phy->vdd == NULL) { | ||
178 | dev_err(phy->isp->dev, "Power regulator for CSI PHY not " | ||
179 | "available\n"); | ||
180 | return -ENODEV; | ||
181 | } | ||
182 | |||
183 | mutex_lock(&phy->mutex); | ||
184 | |||
185 | rval = regulator_enable(phy->vdd); | ||
186 | if (rval < 0) | ||
187 | goto done; | ||
188 | |||
189 | omap3isp_csi2_reset(phy->csi2); | ||
190 | |||
191 | csiphy_dphy_config(phy); | ||
192 | csiphy_lanes_config(phy); | ||
193 | |||
194 | rval = csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_ON); | ||
195 | if (rval) { | ||
196 | regulator_disable(phy->vdd); | ||
197 | goto done; | ||
198 | } | ||
199 | |||
200 | csiphy_power_autoswitch_enable(phy, true); | ||
201 | phy->phy_in_use = 1; | ||
202 | |||
203 | done: | ||
204 | mutex_unlock(&phy->mutex); | ||
205 | return rval; | ||
206 | } | ||
207 | |||
208 | void omap3isp_csiphy_release(struct isp_csiphy *phy) | ||
209 | { | ||
210 | mutex_lock(&phy->mutex); | ||
211 | if (phy->phy_in_use) { | ||
212 | csiphy_power_autoswitch_enable(phy, false); | ||
213 | csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_OFF); | ||
214 | regulator_disable(phy->vdd); | ||
215 | phy->phy_in_use = 0; | ||
216 | } | ||
217 | mutex_unlock(&phy->mutex); | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * omap3isp_csiphy_init - Initialize the CSI PHY frontends | ||
222 | */ | ||
223 | int omap3isp_csiphy_init(struct isp_device *isp) | ||
224 | { | ||
225 | struct isp_csiphy *phy1 = &isp->isp_csiphy1; | ||
226 | struct isp_csiphy *phy2 = &isp->isp_csiphy2; | ||
227 | |||
228 | isp->platform_cb.csiphy_config = csiphy_config; | ||
229 | |||
230 | phy2->isp = isp; | ||
231 | phy2->csi2 = &isp->isp_csi2a; | ||
232 | phy2->num_data_lanes = ISP_CSIPHY2_NUM_DATA_LANES; | ||
233 | phy2->cfg_regs = OMAP3_ISP_IOMEM_CSI2A_REGS1; | ||
234 | phy2->phy_regs = OMAP3_ISP_IOMEM_CSIPHY2; | ||
235 | mutex_init(&phy2->mutex); | ||
236 | |||
237 | if (isp->revision == ISP_REVISION_15_0) { | ||
238 | phy1->isp = isp; | ||
239 | phy1->csi2 = &isp->isp_csi2c; | ||
240 | phy1->num_data_lanes = ISP_CSIPHY1_NUM_DATA_LANES; | ||
241 | phy1->cfg_regs = OMAP3_ISP_IOMEM_CSI2C_REGS1; | ||
242 | phy1->phy_regs = OMAP3_ISP_IOMEM_CSIPHY1; | ||
243 | mutex_init(&phy1->mutex); | ||
244 | } | ||
245 | |||
246 | return 0; | ||
247 | } | ||