phy-exynos-mipi-video.c (11128B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Samsung S5P/Exynos SoC series MIPI CSIS/DSIM DPHY driver 4 * 5 * Copyright (C) 2013,2016 Samsung Electronics Co., Ltd. 6 * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> 7 */ 8 9#include <linux/err.h> 10#include <linux/io.h> 11#include <linux/kernel.h> 12#include <linux/module.h> 13#include <linux/of.h> 14#include <linux/of_address.h> 15#include <linux/of_device.h> 16#include <linux/phy/phy.h> 17#include <linux/regmap.h> 18#include <linux/spinlock.h> 19#include <linux/soc/samsung/exynos-regs-pmu.h> 20#include <linux/mfd/syscon.h> 21 22enum exynos_mipi_phy_id { 23 EXYNOS_MIPI_PHY_ID_NONE = -1, 24 EXYNOS_MIPI_PHY_ID_CSIS0, 25 EXYNOS_MIPI_PHY_ID_DSIM0, 26 EXYNOS_MIPI_PHY_ID_CSIS1, 27 EXYNOS_MIPI_PHY_ID_DSIM1, 28 EXYNOS_MIPI_PHY_ID_CSIS2, 29 EXYNOS_MIPI_PHYS_NUM 30}; 31 32enum exynos_mipi_phy_regmap_id { 33 EXYNOS_MIPI_REGMAP_PMU, 34 EXYNOS_MIPI_REGMAP_DISP, 35 EXYNOS_MIPI_REGMAP_CAM0, 36 EXYNOS_MIPI_REGMAP_CAM1, 37 EXYNOS_MIPI_REGMAPS_NUM 38}; 39 40struct mipi_phy_device_desc { 41 int num_phys; 42 int num_regmaps; 43 const char *regmap_names[EXYNOS_MIPI_REGMAPS_NUM]; 44 struct exynos_mipi_phy_desc { 45 enum exynos_mipi_phy_id coupled_phy_id; 46 u32 enable_val; 47 unsigned int enable_reg; 48 enum exynos_mipi_phy_regmap_id enable_map; 49 u32 resetn_val; 50 unsigned int resetn_reg; 51 enum exynos_mipi_phy_regmap_id resetn_map; 52 } phys[EXYNOS_MIPI_PHYS_NUM]; 53}; 54 55static const struct mipi_phy_device_desc s5pv210_mipi_phy = { 56 .num_regmaps = 1, 57 .regmap_names = {"syscon"}, 58 .num_phys = 4, 59 .phys = { 60 { 61 /* EXYNOS_MIPI_PHY_ID_CSIS0 */ 62 .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, 63 .enable_val = EXYNOS4_PHY_ENABLE, 64 .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), 65 .enable_map = EXYNOS_MIPI_REGMAP_PMU, 66 .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, 67 .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0), 68 .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 69 }, { 70 /* EXYNOS_MIPI_PHY_ID_DSIM0 */ 71 .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, 72 .enable_val = EXYNOS4_PHY_ENABLE, 73 .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), 74 .enable_map = EXYNOS_MIPI_REGMAP_PMU, 75 .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, 76 .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0), 77 .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 78 }, { 79 /* EXYNOS_MIPI_PHY_ID_CSIS1 */ 80 .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1, 81 .enable_val = EXYNOS4_PHY_ENABLE, 82 .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), 83 .enable_map = EXYNOS_MIPI_REGMAP_PMU, 84 .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, 85 .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1), 86 .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 87 }, { 88 /* EXYNOS_MIPI_PHY_ID_DSIM1 */ 89 .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1, 90 .enable_val = EXYNOS4_PHY_ENABLE, 91 .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), 92 .enable_map = EXYNOS_MIPI_REGMAP_PMU, 93 .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, 94 .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1), 95 .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 96 }, 97 }, 98}; 99 100static const struct mipi_phy_device_desc exynos5420_mipi_phy = { 101 .num_regmaps = 1, 102 .regmap_names = {"syscon"}, 103 .num_phys = 5, 104 .phys = { 105 { 106 /* EXYNOS_MIPI_PHY_ID_CSIS0 */ 107 .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, 108 .enable_val = EXYNOS4_PHY_ENABLE, 109 .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), 110 .enable_map = EXYNOS_MIPI_REGMAP_PMU, 111 .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, 112 .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), 113 .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 114 }, { 115 /* EXYNOS_MIPI_PHY_ID_DSIM0 */ 116 .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, 117 .enable_val = EXYNOS4_PHY_ENABLE, 118 .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), 119 .enable_map = EXYNOS_MIPI_REGMAP_PMU, 120 .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, 121 .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), 122 .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 123 }, { 124 /* EXYNOS_MIPI_PHY_ID_CSIS1 */ 125 .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1, 126 .enable_val = EXYNOS4_PHY_ENABLE, 127 .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), 128 .enable_map = EXYNOS_MIPI_REGMAP_PMU, 129 .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, 130 .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), 131 .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 132 }, { 133 /* EXYNOS_MIPI_PHY_ID_DSIM1 */ 134 .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1, 135 .enable_val = EXYNOS4_PHY_ENABLE, 136 .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), 137 .enable_map = EXYNOS_MIPI_REGMAP_PMU, 138 .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, 139 .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), 140 .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 141 }, { 142 /* EXYNOS_MIPI_PHY_ID_CSIS2 */ 143 .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, 144 .enable_val = EXYNOS4_PHY_ENABLE, 145 .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(2), 146 .enable_map = EXYNOS_MIPI_REGMAP_PMU, 147 .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, 148 .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(2), 149 .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 150 }, 151 }, 152}; 153 154#define EXYNOS5433_SYSREG_DISP_MIPI_PHY 0x100C 155#define EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON 0x1014 156#define EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON 0x1020 157 158static const struct mipi_phy_device_desc exynos5433_mipi_phy = { 159 .num_regmaps = 4, 160 .regmap_names = { 161 "samsung,pmu-syscon", 162 "samsung,disp-sysreg", 163 "samsung,cam0-sysreg", 164 "samsung,cam1-sysreg" 165 }, 166 .num_phys = 5, 167 .phys = { 168 { 169 /* EXYNOS_MIPI_PHY_ID_CSIS0 */ 170 .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, 171 .enable_val = EXYNOS4_PHY_ENABLE, 172 .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), 173 .enable_map = EXYNOS_MIPI_REGMAP_PMU, 174 .resetn_val = BIT(0), 175 .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON, 176 .resetn_map = EXYNOS_MIPI_REGMAP_CAM0, 177 }, { 178 /* EXYNOS_MIPI_PHY_ID_DSIM0 */ 179 .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, 180 .enable_val = EXYNOS4_PHY_ENABLE, 181 .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), 182 .enable_map = EXYNOS_MIPI_REGMAP_PMU, 183 .resetn_val = BIT(0), 184 .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY, 185 .resetn_map = EXYNOS_MIPI_REGMAP_DISP, 186 }, { 187 /* EXYNOS_MIPI_PHY_ID_CSIS1 */ 188 .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, 189 .enable_val = EXYNOS4_PHY_ENABLE, 190 .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), 191 .enable_map = EXYNOS_MIPI_REGMAP_PMU, 192 .resetn_val = BIT(1), 193 .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON, 194 .resetn_map = EXYNOS_MIPI_REGMAP_CAM0, 195 }, { 196 /* EXYNOS_MIPI_PHY_ID_DSIM1 */ 197 .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, 198 .enable_val = EXYNOS4_PHY_ENABLE, 199 .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), 200 .enable_map = EXYNOS_MIPI_REGMAP_PMU, 201 .resetn_val = BIT(1), 202 .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY, 203 .resetn_map = EXYNOS_MIPI_REGMAP_DISP, 204 }, { 205 /* EXYNOS_MIPI_PHY_ID_CSIS2 */ 206 .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, 207 .enable_val = EXYNOS4_PHY_ENABLE, 208 .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(2), 209 .enable_map = EXYNOS_MIPI_REGMAP_PMU, 210 .resetn_val = BIT(0), 211 .resetn_reg = EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON, 212 .resetn_map = EXYNOS_MIPI_REGMAP_CAM1, 213 }, 214 }, 215}; 216 217struct exynos_mipi_video_phy { 218 struct regmap *regmaps[EXYNOS_MIPI_REGMAPS_NUM]; 219 int num_phys; 220 struct video_phy_desc { 221 struct phy *phy; 222 unsigned int index; 223 const struct exynos_mipi_phy_desc *data; 224 } phys[EXYNOS_MIPI_PHYS_NUM]; 225 spinlock_t slock; 226}; 227 228static int __set_phy_state(const struct exynos_mipi_phy_desc *data, 229 struct exynos_mipi_video_phy *state, unsigned int on) 230{ 231 struct regmap *enable_map = state->regmaps[data->enable_map]; 232 struct regmap *resetn_map = state->regmaps[data->resetn_map]; 233 234 spin_lock(&state->slock); 235 236 /* disable in PMU sysreg */ 237 if (!on && data->coupled_phy_id >= 0 && 238 state->phys[data->coupled_phy_id].phy->power_count == 0) 239 regmap_update_bits(enable_map, data->enable_reg, 240 data->enable_val, 0); 241 /* PHY reset */ 242 if (on) 243 regmap_update_bits(resetn_map, data->resetn_reg, 244 data->resetn_val, data->resetn_val); 245 else 246 regmap_update_bits(resetn_map, data->resetn_reg, 247 data->resetn_val, 0); 248 /* enable in PMU sysreg */ 249 if (on) 250 regmap_update_bits(enable_map, data->enable_reg, 251 data->enable_val, data->enable_val); 252 253 spin_unlock(&state->slock); 254 255 return 0; 256} 257 258#define to_mipi_video_phy(desc) \ 259 container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]) 260 261static int exynos_mipi_video_phy_power_on(struct phy *phy) 262{ 263 struct video_phy_desc *phy_desc = phy_get_drvdata(phy); 264 struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); 265 266 return __set_phy_state(phy_desc->data, state, 1); 267} 268 269static int exynos_mipi_video_phy_power_off(struct phy *phy) 270{ 271 struct video_phy_desc *phy_desc = phy_get_drvdata(phy); 272 struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); 273 274 return __set_phy_state(phy_desc->data, state, 0); 275} 276 277static struct phy *exynos_mipi_video_phy_xlate(struct device *dev, 278 struct of_phandle_args *args) 279{ 280 struct exynos_mipi_video_phy *state = dev_get_drvdata(dev); 281 282 if (WARN_ON(args->args[0] >= state->num_phys)) 283 return ERR_PTR(-ENODEV); 284 285 return state->phys[args->args[0]].phy; 286} 287 288static const struct phy_ops exynos_mipi_video_phy_ops = { 289 .power_on = exynos_mipi_video_phy_power_on, 290 .power_off = exynos_mipi_video_phy_power_off, 291 .owner = THIS_MODULE, 292}; 293 294static int exynos_mipi_video_phy_probe(struct platform_device *pdev) 295{ 296 const struct mipi_phy_device_desc *phy_dev; 297 struct exynos_mipi_video_phy *state; 298 struct device *dev = &pdev->dev; 299 struct device_node *np = dev->of_node; 300 struct phy_provider *phy_provider; 301 unsigned int i; 302 303 phy_dev = of_device_get_match_data(dev); 304 if (!phy_dev) 305 return -ENODEV; 306 307 state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); 308 if (!state) 309 return -ENOMEM; 310 311 for (i = 0; i < phy_dev->num_regmaps; i++) { 312 state->regmaps[i] = syscon_regmap_lookup_by_phandle(np, 313 phy_dev->regmap_names[i]); 314 if (IS_ERR(state->regmaps[i])) 315 return PTR_ERR(state->regmaps[i]); 316 } 317 state->num_phys = phy_dev->num_phys; 318 spin_lock_init(&state->slock); 319 320 dev_set_drvdata(dev, state); 321 322 for (i = 0; i < state->num_phys; i++) { 323 struct phy *phy = devm_phy_create(dev, NULL, 324 &exynos_mipi_video_phy_ops); 325 if (IS_ERR(phy)) { 326 dev_err(dev, "failed to create PHY %d\n", i); 327 return PTR_ERR(phy); 328 } 329 330 state->phys[i].phy = phy; 331 state->phys[i].index = i; 332 state->phys[i].data = &phy_dev->phys[i]; 333 phy_set_drvdata(phy, &state->phys[i]); 334 } 335 336 phy_provider = devm_of_phy_provider_register(dev, 337 exynos_mipi_video_phy_xlate); 338 339 return PTR_ERR_OR_ZERO(phy_provider); 340} 341 342static const struct of_device_id exynos_mipi_video_phy_of_match[] = { 343 { 344 .compatible = "samsung,s5pv210-mipi-video-phy", 345 .data = &s5pv210_mipi_phy, 346 }, { 347 .compatible = "samsung,exynos5420-mipi-video-phy", 348 .data = &exynos5420_mipi_phy, 349 }, { 350 .compatible = "samsung,exynos5433-mipi-video-phy", 351 .data = &exynos5433_mipi_phy, 352 }, 353 { /* sentinel */ }, 354}; 355MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match); 356 357static struct platform_driver exynos_mipi_video_phy_driver = { 358 .probe = exynos_mipi_video_phy_probe, 359 .driver = { 360 .of_match_table = exynos_mipi_video_phy_of_match, 361 .name = "exynos-mipi-video-phy", 362 .suppress_bind_attrs = true, 363 } 364}; 365module_platform_driver(exynos_mipi_video_phy_driver); 366 367MODULE_DESCRIPTION("Samsung S5P/Exynos SoC MIPI CSI-2/DSI PHY driver"); 368MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); 369MODULE_LICENSE("GPL v2");