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