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