18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Samsung S5P/Exynos SoC series MIPI CSIS/DSIM DPHY driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013,2016 Samsung Electronics Co., Ltd. 68c2ecf20Sopenharmony_ci * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/of_address.h> 158c2ecf20Sopenharmony_ci#include <linux/of_device.h> 168c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 178c2ecf20Sopenharmony_ci#include <linux/regmap.h> 188c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 198c2ecf20Sopenharmony_ci#include <linux/soc/samsung/exynos-regs-pmu.h> 208c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cienum exynos_mipi_phy_id { 238c2ecf20Sopenharmony_ci EXYNOS_MIPI_PHY_ID_NONE = -1, 248c2ecf20Sopenharmony_ci EXYNOS_MIPI_PHY_ID_CSIS0, 258c2ecf20Sopenharmony_ci EXYNOS_MIPI_PHY_ID_DSIM0, 268c2ecf20Sopenharmony_ci EXYNOS_MIPI_PHY_ID_CSIS1, 278c2ecf20Sopenharmony_ci EXYNOS_MIPI_PHY_ID_DSIM1, 288c2ecf20Sopenharmony_ci EXYNOS_MIPI_PHY_ID_CSIS2, 298c2ecf20Sopenharmony_ci EXYNOS_MIPI_PHYS_NUM 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cienum exynos_mipi_phy_regmap_id { 338c2ecf20Sopenharmony_ci EXYNOS_MIPI_REGMAP_PMU, 348c2ecf20Sopenharmony_ci EXYNOS_MIPI_REGMAP_DISP, 358c2ecf20Sopenharmony_ci EXYNOS_MIPI_REGMAP_CAM0, 368c2ecf20Sopenharmony_ci EXYNOS_MIPI_REGMAP_CAM1, 378c2ecf20Sopenharmony_ci EXYNOS_MIPI_REGMAPS_NUM 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct mipi_phy_device_desc { 418c2ecf20Sopenharmony_ci int num_phys; 428c2ecf20Sopenharmony_ci int num_regmaps; 438c2ecf20Sopenharmony_ci const char *regmap_names[EXYNOS_MIPI_REGMAPS_NUM]; 448c2ecf20Sopenharmony_ci struct exynos_mipi_phy_desc { 458c2ecf20Sopenharmony_ci enum exynos_mipi_phy_id coupled_phy_id; 468c2ecf20Sopenharmony_ci u32 enable_val; 478c2ecf20Sopenharmony_ci unsigned int enable_reg; 488c2ecf20Sopenharmony_ci enum exynos_mipi_phy_regmap_id enable_map; 498c2ecf20Sopenharmony_ci u32 resetn_val; 508c2ecf20Sopenharmony_ci unsigned int resetn_reg; 518c2ecf20Sopenharmony_ci enum exynos_mipi_phy_regmap_id resetn_map; 528c2ecf20Sopenharmony_ci } phys[EXYNOS_MIPI_PHYS_NUM]; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic const struct mipi_phy_device_desc s5pv210_mipi_phy = { 568c2ecf20Sopenharmony_ci .num_regmaps = 1, 578c2ecf20Sopenharmony_ci .regmap_names = {"syscon"}, 588c2ecf20Sopenharmony_ci .num_phys = 4, 598c2ecf20Sopenharmony_ci .phys = { 608c2ecf20Sopenharmony_ci { 618c2ecf20Sopenharmony_ci /* EXYNOS_MIPI_PHY_ID_CSIS0 */ 628c2ecf20Sopenharmony_ci .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, 638c2ecf20Sopenharmony_ci .enable_val = EXYNOS4_PHY_ENABLE, 648c2ecf20Sopenharmony_ci .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), 658c2ecf20Sopenharmony_ci .enable_map = EXYNOS_MIPI_REGMAP_PMU, 668c2ecf20Sopenharmony_ci .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, 678c2ecf20Sopenharmony_ci .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0), 688c2ecf20Sopenharmony_ci .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 698c2ecf20Sopenharmony_ci }, { 708c2ecf20Sopenharmony_ci /* EXYNOS_MIPI_PHY_ID_DSIM0 */ 718c2ecf20Sopenharmony_ci .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, 728c2ecf20Sopenharmony_ci .enable_val = EXYNOS4_PHY_ENABLE, 738c2ecf20Sopenharmony_ci .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), 748c2ecf20Sopenharmony_ci .enable_map = EXYNOS_MIPI_REGMAP_PMU, 758c2ecf20Sopenharmony_ci .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, 768c2ecf20Sopenharmony_ci .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0), 778c2ecf20Sopenharmony_ci .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 788c2ecf20Sopenharmony_ci }, { 798c2ecf20Sopenharmony_ci /* EXYNOS_MIPI_PHY_ID_CSIS1 */ 808c2ecf20Sopenharmony_ci .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1, 818c2ecf20Sopenharmony_ci .enable_val = EXYNOS4_PHY_ENABLE, 828c2ecf20Sopenharmony_ci .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), 838c2ecf20Sopenharmony_ci .enable_map = EXYNOS_MIPI_REGMAP_PMU, 848c2ecf20Sopenharmony_ci .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, 858c2ecf20Sopenharmony_ci .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1), 868c2ecf20Sopenharmony_ci .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 878c2ecf20Sopenharmony_ci }, { 888c2ecf20Sopenharmony_ci /* EXYNOS_MIPI_PHY_ID_DSIM1 */ 898c2ecf20Sopenharmony_ci .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1, 908c2ecf20Sopenharmony_ci .enable_val = EXYNOS4_PHY_ENABLE, 918c2ecf20Sopenharmony_ci .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), 928c2ecf20Sopenharmony_ci .enable_map = EXYNOS_MIPI_REGMAP_PMU, 938c2ecf20Sopenharmony_ci .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, 948c2ecf20Sopenharmony_ci .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1), 958c2ecf20Sopenharmony_ci .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 968c2ecf20Sopenharmony_ci }, 978c2ecf20Sopenharmony_ci }, 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic const struct mipi_phy_device_desc exynos5420_mipi_phy = { 1018c2ecf20Sopenharmony_ci .num_regmaps = 1, 1028c2ecf20Sopenharmony_ci .regmap_names = {"syscon"}, 1038c2ecf20Sopenharmony_ci .num_phys = 5, 1048c2ecf20Sopenharmony_ci .phys = { 1058c2ecf20Sopenharmony_ci { 1068c2ecf20Sopenharmony_ci /* EXYNOS_MIPI_PHY_ID_CSIS0 */ 1078c2ecf20Sopenharmony_ci .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, 1088c2ecf20Sopenharmony_ci .enable_val = EXYNOS4_PHY_ENABLE, 1098c2ecf20Sopenharmony_ci .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), 1108c2ecf20Sopenharmony_ci .enable_map = EXYNOS_MIPI_REGMAP_PMU, 1118c2ecf20Sopenharmony_ci .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, 1128c2ecf20Sopenharmony_ci .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), 1138c2ecf20Sopenharmony_ci .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 1148c2ecf20Sopenharmony_ci }, { 1158c2ecf20Sopenharmony_ci /* EXYNOS_MIPI_PHY_ID_DSIM0 */ 1168c2ecf20Sopenharmony_ci .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, 1178c2ecf20Sopenharmony_ci .enable_val = EXYNOS4_PHY_ENABLE, 1188c2ecf20Sopenharmony_ci .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), 1198c2ecf20Sopenharmony_ci .enable_map = EXYNOS_MIPI_REGMAP_PMU, 1208c2ecf20Sopenharmony_ci .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, 1218c2ecf20Sopenharmony_ci .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), 1228c2ecf20Sopenharmony_ci .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 1238c2ecf20Sopenharmony_ci }, { 1248c2ecf20Sopenharmony_ci /* EXYNOS_MIPI_PHY_ID_CSIS1 */ 1258c2ecf20Sopenharmony_ci .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1, 1268c2ecf20Sopenharmony_ci .enable_val = EXYNOS4_PHY_ENABLE, 1278c2ecf20Sopenharmony_ci .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), 1288c2ecf20Sopenharmony_ci .enable_map = EXYNOS_MIPI_REGMAP_PMU, 1298c2ecf20Sopenharmony_ci .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, 1308c2ecf20Sopenharmony_ci .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), 1318c2ecf20Sopenharmony_ci .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 1328c2ecf20Sopenharmony_ci }, { 1338c2ecf20Sopenharmony_ci /* EXYNOS_MIPI_PHY_ID_DSIM1 */ 1348c2ecf20Sopenharmony_ci .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1, 1358c2ecf20Sopenharmony_ci .enable_val = EXYNOS4_PHY_ENABLE, 1368c2ecf20Sopenharmony_ci .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), 1378c2ecf20Sopenharmony_ci .enable_map = EXYNOS_MIPI_REGMAP_PMU, 1388c2ecf20Sopenharmony_ci .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, 1398c2ecf20Sopenharmony_ci .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), 1408c2ecf20Sopenharmony_ci .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 1418c2ecf20Sopenharmony_ci }, { 1428c2ecf20Sopenharmony_ci /* EXYNOS_MIPI_PHY_ID_CSIS2 */ 1438c2ecf20Sopenharmony_ci .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, 1448c2ecf20Sopenharmony_ci .enable_val = EXYNOS4_PHY_ENABLE, 1458c2ecf20Sopenharmony_ci .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(2), 1468c2ecf20Sopenharmony_ci .enable_map = EXYNOS_MIPI_REGMAP_PMU, 1478c2ecf20Sopenharmony_ci .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, 1488c2ecf20Sopenharmony_ci .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(2), 1498c2ecf20Sopenharmony_ci .resetn_map = EXYNOS_MIPI_REGMAP_PMU, 1508c2ecf20Sopenharmony_ci }, 1518c2ecf20Sopenharmony_ci }, 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci#define EXYNOS5433_SYSREG_DISP_MIPI_PHY 0x100C 1558c2ecf20Sopenharmony_ci#define EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON 0x1014 1568c2ecf20Sopenharmony_ci#define EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON 0x1020 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic const struct mipi_phy_device_desc exynos5433_mipi_phy = { 1598c2ecf20Sopenharmony_ci .num_regmaps = 4, 1608c2ecf20Sopenharmony_ci .regmap_names = { 1618c2ecf20Sopenharmony_ci "samsung,pmu-syscon", 1628c2ecf20Sopenharmony_ci "samsung,disp-sysreg", 1638c2ecf20Sopenharmony_ci "samsung,cam0-sysreg", 1648c2ecf20Sopenharmony_ci "samsung,cam1-sysreg" 1658c2ecf20Sopenharmony_ci }, 1668c2ecf20Sopenharmony_ci .num_phys = 5, 1678c2ecf20Sopenharmony_ci .phys = { 1688c2ecf20Sopenharmony_ci { 1698c2ecf20Sopenharmony_ci /* EXYNOS_MIPI_PHY_ID_CSIS0 */ 1708c2ecf20Sopenharmony_ci .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, 1718c2ecf20Sopenharmony_ci .enable_val = EXYNOS4_PHY_ENABLE, 1728c2ecf20Sopenharmony_ci .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), 1738c2ecf20Sopenharmony_ci .enable_map = EXYNOS_MIPI_REGMAP_PMU, 1748c2ecf20Sopenharmony_ci .resetn_val = BIT(0), 1758c2ecf20Sopenharmony_ci .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON, 1768c2ecf20Sopenharmony_ci .resetn_map = EXYNOS_MIPI_REGMAP_CAM0, 1778c2ecf20Sopenharmony_ci }, { 1788c2ecf20Sopenharmony_ci /* EXYNOS_MIPI_PHY_ID_DSIM0 */ 1798c2ecf20Sopenharmony_ci .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, 1808c2ecf20Sopenharmony_ci .enable_val = EXYNOS4_PHY_ENABLE, 1818c2ecf20Sopenharmony_ci .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), 1828c2ecf20Sopenharmony_ci .enable_map = EXYNOS_MIPI_REGMAP_PMU, 1838c2ecf20Sopenharmony_ci .resetn_val = BIT(0), 1848c2ecf20Sopenharmony_ci .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY, 1858c2ecf20Sopenharmony_ci .resetn_map = EXYNOS_MIPI_REGMAP_DISP, 1868c2ecf20Sopenharmony_ci }, { 1878c2ecf20Sopenharmony_ci /* EXYNOS_MIPI_PHY_ID_CSIS1 */ 1888c2ecf20Sopenharmony_ci .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, 1898c2ecf20Sopenharmony_ci .enable_val = EXYNOS4_PHY_ENABLE, 1908c2ecf20Sopenharmony_ci .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), 1918c2ecf20Sopenharmony_ci .enable_map = EXYNOS_MIPI_REGMAP_PMU, 1928c2ecf20Sopenharmony_ci .resetn_val = BIT(1), 1938c2ecf20Sopenharmony_ci .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON, 1948c2ecf20Sopenharmony_ci .resetn_map = EXYNOS_MIPI_REGMAP_CAM0, 1958c2ecf20Sopenharmony_ci }, { 1968c2ecf20Sopenharmony_ci /* EXYNOS_MIPI_PHY_ID_DSIM1 */ 1978c2ecf20Sopenharmony_ci .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, 1988c2ecf20Sopenharmony_ci .enable_val = EXYNOS4_PHY_ENABLE, 1998c2ecf20Sopenharmony_ci .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), 2008c2ecf20Sopenharmony_ci .enable_map = EXYNOS_MIPI_REGMAP_PMU, 2018c2ecf20Sopenharmony_ci .resetn_val = BIT(1), 2028c2ecf20Sopenharmony_ci .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY, 2038c2ecf20Sopenharmony_ci .resetn_map = EXYNOS_MIPI_REGMAP_DISP, 2048c2ecf20Sopenharmony_ci }, { 2058c2ecf20Sopenharmony_ci /* EXYNOS_MIPI_PHY_ID_CSIS2 */ 2068c2ecf20Sopenharmony_ci .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, 2078c2ecf20Sopenharmony_ci .enable_val = EXYNOS4_PHY_ENABLE, 2088c2ecf20Sopenharmony_ci .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(2), 2098c2ecf20Sopenharmony_ci .enable_map = EXYNOS_MIPI_REGMAP_PMU, 2108c2ecf20Sopenharmony_ci .resetn_val = BIT(0), 2118c2ecf20Sopenharmony_ci .resetn_reg = EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON, 2128c2ecf20Sopenharmony_ci .resetn_map = EXYNOS_MIPI_REGMAP_CAM1, 2138c2ecf20Sopenharmony_ci }, 2148c2ecf20Sopenharmony_ci }, 2158c2ecf20Sopenharmony_ci}; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistruct exynos_mipi_video_phy { 2188c2ecf20Sopenharmony_ci struct regmap *regmaps[EXYNOS_MIPI_REGMAPS_NUM]; 2198c2ecf20Sopenharmony_ci int num_phys; 2208c2ecf20Sopenharmony_ci struct video_phy_desc { 2218c2ecf20Sopenharmony_ci struct phy *phy; 2228c2ecf20Sopenharmony_ci unsigned int index; 2238c2ecf20Sopenharmony_ci const struct exynos_mipi_phy_desc *data; 2248c2ecf20Sopenharmony_ci } phys[EXYNOS_MIPI_PHYS_NUM]; 2258c2ecf20Sopenharmony_ci spinlock_t slock; 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int __set_phy_state(const struct exynos_mipi_phy_desc *data, 2298c2ecf20Sopenharmony_ci struct exynos_mipi_video_phy *state, unsigned int on) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct regmap *enable_map = state->regmaps[data->enable_map]; 2328c2ecf20Sopenharmony_ci struct regmap *resetn_map = state->regmaps[data->resetn_map]; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci spin_lock(&state->slock); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* disable in PMU sysreg */ 2378c2ecf20Sopenharmony_ci if (!on && data->coupled_phy_id >= 0 && 2388c2ecf20Sopenharmony_ci state->phys[data->coupled_phy_id].phy->power_count == 0) 2398c2ecf20Sopenharmony_ci regmap_update_bits(enable_map, data->enable_reg, 2408c2ecf20Sopenharmony_ci data->enable_val, 0); 2418c2ecf20Sopenharmony_ci /* PHY reset */ 2428c2ecf20Sopenharmony_ci if (on) 2438c2ecf20Sopenharmony_ci regmap_update_bits(resetn_map, data->resetn_reg, 2448c2ecf20Sopenharmony_ci data->resetn_val, data->resetn_val); 2458c2ecf20Sopenharmony_ci else 2468c2ecf20Sopenharmony_ci regmap_update_bits(resetn_map, data->resetn_reg, 2478c2ecf20Sopenharmony_ci data->resetn_val, 0); 2488c2ecf20Sopenharmony_ci /* enable in PMU sysreg */ 2498c2ecf20Sopenharmony_ci if (on) 2508c2ecf20Sopenharmony_ci regmap_update_bits(enable_map, data->enable_reg, 2518c2ecf20Sopenharmony_ci data->enable_val, data->enable_val); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci spin_unlock(&state->slock); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci#define to_mipi_video_phy(desc) \ 2598c2ecf20Sopenharmony_ci container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]) 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int exynos_mipi_video_phy_power_on(struct phy *phy) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct video_phy_desc *phy_desc = phy_get_drvdata(phy); 2648c2ecf20Sopenharmony_ci struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return __set_phy_state(phy_desc->data, state, 1); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int exynos_mipi_video_phy_power_off(struct phy *phy) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct video_phy_desc *phy_desc = phy_get_drvdata(phy); 2728c2ecf20Sopenharmony_ci struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return __set_phy_state(phy_desc->data, state, 0); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic struct phy *exynos_mipi_video_phy_xlate(struct device *dev, 2788c2ecf20Sopenharmony_ci struct of_phandle_args *args) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct exynos_mipi_video_phy *state = dev_get_drvdata(dev); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (WARN_ON(args->args[0] >= state->num_phys)) 2838c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return state->phys[args->args[0]].phy; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic const struct phy_ops exynos_mipi_video_phy_ops = { 2898c2ecf20Sopenharmony_ci .power_on = exynos_mipi_video_phy_power_on, 2908c2ecf20Sopenharmony_ci .power_off = exynos_mipi_video_phy_power_off, 2918c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2928c2ecf20Sopenharmony_ci}; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int exynos_mipi_video_phy_probe(struct platform_device *pdev) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci const struct mipi_phy_device_desc *phy_dev; 2978c2ecf20Sopenharmony_ci struct exynos_mipi_video_phy *state; 2988c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2998c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 3008c2ecf20Sopenharmony_ci struct phy_provider *phy_provider; 3018c2ecf20Sopenharmony_ci unsigned int i; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci phy_dev = of_device_get_match_data(dev); 3048c2ecf20Sopenharmony_ci if (!phy_dev) 3058c2ecf20Sopenharmony_ci return -ENODEV; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); 3088c2ecf20Sopenharmony_ci if (!state) 3098c2ecf20Sopenharmony_ci return -ENOMEM; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci for (i = 0; i < phy_dev->num_regmaps; i++) { 3128c2ecf20Sopenharmony_ci state->regmaps[i] = syscon_regmap_lookup_by_phandle(np, 3138c2ecf20Sopenharmony_ci phy_dev->regmap_names[i]); 3148c2ecf20Sopenharmony_ci if (IS_ERR(state->regmaps[i])) 3158c2ecf20Sopenharmony_ci return PTR_ERR(state->regmaps[i]); 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci state->num_phys = phy_dev->num_phys; 3188c2ecf20Sopenharmony_ci spin_lock_init(&state->slock); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci dev_set_drvdata(dev, state); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci for (i = 0; i < state->num_phys; i++) { 3238c2ecf20Sopenharmony_ci struct phy *phy = devm_phy_create(dev, NULL, 3248c2ecf20Sopenharmony_ci &exynos_mipi_video_phy_ops); 3258c2ecf20Sopenharmony_ci if (IS_ERR(phy)) { 3268c2ecf20Sopenharmony_ci dev_err(dev, "failed to create PHY %d\n", i); 3278c2ecf20Sopenharmony_ci return PTR_ERR(phy); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci state->phys[i].phy = phy; 3318c2ecf20Sopenharmony_ci state->phys[i].index = i; 3328c2ecf20Sopenharmony_ci state->phys[i].data = &phy_dev->phys[i]; 3338c2ecf20Sopenharmony_ci phy_set_drvdata(phy, &state->phys[i]); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, 3378c2ecf20Sopenharmony_ci exynos_mipi_video_phy_xlate); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic const struct of_device_id exynos_mipi_video_phy_of_match[] = { 3438c2ecf20Sopenharmony_ci { 3448c2ecf20Sopenharmony_ci .compatible = "samsung,s5pv210-mipi-video-phy", 3458c2ecf20Sopenharmony_ci .data = &s5pv210_mipi_phy, 3468c2ecf20Sopenharmony_ci }, { 3478c2ecf20Sopenharmony_ci .compatible = "samsung,exynos5420-mipi-video-phy", 3488c2ecf20Sopenharmony_ci .data = &exynos5420_mipi_phy, 3498c2ecf20Sopenharmony_ci }, { 3508c2ecf20Sopenharmony_ci .compatible = "samsung,exynos5433-mipi-video-phy", 3518c2ecf20Sopenharmony_ci .data = &exynos5433_mipi_phy, 3528c2ecf20Sopenharmony_ci }, 3538c2ecf20Sopenharmony_ci { /* sentinel */ }, 3548c2ecf20Sopenharmony_ci}; 3558c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic struct platform_driver exynos_mipi_video_phy_driver = { 3588c2ecf20Sopenharmony_ci .probe = exynos_mipi_video_phy_probe, 3598c2ecf20Sopenharmony_ci .driver = { 3608c2ecf20Sopenharmony_ci .of_match_table = exynos_mipi_video_phy_of_match, 3618c2ecf20Sopenharmony_ci .name = "exynos-mipi-video-phy", 3628c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci}; 3658c2ecf20Sopenharmony_cimodule_platform_driver(exynos_mipi_video_phy_driver); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Samsung S5P/Exynos SoC MIPI CSI-2/DSI PHY driver"); 3688c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); 3698c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 370