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/module.h>
17 #include <linux/of_address.h>
18 #include <linux/of_device.h>
19 #include <linux/phy/phy.h>
20 #include <linux/regmap.h>
21 #include <linux/spinlock.h>
22 #include <linux/soc/samsung/exynos-regs-pmu.h>
23 #include <linux/mfd/syscon.h>
25 enum exynos_mipi_phy_id
{
26 EXYNOS_MIPI_PHY_ID_NONE
= -1,
27 EXYNOS_MIPI_PHY_ID_CSIS0
,
28 EXYNOS_MIPI_PHY_ID_DSIM0
,
29 EXYNOS_MIPI_PHY_ID_CSIS1
,
30 EXYNOS_MIPI_PHY_ID_DSIM1
,
31 EXYNOS_MIPI_PHY_ID_CSIS2
,
35 enum exynos_mipi_phy_regmap_id
{
36 EXYNOS_MIPI_REGMAP_PMU
,
37 EXYNOS_MIPI_REGMAP_DISP
,
38 EXYNOS_MIPI_REGMAP_CAM0
,
39 EXYNOS_MIPI_REGMAP_CAM1
,
40 EXYNOS_MIPI_REGMAPS_NUM
43 struct mipi_phy_device_desc
{
46 const char *regmap_names
[EXYNOS_MIPI_REGMAPS_NUM
];
47 struct exynos_mipi_phy_desc
{
48 enum exynos_mipi_phy_id coupled_phy_id
;
50 unsigned int enable_reg
;
51 enum exynos_mipi_phy_regmap_id enable_map
;
53 unsigned int resetn_reg
;
54 enum exynos_mipi_phy_regmap_id resetn_map
;
55 } phys
[EXYNOS_MIPI_PHYS_NUM
];
58 static const struct mipi_phy_device_desc s5pv210_mipi_phy
= {
60 .regmap_names
= {"syscon"},
64 /* EXYNOS_MIPI_PHY_ID_CSIS0 */
65 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_DSIM0
,
66 .enable_val
= EXYNOS4_PHY_ENABLE
,
67 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(0),
68 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
69 .resetn_val
= EXYNOS4_MIPI_PHY_SRESETN
,
70 .resetn_reg
= EXYNOS4_MIPI_PHY_CONTROL(0),
71 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
73 /* EXYNOS_MIPI_PHY_ID_DSIM0 */
74 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_CSIS0
,
75 .enable_val
= EXYNOS4_PHY_ENABLE
,
76 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(0),
77 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
78 .resetn_val
= EXYNOS4_MIPI_PHY_MRESETN
,
79 .resetn_reg
= EXYNOS4_MIPI_PHY_CONTROL(0),
80 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
82 /* EXYNOS_MIPI_PHY_ID_CSIS1 */
83 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_DSIM1
,
84 .enable_val
= EXYNOS4_PHY_ENABLE
,
85 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(1),
86 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
87 .resetn_val
= EXYNOS4_MIPI_PHY_SRESETN
,
88 .resetn_reg
= EXYNOS4_MIPI_PHY_CONTROL(1),
89 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
91 /* EXYNOS_MIPI_PHY_ID_DSIM1 */
92 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_CSIS1
,
93 .enable_val
= EXYNOS4_PHY_ENABLE
,
94 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(1),
95 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
96 .resetn_val
= EXYNOS4_MIPI_PHY_MRESETN
,
97 .resetn_reg
= EXYNOS4_MIPI_PHY_CONTROL(1),
98 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
103 static const struct mipi_phy_device_desc exynos5420_mipi_phy
= {
105 .regmap_names
= {"syscon"},
109 /* EXYNOS_MIPI_PHY_ID_CSIS0 */
110 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_DSIM0
,
111 .enable_val
= EXYNOS4_PHY_ENABLE
,
112 .enable_reg
= EXYNOS5420_MIPI_PHY_CONTROL(0),
113 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
114 .resetn_val
= EXYNOS4_MIPI_PHY_SRESETN
,
115 .resetn_reg
= EXYNOS5420_MIPI_PHY_CONTROL(0),
116 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
118 /* EXYNOS_MIPI_PHY_ID_DSIM0 */
119 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_CSIS0
,
120 .enable_val
= EXYNOS4_PHY_ENABLE
,
121 .enable_reg
= EXYNOS5420_MIPI_PHY_CONTROL(0),
122 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
123 .resetn_val
= EXYNOS4_MIPI_PHY_MRESETN
,
124 .resetn_reg
= EXYNOS5420_MIPI_PHY_CONTROL(0),
125 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
127 /* EXYNOS_MIPI_PHY_ID_CSIS1 */
128 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_DSIM1
,
129 .enable_val
= EXYNOS4_PHY_ENABLE
,
130 .enable_reg
= EXYNOS5420_MIPI_PHY_CONTROL(1),
131 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
132 .resetn_val
= EXYNOS4_MIPI_PHY_SRESETN
,
133 .resetn_reg
= EXYNOS5420_MIPI_PHY_CONTROL(1),
134 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
136 /* EXYNOS_MIPI_PHY_ID_DSIM1 */
137 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_CSIS1
,
138 .enable_val
= EXYNOS4_PHY_ENABLE
,
139 .enable_reg
= EXYNOS5420_MIPI_PHY_CONTROL(1),
140 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
141 .resetn_val
= EXYNOS4_MIPI_PHY_MRESETN
,
142 .resetn_reg
= EXYNOS5420_MIPI_PHY_CONTROL(1),
143 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
145 /* EXYNOS_MIPI_PHY_ID_CSIS2 */
146 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_NONE
,
147 .enable_val
= EXYNOS4_PHY_ENABLE
,
148 .enable_reg
= EXYNOS5420_MIPI_PHY_CONTROL(2),
149 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
150 .resetn_val
= EXYNOS4_MIPI_PHY_SRESETN
,
151 .resetn_reg
= EXYNOS5420_MIPI_PHY_CONTROL(2),
152 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
157 #define EXYNOS5433_SYSREG_DISP_MIPI_PHY 0x100C
158 #define EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON 0x1014
159 #define EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON 0x1020
161 static const struct mipi_phy_device_desc exynos5433_mipi_phy
= {
164 "samsung,pmu-syscon",
165 "samsung,disp-sysreg",
166 "samsung,cam0-sysreg",
167 "samsung,cam1-sysreg"
172 /* EXYNOS_MIPI_PHY_ID_CSIS0 */
173 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_DSIM0
,
174 .enable_val
= EXYNOS4_PHY_ENABLE
,
175 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(0),
176 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
177 .resetn_val
= BIT(0),
178 .resetn_reg
= EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON
,
179 .resetn_map
= EXYNOS_MIPI_REGMAP_CAM0
,
181 /* EXYNOS_MIPI_PHY_ID_DSIM0 */
182 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_CSIS0
,
183 .enable_val
= EXYNOS4_PHY_ENABLE
,
184 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(0),
185 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
186 .resetn_val
= BIT(0),
187 .resetn_reg
= EXYNOS5433_SYSREG_DISP_MIPI_PHY
,
188 .resetn_map
= EXYNOS_MIPI_REGMAP_DISP
,
190 /* EXYNOS_MIPI_PHY_ID_CSIS1 */
191 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_NONE
,
192 .enable_val
= EXYNOS4_PHY_ENABLE
,
193 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(1),
194 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
195 .resetn_val
= BIT(1),
196 .resetn_reg
= EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON
,
197 .resetn_map
= EXYNOS_MIPI_REGMAP_CAM0
,
199 /* EXYNOS_MIPI_PHY_ID_DSIM1 */
200 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_NONE
,
201 .enable_val
= EXYNOS4_PHY_ENABLE
,
202 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(1),
203 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
204 .resetn_val
= BIT(1),
205 .resetn_reg
= EXYNOS5433_SYSREG_DISP_MIPI_PHY
,
206 .resetn_map
= EXYNOS_MIPI_REGMAP_DISP
,
208 /* EXYNOS_MIPI_PHY_ID_CSIS2 */
209 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_NONE
,
210 .enable_val
= EXYNOS4_PHY_ENABLE
,
211 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(2),
212 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
213 .resetn_val
= BIT(0),
214 .resetn_reg
= EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON
,
215 .resetn_map
= EXYNOS_MIPI_REGMAP_CAM1
,
220 struct exynos_mipi_video_phy
{
221 struct regmap
*regmaps
[EXYNOS_MIPI_REGMAPS_NUM
];
223 struct video_phy_desc
{
226 const struct exynos_mipi_phy_desc
*data
;
227 } phys
[EXYNOS_MIPI_PHYS_NUM
];
231 static int __set_phy_state(const struct exynos_mipi_phy_desc
*data
,
232 struct exynos_mipi_video_phy
*state
, unsigned int on
)
234 struct regmap
*enable_map
= state
->regmaps
[data
->enable_map
];
235 struct regmap
*resetn_map
= state
->regmaps
[data
->resetn_map
];
237 spin_lock(&state
->slock
);
239 /* disable in PMU sysreg */
240 if (!on
&& data
->coupled_phy_id
>= 0 &&
241 state
->phys
[data
->coupled_phy_id
].phy
->power_count
== 0)
242 regmap_update_bits(enable_map
, data
->enable_reg
,
243 data
->enable_val
, 0);
246 regmap_update_bits(resetn_map
, data
->resetn_reg
,
247 data
->resetn_val
, data
->resetn_val
);
249 regmap_update_bits(resetn_map
, data
->resetn_reg
,
250 data
->resetn_val
, 0);
251 /* enable in PMU sysreg */
253 regmap_update_bits(enable_map
, data
->enable_reg
,
254 data
->enable_val
, data
->enable_val
);
256 spin_unlock(&state
->slock
);
261 #define to_mipi_video_phy(desc) \
262 container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index])
264 static int exynos_mipi_video_phy_power_on(struct phy
*phy
)
266 struct video_phy_desc
*phy_desc
= phy_get_drvdata(phy
);
267 struct exynos_mipi_video_phy
*state
= to_mipi_video_phy(phy_desc
);
269 return __set_phy_state(phy_desc
->data
, state
, 1);
272 static int exynos_mipi_video_phy_power_off(struct phy
*phy
)
274 struct video_phy_desc
*phy_desc
= phy_get_drvdata(phy
);
275 struct exynos_mipi_video_phy
*state
= to_mipi_video_phy(phy_desc
);
277 return __set_phy_state(phy_desc
->data
, state
, 0);
280 static struct phy
*exynos_mipi_video_phy_xlate(struct device
*dev
,
281 struct of_phandle_args
*args
)
283 struct exynos_mipi_video_phy
*state
= dev_get_drvdata(dev
);
285 if (WARN_ON(args
->args
[0] >= state
->num_phys
))
286 return ERR_PTR(-ENODEV
);
288 return state
->phys
[args
->args
[0]].phy
;
291 static const struct phy_ops exynos_mipi_video_phy_ops
= {
292 .power_on
= exynos_mipi_video_phy_power_on
,
293 .power_off
= exynos_mipi_video_phy_power_off
,
294 .owner
= THIS_MODULE
,
297 static int exynos_mipi_video_phy_probe(struct platform_device
*pdev
)
299 const struct mipi_phy_device_desc
*phy_dev
;
300 struct exynos_mipi_video_phy
*state
;
301 struct device
*dev
= &pdev
->dev
;
302 struct device_node
*np
= dev
->of_node
;
303 struct phy_provider
*phy_provider
;
306 phy_dev
= of_device_get_match_data(dev
);
310 state
= devm_kzalloc(dev
, sizeof(*state
), GFP_KERNEL
);
314 for (i
= 0; i
< phy_dev
->num_regmaps
; i
++) {
315 state
->regmaps
[i
] = syscon_regmap_lookup_by_phandle(np
,
316 phy_dev
->regmap_names
[i
]);
317 if (IS_ERR(state
->regmaps
[i
]))
318 return PTR_ERR(state
->regmaps
[i
]);
320 state
->num_phys
= phy_dev
->num_phys
;
321 spin_lock_init(&state
->slock
);
323 dev_set_drvdata(dev
, state
);
325 for (i
= 0; i
< state
->num_phys
; i
++) {
326 struct phy
*phy
= devm_phy_create(dev
, NULL
,
327 &exynos_mipi_video_phy_ops
);
329 dev_err(dev
, "failed to create PHY %d\n", i
);
333 state
->phys
[i
].phy
= phy
;
334 state
->phys
[i
].index
= i
;
335 state
->phys
[i
].data
= &phy_dev
->phys
[i
];
336 phy_set_drvdata(phy
, &state
->phys
[i
]);
339 phy_provider
= devm_of_phy_provider_register(dev
,
340 exynos_mipi_video_phy_xlate
);
342 return PTR_ERR_OR_ZERO(phy_provider
);
345 static const struct of_device_id exynos_mipi_video_phy_of_match
[] = {
347 .compatible
= "samsung,s5pv210-mipi-video-phy",
348 .data
= &s5pv210_mipi_phy
,
350 .compatible
= "samsung,exynos5420-mipi-video-phy",
351 .data
= &exynos5420_mipi_phy
,
353 .compatible
= "samsung,exynos5433-mipi-video-phy",
354 .data
= &exynos5433_mipi_phy
,
358 MODULE_DEVICE_TABLE(of
, exynos_mipi_video_phy_of_match
);
360 static struct platform_driver exynos_mipi_video_phy_driver
= {
361 .probe
= exynos_mipi_video_phy_probe
,
363 .of_match_table
= exynos_mipi_video_phy_of_match
,
364 .name
= "exynos-mipi-video-phy",
367 module_platform_driver(exynos_mipi_video_phy_driver
);
369 MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver");
370 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
371 MODULE_LICENSE("GPL v2");