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