1 /* 2 * Samsung EXYNOS SoC series Display Port PHY driver 3 * 4 * Copyright (C) 2013 Samsung Electronics Co., Ltd. 5 * Author: Jingoo Han <jg1.han@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/module.h> 16 #include <linux/mfd/syscon.h> 17 #include <linux/of.h> 18 #include <linux/of_address.h> 19 #include <linux/phy/phy.h> 20 #include <linux/platform_device.h> 21 #include <linux/regmap.h> 22 #include <linux/soc/samsung/exynos-regs-pmu.h> 23 24 struct exynos_dp_video_phy_drvdata { 25 u32 phy_ctrl_offset; 26 }; 27 28 struct exynos_dp_video_phy { 29 struct regmap *regs; 30 const struct exynos_dp_video_phy_drvdata *drvdata; 31 }; 32 33 static int exynos_dp_video_phy_power_on(struct phy *phy) 34 { 35 struct exynos_dp_video_phy *state = phy_get_drvdata(phy); 36 37 /* Disable power isolation on DP-PHY */ 38 return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset, 39 EXYNOS4_PHY_ENABLE, EXYNOS4_PHY_ENABLE); 40 } 41 42 static int exynos_dp_video_phy_power_off(struct phy *phy) 43 { 44 struct exynos_dp_video_phy *state = phy_get_drvdata(phy); 45 46 /* Enable power isolation on DP-PHY */ 47 return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset, 48 EXYNOS4_PHY_ENABLE, 0); 49 } 50 51 static const struct phy_ops exynos_dp_video_phy_ops = { 52 .power_on = exynos_dp_video_phy_power_on, 53 .power_off = exynos_dp_video_phy_power_off, 54 .owner = THIS_MODULE, 55 }; 56 57 static const struct exynos_dp_video_phy_drvdata exynos5250_dp_video_phy = { 58 .phy_ctrl_offset = EXYNOS5_DPTX_PHY_CONTROL, 59 }; 60 61 static const struct exynos_dp_video_phy_drvdata exynos5420_dp_video_phy = { 62 .phy_ctrl_offset = EXYNOS5420_DPTX_PHY_CONTROL, 63 }; 64 65 static const struct of_device_id exynos_dp_video_phy_of_match[] = { 66 { 67 .compatible = "samsung,exynos5250-dp-video-phy", 68 .data = &exynos5250_dp_video_phy, 69 }, { 70 .compatible = "samsung,exynos5420-dp-video-phy", 71 .data = &exynos5420_dp_video_phy, 72 }, 73 { }, 74 }; 75 MODULE_DEVICE_TABLE(of, exynos_dp_video_phy_of_match); 76 77 static int exynos_dp_video_phy_probe(struct platform_device *pdev) 78 { 79 struct exynos_dp_video_phy *state; 80 struct device *dev = &pdev->dev; 81 const struct of_device_id *match; 82 struct phy_provider *phy_provider; 83 struct phy *phy; 84 85 state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); 86 if (!state) 87 return -ENOMEM; 88 89 state->regs = syscon_regmap_lookup_by_phandle(dev->of_node, 90 "samsung,pmu-syscon"); 91 if (IS_ERR(state->regs)) { 92 dev_err(dev, "Failed to lookup PMU regmap\n"); 93 return PTR_ERR(state->regs); 94 } 95 96 match = of_match_node(exynos_dp_video_phy_of_match, dev->of_node); 97 state->drvdata = match->data; 98 99 phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops); 100 if (IS_ERR(phy)) { 101 dev_err(dev, "failed to create Display Port PHY\n"); 102 return PTR_ERR(phy); 103 } 104 phy_set_drvdata(phy, state); 105 106 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 107 108 return PTR_ERR_OR_ZERO(phy_provider); 109 } 110 111 static struct platform_driver exynos_dp_video_phy_driver = { 112 .probe = exynos_dp_video_phy_probe, 113 .driver = { 114 .name = "exynos-dp-video-phy", 115 .of_match_table = exynos_dp_video_phy_of_match, 116 } 117 }; 118 module_platform_driver(exynos_dp_video_phy_driver); 119 120 MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); 121 MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver"); 122 MODULE_LICENSE("GPL v2"); 123