1 /* 2 * Copyright (c) 2016, The Linux Foundation. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 and 6 * only version 2 as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 14 #include <linux/of_device.h> 15 16 #include "hdmi.h" 17 18 static int msm_hdmi_phy_resource_init(struct hdmi_phy *phy) 19 { 20 struct hdmi_phy_cfg *cfg = phy->cfg; 21 struct device *dev = &phy->pdev->dev; 22 int i, ret; 23 24 phy->regs = devm_kcalloc(dev, cfg->num_regs, sizeof(phy->regs[0]), 25 GFP_KERNEL); 26 if (!phy->regs) 27 return -ENOMEM; 28 29 phy->clks = devm_kcalloc(dev, cfg->num_clks, sizeof(phy->clks[0]), 30 GFP_KERNEL); 31 if (!phy->clks) 32 return -ENOMEM; 33 34 for (i = 0; i < cfg->num_regs; i++) { 35 struct regulator *reg; 36 37 reg = devm_regulator_get(dev, cfg->reg_names[i]); 38 if (IS_ERR(reg)) { 39 ret = PTR_ERR(reg); 40 dev_err(dev, "failed to get phy regulator: %s (%d)\n", 41 cfg->reg_names[i], ret); 42 return ret; 43 } 44 45 phy->regs[i] = reg; 46 } 47 48 for (i = 0; i < cfg->num_clks; i++) { 49 struct clk *clk; 50 51 clk = msm_clk_get(phy->pdev, cfg->clk_names[i]); 52 if (IS_ERR(clk)) { 53 ret = PTR_ERR(clk); 54 dev_err(dev, "failed to get phy clock: %s (%d)\n", 55 cfg->clk_names[i], ret); 56 return ret; 57 } 58 59 phy->clks[i] = clk; 60 } 61 62 return 0; 63 } 64 65 int msm_hdmi_phy_resource_enable(struct hdmi_phy *phy) 66 { 67 struct hdmi_phy_cfg *cfg = phy->cfg; 68 struct device *dev = &phy->pdev->dev; 69 int i, ret = 0; 70 71 pm_runtime_get_sync(dev); 72 73 for (i = 0; i < cfg->num_regs; i++) { 74 ret = regulator_enable(phy->regs[i]); 75 if (ret) 76 dev_err(dev, "failed to enable regulator: %s (%d)\n", 77 cfg->reg_names[i], ret); 78 } 79 80 for (i = 0; i < cfg->num_clks; i++) { 81 ret = clk_prepare_enable(phy->clks[i]); 82 if (ret) 83 dev_err(dev, "failed to enable clock: %s (%d)\n", 84 cfg->clk_names[i], ret); 85 } 86 87 return ret; 88 } 89 90 void msm_hdmi_phy_resource_disable(struct hdmi_phy *phy) 91 { 92 struct hdmi_phy_cfg *cfg = phy->cfg; 93 struct device *dev = &phy->pdev->dev; 94 int i; 95 96 for (i = cfg->num_clks - 1; i >= 0; i--) 97 clk_disable_unprepare(phy->clks[i]); 98 99 for (i = cfg->num_regs - 1; i >= 0; i--) 100 regulator_disable(phy->regs[i]); 101 102 pm_runtime_put_sync(dev); 103 } 104 105 void msm_hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long int pixclock) 106 { 107 if (!phy || !phy->cfg->powerup) 108 return; 109 110 phy->cfg->powerup(phy, pixclock); 111 } 112 113 void msm_hdmi_phy_powerdown(struct hdmi_phy *phy) 114 { 115 if (!phy || !phy->cfg->powerdown) 116 return; 117 118 phy->cfg->powerdown(phy); 119 } 120 121 static int msm_hdmi_phy_pll_init(struct platform_device *pdev, 122 enum hdmi_phy_type type) 123 { 124 int ret; 125 126 switch (type) { 127 case MSM_HDMI_PHY_8960: 128 ret = msm_hdmi_pll_8960_init(pdev); 129 break; 130 case MSM_HDMI_PHY_8996: 131 ret = msm_hdmi_pll_8996_init(pdev); 132 break; 133 /* 134 * we don't have PLL support for these, don't report an error for now 135 */ 136 case MSM_HDMI_PHY_8x60: 137 case MSM_HDMI_PHY_8x74: 138 default: 139 ret = 0; 140 break; 141 } 142 143 return ret; 144 } 145 146 static int msm_hdmi_phy_probe(struct platform_device *pdev) 147 { 148 struct device *dev = &pdev->dev; 149 struct hdmi_phy *phy; 150 int ret; 151 152 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); 153 if (!phy) 154 return -ENODEV; 155 156 phy->cfg = (struct hdmi_phy_cfg *)of_device_get_match_data(dev); 157 if (!phy->cfg) 158 return -ENODEV; 159 160 phy->mmio = msm_ioremap(pdev, "hdmi_phy", "HDMI_PHY"); 161 if (IS_ERR(phy->mmio)) { 162 dev_err(dev, "%s: failed to map phy base\n", __func__); 163 return -ENOMEM; 164 } 165 166 phy->pdev = pdev; 167 168 ret = msm_hdmi_phy_resource_init(phy); 169 if (ret) 170 return ret; 171 172 pm_runtime_enable(&pdev->dev); 173 174 ret = msm_hdmi_phy_resource_enable(phy); 175 if (ret) 176 return ret; 177 178 ret = msm_hdmi_phy_pll_init(pdev, phy->cfg->type); 179 if (ret) { 180 dev_err(dev, "couldn't init PLL\n"); 181 msm_hdmi_phy_resource_disable(phy); 182 return ret; 183 } 184 185 msm_hdmi_phy_resource_disable(phy); 186 187 platform_set_drvdata(pdev, phy); 188 189 return 0; 190 } 191 192 static int msm_hdmi_phy_remove(struct platform_device *pdev) 193 { 194 pm_runtime_disable(&pdev->dev); 195 196 return 0; 197 } 198 199 static const struct of_device_id msm_hdmi_phy_dt_match[] = { 200 { .compatible = "qcom,hdmi-phy-8660", 201 .data = &msm_hdmi_phy_8x60_cfg }, 202 { .compatible = "qcom,hdmi-phy-8960", 203 .data = &msm_hdmi_phy_8960_cfg }, 204 { .compatible = "qcom,hdmi-phy-8974", 205 .data = &msm_hdmi_phy_8x74_cfg }, 206 { .compatible = "qcom,hdmi-phy-8084", 207 .data = &msm_hdmi_phy_8x74_cfg }, 208 { .compatible = "qcom,hdmi-phy-8996", 209 .data = &msm_hdmi_phy_8996_cfg }, 210 {} 211 }; 212 213 static struct platform_driver msm_hdmi_phy_platform_driver = { 214 .probe = msm_hdmi_phy_probe, 215 .remove = msm_hdmi_phy_remove, 216 .driver = { 217 .name = "msm_hdmi_phy", 218 .of_match_table = msm_hdmi_phy_dt_match, 219 }, 220 }; 221 222 void __init msm_hdmi_phy_driver_register(void) 223 { 224 platform_driver_register(&msm_hdmi_phy_platform_driver); 225 } 226 227 void __exit msm_hdmi_phy_driver_unregister(void) 228 { 229 platform_driver_unregister(&msm_hdmi_phy_platform_driver); 230 } 231