372 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Samsung S5P/Exynos SoC series MIPI CSIS/DSIM DPHY driver4 *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 21enum 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_NUM29};30 31enum 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_NUM37};38 39struct 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 54static 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 99static 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 0x100C154#define EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON 0x1014155#define EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON 0x1020156 157static 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 216struct 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 227static 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 else245 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 260static 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 268static 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 276static struct phy *exynos_mipi_video_phy_xlate(struct device *dev,277 const 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 287static 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 293static 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 344static 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};357MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match);358 359static 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};367module_platform_driver(exynos_mipi_video_phy_driver);368 369MODULE_DESCRIPTION("Samsung S5P/Exynos SoC MIPI CSI-2/DSI PHY driver");370MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");371MODULE_LICENSE("GPL v2");372