xref: /openbmc/linux/drivers/gpu/drm/msm/hdmi/hdmi_phy.c (revision fcda50c8)
115b4a452SArchit Taneja /*
215b4a452SArchit Taneja  * Copyright (c) 2016, The Linux Foundation. All rights reserved.
315b4a452SArchit Taneja  *
415b4a452SArchit Taneja  * This program is free software; you can redistribute it and/or modify
515b4a452SArchit Taneja  * it under the terms of the GNU General Public License version 2 and
615b4a452SArchit Taneja  * only version 2 as published by the Free Software Foundation.
715b4a452SArchit Taneja  *
815b4a452SArchit Taneja  * This program is distributed in the hope that it will be useful,
915b4a452SArchit Taneja  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1015b4a452SArchit Taneja  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1115b4a452SArchit Taneja  * GNU General Public License for more details.
1215b4a452SArchit Taneja  */
1315b4a452SArchit Taneja 
1415b4a452SArchit Taneja #include <linux/of_device.h>
1515b4a452SArchit Taneja 
1615b4a452SArchit Taneja #include "hdmi.h"
1715b4a452SArchit Taneja 
18fcda50c8SArnd Bergmann static int msm_hdmi_phy_resource_init(struct hdmi_phy *phy)
1915b4a452SArchit Taneja {
2015b4a452SArchit Taneja 	struct hdmi_phy_cfg *cfg = phy->cfg;
2115b4a452SArchit Taneja 	struct device *dev = &phy->pdev->dev;
2215b4a452SArchit Taneja 	int i, ret;
2315b4a452SArchit Taneja 
2415b4a452SArchit Taneja 	phy->regs = devm_kzalloc(dev, sizeof(phy->regs[0]) * cfg->num_regs,
2515b4a452SArchit Taneja 				 GFP_KERNEL);
2615b4a452SArchit Taneja 	if (!phy->regs)
2715b4a452SArchit Taneja 		return -ENOMEM;
2815b4a452SArchit Taneja 
2915b4a452SArchit Taneja 	phy->clks = devm_kzalloc(dev, sizeof(phy->clks[0]) * cfg->num_clks,
3015b4a452SArchit Taneja 				 GFP_KERNEL);
3115b4a452SArchit Taneja 	if (!phy->clks)
3215b4a452SArchit Taneja 		return -ENOMEM;
3315b4a452SArchit Taneja 
3415b4a452SArchit Taneja 	for (i = 0; i < cfg->num_regs; i++) {
3515b4a452SArchit Taneja 		struct regulator *reg;
3615b4a452SArchit Taneja 
3715b4a452SArchit Taneja 		reg = devm_regulator_get(dev, cfg->reg_names[i]);
3815b4a452SArchit Taneja 		if (IS_ERR(reg)) {
3915b4a452SArchit Taneja 			ret = PTR_ERR(reg);
4015b4a452SArchit Taneja 			dev_err(dev, "failed to get phy regulator: %s (%d)\n",
4115b4a452SArchit Taneja 				cfg->reg_names[i], ret);
4215b4a452SArchit Taneja 			return ret;
4315b4a452SArchit Taneja 		}
4415b4a452SArchit Taneja 
4515b4a452SArchit Taneja 		phy->regs[i] = reg;
4615b4a452SArchit Taneja 	}
4715b4a452SArchit Taneja 
4815b4a452SArchit Taneja 	for (i = 0; i < cfg->num_clks; i++) {
4915b4a452SArchit Taneja 		struct clk *clk;
5015b4a452SArchit Taneja 
5115b4a452SArchit Taneja 		clk = devm_clk_get(dev, cfg->clk_names[i]);
5215b4a452SArchit Taneja 		if (IS_ERR(clk)) {
5315b4a452SArchit Taneja 			ret = PTR_ERR(clk);
5415b4a452SArchit Taneja 			dev_err(dev, "failed to get phy clock: %s (%d)\n",
5515b4a452SArchit Taneja 				cfg->clk_names[i], ret);
5615b4a452SArchit Taneja 			return ret;
5715b4a452SArchit Taneja 		}
5815b4a452SArchit Taneja 
5915b4a452SArchit Taneja 		phy->clks[i] = clk;
6015b4a452SArchit Taneja 	}
6115b4a452SArchit Taneja 
6215b4a452SArchit Taneja 	return 0;
6315b4a452SArchit Taneja }
6415b4a452SArchit Taneja 
65fcda50c8SArnd Bergmann int msm_hdmi_phy_resource_enable(struct hdmi_phy *phy)
6615b4a452SArchit Taneja {
6715b4a452SArchit Taneja 	struct hdmi_phy_cfg *cfg = phy->cfg;
6815b4a452SArchit Taneja 	struct device *dev = &phy->pdev->dev;
6915b4a452SArchit Taneja 	int i, ret = 0;
7015b4a452SArchit Taneja 
7115b4a452SArchit Taneja 	pm_runtime_get_sync(dev);
7215b4a452SArchit Taneja 
7315b4a452SArchit Taneja 	for (i = 0; i < cfg->num_regs; i++) {
7415b4a452SArchit Taneja 		ret = regulator_enable(phy->regs[i]);
7515b4a452SArchit Taneja 		if (ret)
7615b4a452SArchit Taneja 			dev_err(dev, "failed to enable regulator: %s (%d)\n",
7715b4a452SArchit Taneja 				cfg->reg_names[i], ret);
7815b4a452SArchit Taneja 	}
7915b4a452SArchit Taneja 
8015b4a452SArchit Taneja 	for (i = 0; i < cfg->num_clks; i++) {
8115b4a452SArchit Taneja 		ret = clk_prepare_enable(phy->clks[i]);
8215b4a452SArchit Taneja 		if (ret)
8315b4a452SArchit Taneja 			dev_err(dev, "failed to enable clock: %s (%d)\n",
8415b4a452SArchit Taneja 				cfg->clk_names[i], ret);
8515b4a452SArchit Taneja 	}
8615b4a452SArchit Taneja 
8715b4a452SArchit Taneja 	return ret;
8815b4a452SArchit Taneja }
8915b4a452SArchit Taneja 
90fcda50c8SArnd Bergmann void msm_hdmi_phy_resource_disable(struct hdmi_phy *phy)
9115b4a452SArchit Taneja {
9215b4a452SArchit Taneja 	struct hdmi_phy_cfg *cfg = phy->cfg;
9315b4a452SArchit Taneja 	struct device *dev = &phy->pdev->dev;
9415b4a452SArchit Taneja 	int i;
9515b4a452SArchit Taneja 
9615b4a452SArchit Taneja 	for (i = cfg->num_clks - 1; i >= 0; i--)
9715b4a452SArchit Taneja 		clk_disable_unprepare(phy->clks[i]);
9815b4a452SArchit Taneja 
9915b4a452SArchit Taneja 	for (i = cfg->num_regs - 1; i >= 0; i--)
10015b4a452SArchit Taneja 		regulator_disable(phy->regs[i]);
10115b4a452SArchit Taneja 
10215b4a452SArchit Taneja 	pm_runtime_put_sync(dev);
10315b4a452SArchit Taneja }
10415b4a452SArchit Taneja 
105fcda50c8SArnd Bergmann void msm_hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long int pixclock)
10615b4a452SArchit Taneja {
10715b4a452SArchit Taneja 	if (!phy || !phy->cfg->powerup)
10815b4a452SArchit Taneja 		return;
10915b4a452SArchit Taneja 
11015b4a452SArchit Taneja 	phy->cfg->powerup(phy, pixclock);
11115b4a452SArchit Taneja }
11215b4a452SArchit Taneja 
113fcda50c8SArnd Bergmann void msm_hdmi_phy_powerdown(struct hdmi_phy *phy)
11415b4a452SArchit Taneja {
11515b4a452SArchit Taneja 	if (!phy || !phy->cfg->powerdown)
11615b4a452SArchit Taneja 		return;
11715b4a452SArchit Taneja 
11815b4a452SArchit Taneja 	phy->cfg->powerdown(phy);
11915b4a452SArchit Taneja }
12015b4a452SArchit Taneja 
121fcda50c8SArnd Bergmann static int msm_hdmi_phy_pll_init(struct platform_device *pdev,
122ea184891SArchit Taneja 			     enum hdmi_phy_type type)
123ea184891SArchit Taneja {
124ea184891SArchit Taneja 	int ret;
125ea184891SArchit Taneja 
126ea184891SArchit Taneja 	switch (type) {
127ea184891SArchit Taneja 	case MSM_HDMI_PHY_8960:
128fcda50c8SArnd Bergmann 		ret = msm_hdmi_pll_8960_init(pdev);
129ea184891SArchit Taneja 		break;
130e17afdceSArchit Taneja 	case MSM_HDMI_PHY_8996:
131fcda50c8SArnd Bergmann 		ret = msm_hdmi_pll_8996_init(pdev);
132e17afdceSArchit Taneja 		break;
133ea184891SArchit Taneja 	/*
134ea184891SArchit Taneja 	 * we don't have PLL support for these, don't report an error for now
135ea184891SArchit Taneja 	 */
136ea184891SArchit Taneja 	case MSM_HDMI_PHY_8x60:
137ea184891SArchit Taneja 	case MSM_HDMI_PHY_8x74:
138ea184891SArchit Taneja 	default:
139ea184891SArchit Taneja 		ret = 0;
140ea184891SArchit Taneja 		break;
141ea184891SArchit Taneja 	}
142ea184891SArchit Taneja 
143ea184891SArchit Taneja 	return ret;
144ea184891SArchit Taneja }
145ea184891SArchit Taneja 
146fcda50c8SArnd Bergmann static int msm_hdmi_phy_probe(struct platform_device *pdev)
14715b4a452SArchit Taneja {
14815b4a452SArchit Taneja 	struct device *dev = &pdev->dev;
14915b4a452SArchit Taneja 	struct hdmi_phy *phy;
15015b4a452SArchit Taneja 	int ret;
15115b4a452SArchit Taneja 
15215b4a452SArchit Taneja 	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
15315b4a452SArchit Taneja 	if (!phy)
15415b4a452SArchit Taneja 		return -ENODEV;
15515b4a452SArchit Taneja 
15615b4a452SArchit Taneja 	phy->cfg = (struct hdmi_phy_cfg *)of_device_get_match_data(dev);
15715b4a452SArchit Taneja 	if (!phy->cfg)
15815b4a452SArchit Taneja 		return -ENODEV;
15915b4a452SArchit Taneja 
16015b4a452SArchit Taneja 	phy->mmio = msm_ioremap(pdev, "hdmi_phy", "HDMI_PHY");
16115b4a452SArchit Taneja 	if (IS_ERR(phy->mmio)) {
16215b4a452SArchit Taneja 		dev_err(dev, "%s: failed to map phy base\n", __func__);
16315b4a452SArchit Taneja 		return -ENOMEM;
16415b4a452SArchit Taneja 	}
16515b4a452SArchit Taneja 
16615b4a452SArchit Taneja 	phy->pdev = pdev;
16715b4a452SArchit Taneja 
168fcda50c8SArnd Bergmann 	ret = msm_hdmi_phy_resource_init(phy);
16915b4a452SArchit Taneja 	if (ret)
17015b4a452SArchit Taneja 		return ret;
17115b4a452SArchit Taneja 
17215b4a452SArchit Taneja 	pm_runtime_enable(&pdev->dev);
17315b4a452SArchit Taneja 
174fcda50c8SArnd Bergmann 	ret = msm_hdmi_phy_resource_enable(phy);
175ea184891SArchit Taneja 	if (ret)
176ea184891SArchit Taneja 		return ret;
177ea184891SArchit Taneja 
178fcda50c8SArnd Bergmann 	ret = msm_hdmi_phy_pll_init(pdev, phy->cfg->type);
179ea184891SArchit Taneja 	if (ret) {
180ea184891SArchit Taneja 		dev_err(dev, "couldn't init PLL\n");
181fcda50c8SArnd Bergmann 		msm_hdmi_phy_resource_disable(phy);
182ea184891SArchit Taneja 		return ret;
183ea184891SArchit Taneja 	}
184ea184891SArchit Taneja 
185fcda50c8SArnd Bergmann 	msm_hdmi_phy_resource_disable(phy);
186ea184891SArchit Taneja 
18715b4a452SArchit Taneja 	platform_set_drvdata(pdev, phy);
18815b4a452SArchit Taneja 
18915b4a452SArchit Taneja 	return 0;
19015b4a452SArchit Taneja }
19115b4a452SArchit Taneja 
192fcda50c8SArnd Bergmann static int msm_hdmi_phy_remove(struct platform_device *pdev)
19315b4a452SArchit Taneja {
19415b4a452SArchit Taneja 	pm_runtime_disable(&pdev->dev);
19515b4a452SArchit Taneja 
19615b4a452SArchit Taneja 	return 0;
19715b4a452SArchit Taneja }
19815b4a452SArchit Taneja 
199fcda50c8SArnd Bergmann static const struct of_device_id msm_hdmi_phy_dt_match[] = {
20015b4a452SArchit Taneja 	{ .compatible = "qcom,hdmi-phy-8660",
201fcda50c8SArnd Bergmann 	  .data = &msm_hdmi_phy_8x60_cfg },
20215b4a452SArchit Taneja 	{ .compatible = "qcom,hdmi-phy-8960",
203fcda50c8SArnd Bergmann 	  .data = &msm_hdmi_phy_8960_cfg },
20415b4a452SArchit Taneja 	{ .compatible = "qcom,hdmi-phy-8974",
205fcda50c8SArnd Bergmann 	  .data = &msm_hdmi_phy_8x74_cfg },
20615b4a452SArchit Taneja 	{ .compatible = "qcom,hdmi-phy-8084",
207fcda50c8SArnd Bergmann 	  .data = &msm_hdmi_phy_8x74_cfg },
208e17afdceSArchit Taneja 	{ .compatible = "qcom,hdmi-phy-8996",
209fcda50c8SArnd Bergmann 	  .data = &msm_hdmi_phy_8996_cfg },
21015b4a452SArchit Taneja 	{}
21115b4a452SArchit Taneja };
21215b4a452SArchit Taneja 
213fcda50c8SArnd Bergmann static struct platform_driver msm_hdmi_phy_platform_driver = {
214fcda50c8SArnd Bergmann 	.probe      = msm_hdmi_phy_probe,
215fcda50c8SArnd Bergmann 	.remove     = msm_hdmi_phy_remove,
21615b4a452SArchit Taneja 	.driver     = {
21715b4a452SArchit Taneja 		.name   = "msm_hdmi_phy",
218fcda50c8SArnd Bergmann 		.of_match_table = msm_hdmi_phy_dt_match,
21915b4a452SArchit Taneja 	},
22015b4a452SArchit Taneja };
22115b4a452SArchit Taneja 
222fcda50c8SArnd Bergmann void __init msm_hdmi_phy_driver_register(void)
22315b4a452SArchit Taneja {
224fcda50c8SArnd Bergmann 	platform_driver_register(&msm_hdmi_phy_platform_driver);
22515b4a452SArchit Taneja }
22615b4a452SArchit Taneja 
227fcda50c8SArnd Bergmann void __exit msm_hdmi_phy_driver_unregister(void)
22815b4a452SArchit Taneja {
229fcda50c8SArnd Bergmann 	platform_driver_unregister(&msm_hdmi_phy_platform_driver);
23015b4a452SArchit Taneja }
231