xref: /linux/drivers/gpu/drm/msm/hdmi/hdmi_phy.c (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce)
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 
msm_hdmi_phy_resource_init(struct hdmi_phy * phy)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 
msm_hdmi_phy_resource_enable(struct hdmi_phy * phy)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 
msm_hdmi_phy_resource_disable(struct hdmi_phy * phy)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 
msm_hdmi_phy_powerup(struct hdmi_phy * phy,unsigned long int pixclock)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 
msm_hdmi_phy_powerdown(struct hdmi_phy * phy)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 
msm_hdmi_phy_pll_init(struct platform_device * pdev,enum hdmi_phy_type type)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 
msm_hdmi_phy_probe(struct platform_device * pdev)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 
msm_hdmi_phy_remove(struct platform_device * pdev)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 
msm_hdmi_phy_driver_register(void)217 void __init msm_hdmi_phy_driver_register(void)
218 {
219 	platform_driver_register(&msm_hdmi_phy_platform_driver);
220 }
221 
msm_hdmi_phy_driver_unregister(void)222 void __exit msm_hdmi_phy_driver_unregister(void)
223 {
224 	platform_driver_unregister(&msm_hdmi_phy_platform_driver);
225 }
226