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.h> 7 #include <linux/platform_device.h> 8 9 #include "hdmi.h" 10 11 static int msm_hdmi_phy_resource_init(struct hdmi_phy *phy) 12 { 13 struct hdmi_phy_cfg *cfg = phy->cfg; 14 struct device *dev = &phy->pdev->dev; 15 int i, ret; 16 17 phy->regs = devm_kcalloc(dev, cfg->num_regs, sizeof(phy->regs[0]), 18 GFP_KERNEL); 19 if (!phy->regs) 20 return -ENOMEM; 21 22 phy->clks = devm_kcalloc(dev, cfg->num_clks, sizeof(phy->clks[0]), 23 GFP_KERNEL); 24 if (!phy->clks) 25 return -ENOMEM; 26 27 for (i = 0; i < cfg->num_regs; i++) 28 phy->regs[i].supply = cfg->reg_names[i]; 29 30 ret = devm_regulator_bulk_get(dev, cfg->num_regs, phy->regs); 31 if (ret) { 32 if (ret != -EPROBE_DEFER) 33 DRM_DEV_ERROR(dev, "failed to get phy regulators: %d\n", ret); 34 35 return ret; 36 } 37 38 for (i = 0; i < cfg->num_clks; i++) { 39 struct clk *clk; 40 41 clk = msm_clk_get(phy->pdev, cfg->clk_names[i]); 42 if (IS_ERR(clk)) { 43 ret = PTR_ERR(clk); 44 DRM_DEV_ERROR(dev, "failed to get phy clock: %s (%d)\n", 45 cfg->clk_names[i], ret); 46 return ret; 47 } 48 49 phy->clks[i] = clk; 50 } 51 52 return 0; 53 } 54 55 int msm_hdmi_phy_resource_enable(struct hdmi_phy *phy) 56 { 57 struct hdmi_phy_cfg *cfg = phy->cfg; 58 struct device *dev = &phy->pdev->dev; 59 int i, ret = 0; 60 61 ret = pm_runtime_resume_and_get(dev); 62 if (ret) { 63 DRM_DEV_ERROR(dev, "runtime resume failed: %d\n", ret); 64 return ret; 65 } 66 67 ret = regulator_bulk_enable(cfg->num_regs, phy->regs); 68 if (ret) { 69 DRM_DEV_ERROR(dev, "failed to enable regulators: (%d)\n", ret); 70 return ret; 71 } 72 73 for (i = 0; i < cfg->num_clks; i++) { 74 ret = clk_prepare_enable(phy->clks[i]); 75 if (ret) 76 DRM_DEV_ERROR(dev, "failed to enable clock: %s (%d)\n", 77 cfg->clk_names[i], ret); 78 } 79 80 return ret; 81 } 82 83 void msm_hdmi_phy_resource_disable(struct hdmi_phy *phy) 84 { 85 struct hdmi_phy_cfg *cfg = phy->cfg; 86 struct device *dev = &phy->pdev->dev; 87 int i; 88 89 for (i = cfg->num_clks - 1; i >= 0; i--) 90 clk_disable_unprepare(phy->clks[i]); 91 92 regulator_bulk_disable(cfg->num_regs, phy->regs); 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 case MSM_HDMI_PHY_8998: 126 ret = msm_hdmi_pll_8998_init(pdev); 127 break; 128 /* 129 * we don't have PLL support for these, don't report an error for now 130 */ 131 case MSM_HDMI_PHY_8x60: 132 case MSM_HDMI_PHY_8x74: 133 default: 134 ret = 0; 135 break; 136 } 137 138 return ret; 139 } 140 141 static int msm_hdmi_phy_probe(struct platform_device *pdev) 142 { 143 struct device *dev = &pdev->dev; 144 struct hdmi_phy *phy; 145 int ret; 146 147 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); 148 if (!phy) 149 return -ENODEV; 150 151 phy->cfg = (struct hdmi_phy_cfg *)of_device_get_match_data(dev); 152 if (!phy->cfg) 153 return -ENODEV; 154 155 phy->mmio = msm_ioremap(pdev, "hdmi_phy"); 156 if (IS_ERR(phy->mmio)) { 157 DRM_DEV_ERROR(dev, "%s: failed to map phy base\n", __func__); 158 return -ENOMEM; 159 } 160 161 phy->pdev = pdev; 162 163 ret = msm_hdmi_phy_resource_init(phy); 164 if (ret) 165 return ret; 166 167 pm_runtime_enable(&pdev->dev); 168 169 ret = msm_hdmi_phy_resource_enable(phy); 170 if (ret) 171 return ret; 172 173 ret = msm_hdmi_phy_pll_init(pdev, phy->cfg->type); 174 if (ret) { 175 DRM_DEV_ERROR(dev, "couldn't init PLL\n"); 176 msm_hdmi_phy_resource_disable(phy); 177 return ret; 178 } 179 180 msm_hdmi_phy_resource_disable(phy); 181 182 platform_set_drvdata(pdev, phy); 183 184 return 0; 185 } 186 187 static void msm_hdmi_phy_remove(struct platform_device *pdev) 188 { 189 pm_runtime_disable(&pdev->dev); 190 } 191 192 static const struct of_device_id msm_hdmi_phy_dt_match[] = { 193 { .compatible = "qcom,hdmi-phy-8660", 194 .data = &msm_hdmi_phy_8x60_cfg }, 195 { .compatible = "qcom,hdmi-phy-8960", 196 .data = &msm_hdmi_phy_8960_cfg }, 197 { .compatible = "qcom,hdmi-phy-8974", 198 .data = &msm_hdmi_phy_8x74_cfg }, 199 { .compatible = "qcom,hdmi-phy-8084", 200 .data = &msm_hdmi_phy_8x74_cfg }, 201 { .compatible = "qcom,hdmi-phy-8996", 202 .data = &msm_hdmi_phy_8996_cfg }, 203 { .compatible = "qcom,hdmi-phy-8998", 204 .data = &msm_hdmi_phy_8998_cfg }, 205 {} 206 }; 207 208 static struct platform_driver msm_hdmi_phy_platform_driver = { 209 .probe = msm_hdmi_phy_probe, 210 .remove = msm_hdmi_phy_remove, 211 .driver = { 212 .name = "msm_hdmi_phy", 213 .of_match_table = msm_hdmi_phy_dt_match, 214 }, 215 }; 216 217 void __init msm_hdmi_phy_driver_register(void) 218 { 219 platform_driver_register(&msm_hdmi_phy_platform_driver); 220 } 221 222 void __exit msm_hdmi_phy_driver_unregister(void) 223 { 224 platform_driver_unregister(&msm_hdmi_phy_platform_driver); 225 } 226