Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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