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