xref: /openbmc/linux/drivers/gpu/drm/msm/hdmi/hdmi_phy.c (revision ecd25094)
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