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/mfd/syscon/exynos5-pmu.h> 18#include <linux/of.h> 19#include <linux/of_address.h> 20#include <linux/phy/phy.h> 21#include <linux/platform_device.h> 22#include <linux/regmap.h> 23 24struct exynos_dp_video_phy_drvdata { 25 u32 phy_ctrl_offset; 26}; 27 28struct exynos_dp_video_phy { 29 struct regmap *regs; 30 const struct exynos_dp_video_phy_drvdata *drvdata; 31}; 32 33static 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 EXYNOS5_PHY_ENABLE, EXYNOS5_PHY_ENABLE); 40} 41 42static 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 EXYNOS5_PHY_ENABLE, 0); 49} 50 51static 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 57static const struct exynos_dp_video_phy_drvdata exynos5250_dp_video_phy = { 58 .phy_ctrl_offset = EXYNOS5_DPTX_PHY_CONTROL, 59}; 60 61static const struct exynos_dp_video_phy_drvdata exynos5420_dp_video_phy = { 62 .phy_ctrl_offset = EXYNOS5420_DPTX_PHY_CONTROL, 63}; 64 65static 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}; 75MODULE_DEVICE_TABLE(of, exynos_dp_video_phy_of_match); 76 77static 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 111static 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}; 118module_platform_driver(exynos_dp_video_phy_driver); 119 120MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); 121MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver"); 122MODULE_LICENSE("GPL v2"); 123