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 struct exynos_mipi_video_phy { 217 struct regmap *regmaps[EXYNOS_MIPI_REGMAPS_NUM]; 218 int num_phys; 219 struct video_phy_desc { 220 struct phy *phy; 221 unsigned int index; 222 const struct exynos_mipi_phy_desc *data; 223 } phys[EXYNOS_MIPI_PHYS_NUM]; 224 spinlock_t slock; 225 }; 226 227 static int __set_phy_state(const struct exynos_mipi_phy_desc *data, 228 struct exynos_mipi_video_phy *state, unsigned int on) 229 { 230 struct regmap *enable_map = state->regmaps[data->enable_map]; 231 struct regmap *resetn_map = state->regmaps[data->resetn_map]; 232 233 spin_lock(&state->slock); 234 235 /* disable in PMU sysreg */ 236 if (!on && data->coupled_phy_id >= 0 && 237 state->phys[data->coupled_phy_id].phy->power_count == 0) 238 regmap_update_bits(enable_map, data->enable_reg, 239 data->enable_val, 0); 240 /* PHY reset */ 241 if (on) 242 regmap_update_bits(resetn_map, data->resetn_reg, 243 data->resetn_val, data->resetn_val); 244 else 245 regmap_update_bits(resetn_map, data->resetn_reg, 246 data->resetn_val, 0); 247 /* enable in PMU sysreg */ 248 if (on) 249 regmap_update_bits(enable_map, data->enable_reg, 250 data->enable_val, data->enable_val); 251 252 spin_unlock(&state->slock); 253 254 return 0; 255 } 256 257 #define to_mipi_video_phy(desc) \ 258 container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]) 259 260 static int exynos_mipi_video_phy_power_on(struct phy *phy) 261 { 262 struct video_phy_desc *phy_desc = phy_get_drvdata(phy); 263 struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); 264 265 return __set_phy_state(phy_desc->data, state, 1); 266 } 267 268 static int exynos_mipi_video_phy_power_off(struct phy *phy) 269 { 270 struct video_phy_desc *phy_desc = phy_get_drvdata(phy); 271 struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); 272 273 return __set_phy_state(phy_desc->data, state, 0); 274 } 275 276 static struct phy *exynos_mipi_video_phy_xlate(struct device *dev, 277 struct of_phandle_args *args) 278 { 279 struct exynos_mipi_video_phy *state = dev_get_drvdata(dev); 280 281 if (WARN_ON(args->args[0] >= state->num_phys)) 282 return ERR_PTR(-ENODEV); 283 284 return state->phys[args->args[0]].phy; 285 } 286 287 static const struct phy_ops exynos_mipi_video_phy_ops = { 288 .power_on = exynos_mipi_video_phy_power_on, 289 .power_off = exynos_mipi_video_phy_power_off, 290 .owner = THIS_MODULE, 291 }; 292 293 static int exynos_mipi_video_phy_probe(struct platform_device *pdev) 294 { 295 const struct mipi_phy_device_desc *phy_dev; 296 struct exynos_mipi_video_phy *state; 297 struct device *dev = &pdev->dev; 298 struct device_node *np = dev->of_node; 299 struct phy_provider *phy_provider; 300 unsigned int i = 0; 301 302 phy_dev = of_device_get_match_data(dev); 303 if (!phy_dev) 304 return -ENODEV; 305 306 state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); 307 if (!state) 308 return -ENOMEM; 309 310 state->regmaps[i] = syscon_node_to_regmap(dev->parent->of_node); 311 if (!IS_ERR(state->regmaps[i])) 312 i++; 313 for (; i < phy_dev->num_regmaps; i++) { 314 state->regmaps[i] = syscon_regmap_lookup_by_phandle(np, 315 phy_dev->regmap_names[i]); 316 if (IS_ERR(state->regmaps[i])) 317 return PTR_ERR(state->regmaps[i]); 318 } 319 state->num_phys = phy_dev->num_phys; 320 spin_lock_init(&state->slock); 321 322 dev_set_drvdata(dev, state); 323 324 for (i = 0; i < state->num_phys; i++) { 325 struct phy *phy = devm_phy_create(dev, NULL, 326 &exynos_mipi_video_phy_ops); 327 if (IS_ERR(phy)) { 328 dev_err(dev, "failed to create PHY %d\n", i); 329 return PTR_ERR(phy); 330 } 331 332 state->phys[i].phy = phy; 333 state->phys[i].index = i; 334 state->phys[i].data = &phy_dev->phys[i]; 335 phy_set_drvdata(phy, &state->phys[i]); 336 } 337 338 phy_provider = devm_of_phy_provider_register(dev, 339 exynos_mipi_video_phy_xlate); 340 341 return PTR_ERR_OR_ZERO(phy_provider); 342 } 343 344 static const struct of_device_id exynos_mipi_video_phy_of_match[] = { 345 { 346 .compatible = "samsung,s5pv210-mipi-video-phy", 347 .data = &s5pv210_mipi_phy, 348 }, { 349 .compatible = "samsung,exynos5420-mipi-video-phy", 350 .data = &exynos5420_mipi_phy, 351 }, { 352 .compatible = "samsung,exynos5433-mipi-video-phy", 353 .data = &exynos5433_mipi_phy, 354 }, 355 { /* sentinel */ }, 356 }; 357 MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match); 358 359 static struct platform_driver exynos_mipi_video_phy_driver = { 360 .probe = exynos_mipi_video_phy_probe, 361 .driver = { 362 .of_match_table = exynos_mipi_video_phy_of_match, 363 .name = "exynos-mipi-video-phy", 364 .suppress_bind_attrs = true, 365 } 366 }; 367 module_platform_driver(exynos_mipi_video_phy_driver); 368 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"); 372