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