4 * TI OMAP3 ISP - CSI PHY module
6 * Copyright (C) 2010 Nokia Corporation
7 * Copyright (C) 2009 Texas Instruments, Inc.
9 * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
10 * Sakari Ailus <sakari.ailus@iki.fi>
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.
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.
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
27 #include <linux/delay.h>
28 #include <linux/device.h>
29 #include <linux/regulator/consumer.h>
33 #include "ispcsiphy.h"
36 * csiphy_lanes_config - Configuration of CSIPHY lanes.
38 * Updates HW configuration.
39 * Called with phy->mutex taken.
41 static void csiphy_lanes_config(struct isp_csiphy
*phy
)
46 reg
= isp_reg_readl(phy
->isp
, phy
->cfg_regs
, ISPCSI2_PHY_CFG
);
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));
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
;
62 isp_reg_writel(phy
->isp
, reg
, phy
->cfg_regs
, ISPCSI2_PHY_CFG
);
66 * csiphy_power_autoswitch_enable
67 * @enable: Sets or clears the autoswitch function enable flag.
69 static void csiphy_power_autoswitch_enable(struct isp_csiphy
*phy
, bool enable
)
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);
78 * @power: Power state to be set.
80 * Returns 0 if successful, or -EBUSY if the retry count is exceeded.
82 static int csiphy_set_power(struct isp_csiphy
*phy
, u32 power
)
87 isp_reg_clr_set(phy
->isp
, phy
->cfg_regs
, ISPCSI2_PHY_CFG
,
88 ISPCSI2_PHY_CFG_PWR_CMD_MASK
, power
);
93 reg
= isp_reg_readl(phy
->isp
, phy
->cfg_regs
, ISPCSI2_PHY_CFG
) &
94 ISPCSI2_PHY_CFG_PWR_STATUS_MASK
;
96 if (reg
!= power
>> 2)
99 } while ((reg
!= power
>> 2) && (retry_count
< 100));
101 if (retry_count
== 100) {
102 printk(KERN_ERR
"CSI2 CIO set power failed!\n");
110 * csiphy_dphy_config - Configure CSI2 D-PHY parameters.
112 * Called with phy->mutex taken.
114 static void csiphy_dphy_config(struct isp_csiphy
*phy
)
118 /* Set up ISPCSIPHY_REG0 */
119 reg
= isp_reg_readl(phy
->isp
, phy
->phy_regs
, ISPCSIPHY_REG0
);
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
;
126 isp_reg_writel(phy
->isp
, reg
, phy
->phy_regs
, ISPCSIPHY_REG0
);
128 /* Set up ISPCSIPHY_REG1 */
129 reg
= isp_reg_readl(phy
->isp
, phy
->phy_regs
, ISPCSIPHY_REG1
);
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
;
138 isp_reg_writel(phy
->isp
, reg
, phy
->phy_regs
, ISPCSIPHY_REG1
);
141 static int csiphy_config(struct isp_csiphy
*phy
,
142 struct isp_csiphy_dphy_cfg
*dphy
,
143 struct isp_csiphy_lanes_cfg
*lanes
)
145 unsigned int used_lanes
= 0;
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)
153 if (used_lanes
& (1 << lanes
->data
[i
].pos
))
156 used_lanes
|= 1 << lanes
->data
[i
].pos
;
159 if (lanes
->clk
.pol
> 1 || lanes
->clk
.pos
> 3)
162 if (lanes
->clk
.pos
== 0 || used_lanes
& (1 << lanes
->clk
.pos
))
165 mutex_lock(&phy
->mutex
);
168 mutex_unlock(&phy
->mutex
);
173 int omap3isp_csiphy_acquire(struct isp_csiphy
*phy
)
177 if (phy
->vdd
== NULL
) {
178 dev_err(phy
->isp
->dev
, "Power regulator for CSI PHY not "
183 mutex_lock(&phy
->mutex
);
185 rval
= regulator_enable(phy
->vdd
);
189 omap3isp_csi2_reset(phy
->csi2
);
191 csiphy_dphy_config(phy
);
192 csiphy_lanes_config(phy
);
194 rval
= csiphy_set_power(phy
, ISPCSI2_PHY_CFG_PWR_CMD_ON
);
196 regulator_disable(phy
->vdd
);
200 csiphy_power_autoswitch_enable(phy
, true);
204 mutex_unlock(&phy
->mutex
);
208 void omap3isp_csiphy_release(struct isp_csiphy
*phy
)
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
);
217 mutex_unlock(&phy
->mutex
);
221 * omap3isp_csiphy_init - Initialize the CSI PHY frontends
223 int omap3isp_csiphy_init(struct isp_device
*isp
)
225 struct isp_csiphy
*phy1
= &isp
->isp_csiphy1
;
226 struct isp_csiphy
*phy2
= &isp
->isp_csiphy2
;
228 isp
->platform_cb
.csiphy_config
= csiphy_config
;
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
);
237 if (isp
->revision
== ISP_REVISION_15_0
) {
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
);