2 * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
4 * Copyright (C) 2013 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/module.h>
18 #include <linux/of_address.h>
19 #include <linux/of_device.h>
20 #include <linux/phy/phy.h>
21 #include <linux/regmap.h>
22 #include <linux/spinlock.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
,
34 enum exynos_mipi_phy_regmap_id
{
35 EXYNOS_MIPI_REGMAP_PMU
,
36 EXYNOS_MIPI_REGMAPS_NUM
39 struct mipi_phy_device_desc
{
42 const char *regmap_names
[EXYNOS_MIPI_REGMAPS_NUM
];
43 struct exynos_mipi_phy_desc
{
44 enum exynos_mipi_phy_id coupled_phy_id
;
46 unsigned int enable_reg
;
47 enum exynos_mipi_phy_regmap_id enable_map
;
49 unsigned int resetn_reg
;
50 enum exynos_mipi_phy_regmap_id resetn_map
;
51 } phys
[EXYNOS_MIPI_PHYS_NUM
];
54 static const struct mipi_phy_device_desc s5pv210_mipi_phy
= {
56 .regmap_names
= {"syscon"},
60 /* EXYNOS_MIPI_PHY_ID_CSIS0 */
61 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_DSIM0
,
62 .enable_val
= EXYNOS4_MIPI_PHY_ENABLE
,
63 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(0),
64 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
65 .resetn_val
= EXYNOS4_MIPI_PHY_SRESETN
,
66 .resetn_reg
= EXYNOS4_MIPI_PHY_CONTROL(0),
67 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
69 /* EXYNOS_MIPI_PHY_ID_DSIM0 */
70 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_CSIS0
,
71 .enable_val
= EXYNOS4_MIPI_PHY_ENABLE
,
72 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(0),
73 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
74 .resetn_val
= EXYNOS4_MIPI_PHY_MRESETN
,
75 .resetn_reg
= EXYNOS4_MIPI_PHY_CONTROL(0),
76 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
78 /* EXYNOS_MIPI_PHY_ID_CSIS1 */
79 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_DSIM1
,
80 .enable_val
= EXYNOS4_MIPI_PHY_ENABLE
,
81 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(1),
82 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
83 .resetn_val
= EXYNOS4_MIPI_PHY_SRESETN
,
84 .resetn_reg
= EXYNOS4_MIPI_PHY_CONTROL(1),
85 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
87 /* EXYNOS_MIPI_PHY_ID_DSIM1 */
88 .coupled_phy_id
= EXYNOS_MIPI_PHY_ID_CSIS1
,
89 .enable_val
= EXYNOS4_MIPI_PHY_ENABLE
,
90 .enable_reg
= EXYNOS4_MIPI_PHY_CONTROL(1),
91 .enable_map
= EXYNOS_MIPI_REGMAP_PMU
,
92 .resetn_val
= EXYNOS4_MIPI_PHY_MRESETN
,
93 .resetn_reg
= EXYNOS4_MIPI_PHY_CONTROL(1),
94 .resetn_map
= EXYNOS_MIPI_REGMAP_PMU
,
100 struct exynos_mipi_video_phy
{
101 struct regmap
*regmaps
[EXYNOS_MIPI_REGMAPS_NUM
];
103 struct video_phy_desc
{
106 const struct exynos_mipi_phy_desc
*data
;
107 } phys
[EXYNOS_MIPI_PHYS_NUM
];
111 static inline int __is_running(const struct exynos_mipi_phy_desc
*data
,
112 struct exynos_mipi_video_phy
*state
)
116 regmap_read(state
->regmaps
[data
->resetn_map
], data
->resetn_reg
, &val
);
117 return val
& data
->resetn_val
;
120 static int __set_phy_state(const struct exynos_mipi_phy_desc
*data
,
121 struct exynos_mipi_video_phy
*state
, unsigned int on
)
125 spin_lock(&state
->slock
);
127 /* disable in PMU sysreg */
128 if (!on
&& data
->coupled_phy_id
>= 0 &&
129 !__is_running(state
->phys
[data
->coupled_phy_id
].data
, state
)) {
130 regmap_read(state
->regmaps
[data
->enable_map
], data
->enable_reg
,
132 val
&= ~data
->enable_val
;
133 regmap_write(state
->regmaps
[data
->enable_map
], data
->enable_reg
,
138 regmap_read(state
->regmaps
[data
->resetn_map
], data
->resetn_reg
, &val
);
139 val
= on
? (val
| data
->resetn_val
) : (val
& ~data
->resetn_val
);
140 regmap_write(state
->regmaps
[data
->resetn_map
], data
->resetn_reg
, val
);
142 /* enable in PMU sysreg */
144 regmap_read(state
->regmaps
[data
->enable_map
], data
->enable_reg
,
146 val
|= data
->enable_val
;
147 regmap_write(state
->regmaps
[data
->enable_map
], data
->enable_reg
,
151 spin_unlock(&state
->slock
);
156 #define to_mipi_video_phy(desc) \
157 container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index])
159 static int exynos_mipi_video_phy_power_on(struct phy
*phy
)
161 struct video_phy_desc
*phy_desc
= phy_get_drvdata(phy
);
162 struct exynos_mipi_video_phy
*state
= to_mipi_video_phy(phy_desc
);
164 return __set_phy_state(phy_desc
->data
, state
, 1);
167 static int exynos_mipi_video_phy_power_off(struct phy
*phy
)
169 struct video_phy_desc
*phy_desc
= phy_get_drvdata(phy
);
170 struct exynos_mipi_video_phy
*state
= to_mipi_video_phy(phy_desc
);
172 return __set_phy_state(phy_desc
->data
, state
, 0);
175 static struct phy
*exynos_mipi_video_phy_xlate(struct device
*dev
,
176 struct of_phandle_args
*args
)
178 struct exynos_mipi_video_phy
*state
= dev_get_drvdata(dev
);
180 if (WARN_ON(args
->args
[0] >= state
->num_phys
))
181 return ERR_PTR(-ENODEV
);
183 return state
->phys
[args
->args
[0]].phy
;
186 static const struct phy_ops exynos_mipi_video_phy_ops
= {
187 .power_on
= exynos_mipi_video_phy_power_on
,
188 .power_off
= exynos_mipi_video_phy_power_off
,
189 .owner
= THIS_MODULE
,
192 static int exynos_mipi_video_phy_probe(struct platform_device
*pdev
)
194 const struct mipi_phy_device_desc
*phy_dev
;
195 struct exynos_mipi_video_phy
*state
;
196 struct device
*dev
= &pdev
->dev
;
197 struct device_node
*np
= dev
->of_node
;
198 struct phy_provider
*phy_provider
;
201 phy_dev
= of_device_get_match_data(dev
);
205 state
= devm_kzalloc(dev
, sizeof(*state
), GFP_KERNEL
);
209 for (i
= 0; i
< phy_dev
->num_regmaps
; i
++) {
210 state
->regmaps
[i
] = syscon_regmap_lookup_by_phandle(np
,
211 phy_dev
->regmap_names
[i
]);
212 if (IS_ERR(state
->regmaps
[i
]))
213 return PTR_ERR(state
->regmaps
[i
]);
215 state
->num_phys
= phy_dev
->num_phys
;
216 spin_lock_init(&state
->slock
);
218 dev_set_drvdata(dev
, state
);
220 for (i
= 0; i
< state
->num_phys
; i
++) {
221 struct phy
*phy
= devm_phy_create(dev
, NULL
,
222 &exynos_mipi_video_phy_ops
);
224 dev_err(dev
, "failed to create PHY %d\n", i
);
228 state
->phys
[i
].phy
= phy
;
229 state
->phys
[i
].index
= i
;
230 state
->phys
[i
].data
= &phy_dev
->phys
[i
];
231 phy_set_drvdata(phy
, &state
->phys
[i
]);
234 phy_provider
= devm_of_phy_provider_register(dev
,
235 exynos_mipi_video_phy_xlate
);
237 return PTR_ERR_OR_ZERO(phy_provider
);
240 static const struct of_device_id exynos_mipi_video_phy_of_match
[] = {
242 .compatible
= "samsung,s5pv210-mipi-video-phy",
243 .data
= &s5pv210_mipi_phy
,
247 MODULE_DEVICE_TABLE(of
, exynos_mipi_video_phy_of_match
);
249 static struct platform_driver exynos_mipi_video_phy_driver
= {
250 .probe
= exynos_mipi_video_phy_probe
,
252 .of_match_table
= exynos_mipi_video_phy_of_match
,
253 .name
= "exynos-mipi-video-phy",
256 module_platform_driver(exynos_mipi_video_phy_driver
);
258 MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver");
259 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
260 MODULE_LICENSE("GPL v2");