2 * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
4 * Copyright (C) 2013,2016 Samsung Electronics Co., Ltd.
5 * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #include <linux/err.h>
14 #include <linux/kernel.h>
15 #include <linux/mfd/syscon/exynos4-pmu.h>
16 #include <linux/mfd/syscon/exynos5-pmu.h>
17 #include <linux/module.h>
19 #include <linux/of_address.h>
20 #include <linux/of_device.h>
21 #include <linux/phy/phy.h>
22 #include <linux/regmap.h>
23 #include <linux/spinlock.h>
24 #include <linux/mfd/syscon.h>
26 enum exynos_mipi_phy_id
{
27 EXYNOS_MIPI_PHY_ID_NONE
= -1,
28 EXYNOS_MIPI_PHY_ID_CSIS0
,
29 EXYNOS_MIPI_PHY_ID_DSIM0
,
30 EXYNOS_MIPI_PHY_ID_CSIS1
,
31 EXYNOS_MIPI_PHY_ID_DSIM1
,
32 EXYNOS_MIPI_PHY_ID_CSIS2
,
36 enum exynos_mipi_phy_regmap_id
{
37 EXYNOS_MIPI_REGMAP_PMU
,
38 EXYNOS_MIPI_REGMAP_DISP
,
39 EXYNOS_MIPI_REGMAP_CAM0
,
40 EXYNOS_MIPI_REGMAP_CAM1
,
41 EXYNOS_MIPI_REGMAPS_NUM
44 struct mipi_phy_device_desc
{
47 const char *regmap_names
[EXYNOS_MIPI_REGMAPS_NUM
];
48 struct exynos_mipi_phy_desc
{
49 enum exynos_mipi_phy_id coupled_phy_id
;
51 unsigned int enable_reg
;
52 enum exynos_mipi_phy_regmap_id enable_map
;
54 unsigned int resetn_reg
;
55 enum exynos_mipi_phy_regmap_id resetn_map
;
56 } phys
[EXYNOS_MIPI_PHYS_NUM
];
59 static const struct mipi_phy_device_desc s5pv210_mipi_phy
= {
61 .regmap_names
= {"syscon"},
65 /* EXYNOS_MIPI_PHY_ID_CSIS0 */
66 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_DSIM0
,
67 .enable_val
= EXYNOS4_MIPI_PHY_ENABLE
,
68 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(0),
69 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
70 .resetn_val
= EXYNOS4_MIPI_PHY_SRESETN
,
71 .resetn_reg
= EXYNOS4_MIPI_PHY_CONTROL(0),
72 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
74 /* EXYNOS_MIPI_PHY_ID_DSIM0 */
75 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_CSIS0
,
76 .enable_val
= EXYNOS4_MIPI_PHY_ENABLE
,
77 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(0),
78 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
79 .resetn_val
= EXYNOS4_MIPI_PHY_MRESETN
,
80 .resetn_reg
= EXYNOS4_MIPI_PHY_CONTROL(0),
81 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
83 /* EXYNOS_MIPI_PHY_ID_CSIS1 */
84 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_DSIM1
,
85 .enable_val
= EXYNOS4_MIPI_PHY_ENABLE
,
86 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(1),
87 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
88 .resetn_val
= EXYNOS4_MIPI_PHY_SRESETN
,
89 .resetn_reg
= EXYNOS4_MIPI_PHY_CONTROL(1),
90 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
92 /* EXYNOS_MIPI_PHY_ID_DSIM1 */
93 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_CSIS1
,
94 .enable_val
= EXYNOS4_MIPI_PHY_ENABLE
,
95 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(1),
96 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
97 .resetn_val
= EXYNOS4_MIPI_PHY_MRESETN
,
98 .resetn_reg
= EXYNOS4_MIPI_PHY_CONTROL(1),
99 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
104 static const struct mipi_phy_device_desc exynos5420_mipi_phy
= {
106 .regmap_names
= {"syscon"},
110 /* EXYNOS_MIPI_PHY_ID_CSIS0 */
111 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_DSIM0
,
112 .enable_val
= EXYNOS5_PHY_ENABLE
,
113 .enable_reg
= EXYNOS5420_MIPI_PHY0_CONTROL
,
114 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
115 .resetn_val
= EXYNOS5_MIPI_PHY_S_RESETN
,
116 .resetn_reg
= EXYNOS5420_MIPI_PHY0_CONTROL
,
117 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
119 /* EXYNOS_MIPI_PHY_ID_DSIM0 */
120 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_CSIS0
,
121 .enable_val
= EXYNOS5_PHY_ENABLE
,
122 .enable_reg
= EXYNOS5420_MIPI_PHY0_CONTROL
,
123 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
124 .resetn_val
= EXYNOS5_MIPI_PHY_M_RESETN
,
125 .resetn_reg
= EXYNOS5420_MIPI_PHY0_CONTROL
,
126 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
128 /* EXYNOS_MIPI_PHY_ID_CSIS1 */
129 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_DSIM1
,
130 .enable_val
= EXYNOS5_PHY_ENABLE
,
131 .enable_reg
= EXYNOS5420_MIPI_PHY1_CONTROL
,
132 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
133 .resetn_val
= EXYNOS5_MIPI_PHY_S_RESETN
,
134 .resetn_reg
= EXYNOS5420_MIPI_PHY1_CONTROL
,
135 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
137 /* EXYNOS_MIPI_PHY_ID_DSIM1 */
138 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_CSIS1
,
139 .enable_val
= EXYNOS5_PHY_ENABLE
,
140 .enable_reg
= EXYNOS5420_MIPI_PHY1_CONTROL
,
141 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
142 .resetn_val
= EXYNOS5_MIPI_PHY_M_RESETN
,
143 .resetn_reg
= EXYNOS5420_MIPI_PHY1_CONTROL
,
144 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
146 /* EXYNOS_MIPI_PHY_ID_CSIS2 */
147 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_NONE
,
148 .enable_val
= EXYNOS5_PHY_ENABLE
,
149 .enable_reg
= EXYNOS5420_MIPI_PHY2_CONTROL
,
150 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
151 .resetn_val
= EXYNOS5_MIPI_PHY_S_RESETN
,
152 .resetn_reg
= EXYNOS5420_MIPI_PHY2_CONTROL
,
153 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
158 #define EXYNOS5433_SYSREG_DISP_MIPI_PHY 0x100C
159 #define EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON 0x1014
160 #define EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON 0x1020
162 static const struct mipi_phy_device_desc exynos5433_mipi_phy
= {
165 "samsung,pmu-syscon",
166 "samsung,disp-sysreg",
167 "samsung,cam0-sysreg",
168 "samsung,cam1-sysreg"
173 /* EXYNOS_MIPI_PHY_ID_CSIS0 */
174 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_DSIM0
,
175 .enable_val
= EXYNOS5_PHY_ENABLE
,
176 .enable_reg
= EXYNOS5433_MIPI_PHY0_CONTROL
,
177 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
178 .resetn_val
= BIT(0),
179 .resetn_reg
= EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON
,
180 .resetn_map
= EXYNOS_MIPI_REGMAP_CAM0
,
182 /* EXYNOS_MIPI_PHY_ID_DSIM0 */
183 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_CSIS0
,
184 .enable_val
= EXYNOS5_PHY_ENABLE
,
185 .enable_reg
= EXYNOS5433_MIPI_PHY0_CONTROL
,
186 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
187 .resetn_val
= BIT(0),
188 .resetn_reg
= EXYNOS5433_SYSREG_DISP_MIPI_PHY
,
189 .resetn_map
= EXYNOS_MIPI_REGMAP_DISP
,
191 /* EXYNOS_MIPI_PHY_ID_CSIS1 */
192 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_NONE
,
193 .enable_val
= EXYNOS5_PHY_ENABLE
,
194 .enable_reg
= EXYNOS5433_MIPI_PHY1_CONTROL
,
195 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
196 .resetn_val
= BIT(1),
197 .resetn_reg
= EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON
,
198 .resetn_map
= EXYNOS_MIPI_REGMAP_CAM0
,
200 /* EXYNOS_MIPI_PHY_ID_DSIM1 */
201 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_NONE
,
202 .enable_val
= EXYNOS5_PHY_ENABLE
,
203 .enable_reg
= EXYNOS5433_MIPI_PHY1_CONTROL
,
204 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
205 .resetn_val
= BIT(1),
206 .resetn_reg
= EXYNOS5433_SYSREG_DISP_MIPI_PHY
,
207 .resetn_map
= EXYNOS_MIPI_REGMAP_DISP
,
209 /* EXYNOS_MIPI_PHY_ID_CSIS2 */
210 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_NONE
,
211 .enable_val
= EXYNOS5_PHY_ENABLE
,
212 .enable_reg
= EXYNOS5433_MIPI_PHY2_CONTROL
,
213 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
214 .resetn_val
= BIT(0),
215 .resetn_reg
= EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON
,
216 .resetn_map
= EXYNOS_MIPI_REGMAP_CAM1
,
221 struct exynos_mipi_video_phy
{
222 struct regmap
*regmaps
[EXYNOS_MIPI_REGMAPS_NUM
];
224 struct video_phy_desc
{
227 const struct exynos_mipi_phy_desc
*data
;
228 } phys
[EXYNOS_MIPI_PHYS_NUM
];
232 static inline int __is_running(const struct exynos_mipi_phy_desc
*data
,
233 struct exynos_mipi_video_phy
*state
)
238 ret
= regmap_read(state
->regmaps
[data
->resetn_map
], data
->resetn_reg
, &val
);
242 return val
& data
->resetn_val
;
245 static int __set_phy_state(const struct exynos_mipi_phy_desc
*data
,
246 struct exynos_mipi_video_phy
*state
, unsigned int on
)
250 spin_lock(&state
->slock
);
252 /* disable in PMU sysreg */
253 if (!on
&& data
->coupled_phy_id
>= 0 &&
254 !__is_running(state
->phys
[data
->coupled_phy_id
].data
, state
)) {
255 regmap_read(state
->regmaps
[data
->enable_map
], data
->enable_reg
,
257 val
&= ~data
->enable_val
;
258 regmap_write(state
->regmaps
[data
->enable_map
], data
->enable_reg
,
263 regmap_read(state
->regmaps
[data
->resetn_map
], data
->resetn_reg
, &val
);
264 val
= on
? (val
| data
->resetn_val
) : (val
& ~data
->resetn_val
);
265 regmap_write(state
->regmaps
[data
->resetn_map
], data
->resetn_reg
, val
);
267 /* enable in PMU sysreg */
269 regmap_read(state
->regmaps
[data
->enable_map
], data
->enable_reg
,
271 val
|= data
->enable_val
;
272 regmap_write(state
->regmaps
[data
->enable_map
], data
->enable_reg
,
276 spin_unlock(&state
->slock
);
281 #define to_mipi_video_phy(desc) \
282 container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index])
284 static int exynos_mipi_video_phy_power_on(struct phy
*phy
)
286 struct video_phy_desc
*phy_desc
= phy_get_drvdata(phy
);
287 struct exynos_mipi_video_phy
*state
= to_mipi_video_phy(phy_desc
);
289 return __set_phy_state(phy_desc
->data
, state
, 1);
292 static int exynos_mipi_video_phy_power_off(struct phy
*phy
)
294 struct video_phy_desc
*phy_desc
= phy_get_drvdata(phy
);
295 struct exynos_mipi_video_phy
*state
= to_mipi_video_phy(phy_desc
);
297 return __set_phy_state(phy_desc
->data
, state
, 0);
300 static struct phy
*exynos_mipi_video_phy_xlate(struct device
*dev
,
301 struct of_phandle_args
*args
)
303 struct exynos_mipi_video_phy
*state
= dev_get_drvdata(dev
);
305 if (WARN_ON(args
->args
[0] >= state
->num_phys
))
306 return ERR_PTR(-ENODEV
);
308 return state
->phys
[args
->args
[0]].phy
;
311 static const struct phy_ops exynos_mipi_video_phy_ops
= {
312 .power_on
= exynos_mipi_video_phy_power_on
,
313 .power_off
= exynos_mipi_video_phy_power_off
,
314 .owner
= THIS_MODULE
,
317 static int exynos_mipi_video_phy_probe(struct platform_device
*pdev
)
319 const struct mipi_phy_device_desc
*phy_dev
;
320 struct exynos_mipi_video_phy
*state
;
321 struct device
*dev
= &pdev
->dev
;
322 struct device_node
*np
= dev
->of_node
;
323 struct phy_provider
*phy_provider
;
326 phy_dev
= of_device_get_match_data(dev
);
330 state
= devm_kzalloc(dev
, sizeof(*state
), GFP_KERNEL
);
334 for (i
= 0; i
< phy_dev
->num_regmaps
; i
++) {
335 state
->regmaps
[i
] = syscon_regmap_lookup_by_phandle(np
,
336 phy_dev
->regmap_names
[i
]);
337 if (IS_ERR(state
->regmaps
[i
]))
338 return PTR_ERR(state
->regmaps
[i
]);
340 state
->num_phys
= phy_dev
->num_phys
;
341 spin_lock_init(&state
->slock
);
343 dev_set_drvdata(dev
, state
);
345 for (i
= 0; i
< state
->num_phys
; i
++) {
346 struct phy
*phy
= devm_phy_create(dev
, NULL
,
347 &exynos_mipi_video_phy_ops
);
349 dev_err(dev
, "failed to create PHY %d\n", i
);
353 state
->phys
[i
].phy
= phy
;
354 state
->phys
[i
].index
= i
;
355 state
->phys
[i
].data
= &phy_dev
->phys
[i
];
356 phy_set_drvdata(phy
, &state
->phys
[i
]);
359 phy_provider
= devm_of_phy_provider_register(dev
,
360 exynos_mipi_video_phy_xlate
);
362 return PTR_ERR_OR_ZERO(phy_provider
);
365 static const struct of_device_id exynos_mipi_video_phy_of_match
[] = {
367 .compatible
= "samsung,s5pv210-mipi-video-phy",
368 .data
= &s5pv210_mipi_phy
,
370 .compatible
= "samsung,exynos5420-mipi-video-phy",
371 .data
= &exynos5420_mipi_phy
,
373 .compatible
= "samsung,exynos5433-mipi-video-phy",
374 .data
= &exynos5433_mipi_phy
,
378 MODULE_DEVICE_TABLE(of
, exynos_mipi_video_phy_of_match
);
380 static struct platform_driver exynos_mipi_video_phy_driver
= {
381 .probe
= exynos_mipi_video_phy_probe
,
383 .of_match_table
= exynos_mipi_video_phy_of_match
,
384 .name
= "exynos-mipi-video-phy",
387 module_platform_driver(exynos_mipi_video_phy_driver
);
389 MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver");
390 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
391 MODULE_LICENSE("GPL v2");