xref: /linux/drivers/gpu/drm/msm/hdmi/hdmi_phy.c (revision 55d0969c451159cff86949b38c39171cab962069)
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 	case MSM_HDMI_PHY_8998:
122 		ret = msm_hdmi_pll_8998_init(pdev);
123 		break;
124 	/*
125 	 * we don't have PLL support for these, don't report an error for now
126 	 */
127 	case MSM_HDMI_PHY_8x60:
128 	case MSM_HDMI_PHY_8x74:
129 	default:
130 		ret = 0;
131 		break;
132 	}
133 
134 	return ret;
135 }
136 
137 static int msm_hdmi_phy_probe(struct platform_device *pdev)
138 {
139 	struct device *dev = &pdev->dev;
140 	struct hdmi_phy *phy;
141 	int ret;
142 
143 	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
144 	if (!phy)
145 		return -ENODEV;
146 
147 	phy->cfg = (struct hdmi_phy_cfg *)of_device_get_match_data(dev);
148 	if (!phy->cfg)
149 		return -ENODEV;
150 
151 	phy->mmio = msm_ioremap(pdev, "hdmi_phy");
152 	if (IS_ERR(phy->mmio)) {
153 		DRM_DEV_ERROR(dev, "%s: failed to map phy base\n", __func__);
154 		return -ENOMEM;
155 	}
156 
157 	phy->pdev = pdev;
158 
159 	ret = msm_hdmi_phy_resource_init(phy);
160 	if (ret)
161 		return ret;
162 
163 	pm_runtime_enable(&pdev->dev);
164 
165 	ret = msm_hdmi_phy_resource_enable(phy);
166 	if (ret)
167 		return ret;
168 
169 	ret = msm_hdmi_phy_pll_init(pdev, phy->cfg->type);
170 	if (ret) {
171 		DRM_DEV_ERROR(dev, "couldn't init PLL\n");
172 		msm_hdmi_phy_resource_disable(phy);
173 		return ret;
174 	}
175 
176 	msm_hdmi_phy_resource_disable(phy);
177 
178 	platform_set_drvdata(pdev, phy);
179 
180 	return 0;
181 }
182 
183 static void msm_hdmi_phy_remove(struct platform_device *pdev)
184 {
185 	pm_runtime_disable(&pdev->dev);
186 }
187 
188 static const struct of_device_id msm_hdmi_phy_dt_match[] = {
189 	{ .compatible = "qcom,hdmi-phy-8660",
190 	  .data = &msm_hdmi_phy_8x60_cfg },
191 	{ .compatible = "qcom,hdmi-phy-8960",
192 	  .data = &msm_hdmi_phy_8960_cfg },
193 	{ .compatible = "qcom,hdmi-phy-8974",
194 	  .data = &msm_hdmi_phy_8x74_cfg },
195 	{ .compatible = "qcom,hdmi-phy-8084",
196 	  .data = &msm_hdmi_phy_8x74_cfg },
197 	{ .compatible = "qcom,hdmi-phy-8996",
198 	  .data = &msm_hdmi_phy_8996_cfg },
199 	{ .compatible = "qcom,hdmi-phy-8998",
200 	  .data = &msm_hdmi_phy_8998_cfg },
201 	{}
202 };
203 
204 static struct platform_driver msm_hdmi_phy_platform_driver = {
205 	.probe      = msm_hdmi_phy_probe,
206 	.remove_new = msm_hdmi_phy_remove,
207 	.driver     = {
208 		.name   = "msm_hdmi_phy",
209 		.of_match_table = msm_hdmi_phy_dt_match,
210 	},
211 };
212 
213 void __init msm_hdmi_phy_driver_register(void)
214 {
215 	platform_driver_register(&msm_hdmi_phy_platform_driver);
216 }
217 
218 void __exit msm_hdmi_phy_driver_unregister(void)
219 {
220 	platform_driver_unregister(&msm_hdmi_phy_platform_driver);
221 }
222