xref: /linux/drivers/gpu/drm/msm/hdmi/hdmi_phy.c (revision de848da12f752170c2ebe114804a985314fd5a6a)
197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
215b4a452SArchit Taneja /*
315b4a452SArchit Taneja  * Copyright (c) 2016, The Linux Foundation. All rights reserved.
415b4a452SArchit Taneja  */
515b4a452SArchit Taneja 
6722d4f06SRob Herring #include <linux/of.h>
7722d4f06SRob Herring #include <linux/platform_device.h>
815b4a452SArchit Taneja 
915b4a452SArchit Taneja #include "hdmi.h"
1015b4a452SArchit Taneja 
11fcda50c8SArnd Bergmann static int msm_hdmi_phy_resource_init(struct hdmi_phy *phy)
1215b4a452SArchit Taneja {
1315b4a452SArchit Taneja 	struct hdmi_phy_cfg *cfg = phy->cfg;
1415b4a452SArchit Taneja 	struct device *dev = &phy->pdev->dev;
1515b4a452SArchit Taneja 	int i, ret;
1615b4a452SArchit Taneja 
17a86854d0SKees Cook 	phy->regs = devm_kcalloc(dev, cfg->num_regs, sizeof(phy->regs[0]),
1815b4a452SArchit Taneja 				 GFP_KERNEL);
1915b4a452SArchit Taneja 	if (!phy->regs)
2015b4a452SArchit Taneja 		return -ENOMEM;
2115b4a452SArchit Taneja 
22a86854d0SKees Cook 	phy->clks = devm_kcalloc(dev, cfg->num_clks, sizeof(phy->clks[0]),
2315b4a452SArchit Taneja 				 GFP_KERNEL);
2415b4a452SArchit Taneja 	if (!phy->clks)
2515b4a452SArchit Taneja 		return -ENOMEM;
2615b4a452SArchit Taneja 
2731b3b1f5SDmitry Baryshkov 	for (i = 0; i < cfg->num_regs; i++)
2831b3b1f5SDmitry Baryshkov 		phy->regs[i].supply = cfg->reg_names[i];
2915b4a452SArchit Taneja 
3031b3b1f5SDmitry Baryshkov 	ret = devm_regulator_bulk_get(dev, cfg->num_regs, phy->regs);
3131b3b1f5SDmitry Baryshkov 	if (ret) {
3231b3b1f5SDmitry Baryshkov 		if (ret != -EPROBE_DEFER)
3331b3b1f5SDmitry Baryshkov 			DRM_DEV_ERROR(dev, "failed to get phy regulators: %d\n", ret);
34fd6c798bSBrian Masney 
3515b4a452SArchit Taneja 		return ret;
3615b4a452SArchit Taneja 	}
3715b4a452SArchit Taneja 
3815b4a452SArchit Taneja 	for (i = 0; i < cfg->num_clks; i++) {
3915b4a452SArchit Taneja 		struct clk *clk;
4015b4a452SArchit Taneja 
41aede1e9eSRob Clark 		clk = msm_clk_get(phy->pdev, cfg->clk_names[i]);
4215b4a452SArchit Taneja 		if (IS_ERR(clk)) {
4315b4a452SArchit Taneja 			ret = PTR_ERR(clk);
446a41da17SMamta Shukla 			DRM_DEV_ERROR(dev, "failed to get phy clock: %s (%d)\n",
4515b4a452SArchit Taneja 				cfg->clk_names[i], ret);
4615b4a452SArchit Taneja 			return ret;
4715b4a452SArchit Taneja 		}
4815b4a452SArchit Taneja 
4915b4a452SArchit Taneja 		phy->clks[i] = clk;
5015b4a452SArchit Taneja 	}
5115b4a452SArchit Taneja 
5215b4a452SArchit Taneja 	return 0;
5315b4a452SArchit Taneja }
5415b4a452SArchit Taneja 
55fcda50c8SArnd Bergmann int msm_hdmi_phy_resource_enable(struct hdmi_phy *phy)
5615b4a452SArchit Taneja {
5715b4a452SArchit Taneja 	struct hdmi_phy_cfg *cfg = phy->cfg;
5815b4a452SArchit Taneja 	struct device *dev = &phy->pdev->dev;
5915b4a452SArchit Taneja 	int i, ret = 0;
6015b4a452SArchit Taneja 
6115b4a452SArchit Taneja 	pm_runtime_get_sync(dev);
6215b4a452SArchit Taneja 
6331b3b1f5SDmitry Baryshkov 	ret = regulator_bulk_enable(cfg->num_regs, phy->regs);
6431b3b1f5SDmitry Baryshkov 	if (ret) {
6531b3b1f5SDmitry Baryshkov 		DRM_DEV_ERROR(dev, "failed to enable regulators: (%d)\n", ret);
6631b3b1f5SDmitry Baryshkov 		return ret;
6715b4a452SArchit Taneja 	}
6815b4a452SArchit Taneja 
6915b4a452SArchit Taneja 	for (i = 0; i < cfg->num_clks; i++) {
7015b4a452SArchit Taneja 		ret = clk_prepare_enable(phy->clks[i]);
7115b4a452SArchit Taneja 		if (ret)
726a41da17SMamta Shukla 			DRM_DEV_ERROR(dev, "failed to enable clock: %s (%d)\n",
7315b4a452SArchit Taneja 				cfg->clk_names[i], ret);
7415b4a452SArchit Taneja 	}
7515b4a452SArchit Taneja 
7615b4a452SArchit Taneja 	return ret;
7715b4a452SArchit Taneja }
7815b4a452SArchit Taneja 
79fcda50c8SArnd Bergmann void msm_hdmi_phy_resource_disable(struct hdmi_phy *phy)
8015b4a452SArchit Taneja {
8115b4a452SArchit Taneja 	struct hdmi_phy_cfg *cfg = phy->cfg;
8215b4a452SArchit Taneja 	struct device *dev = &phy->pdev->dev;
8315b4a452SArchit Taneja 	int i;
8415b4a452SArchit Taneja 
8515b4a452SArchit Taneja 	for (i = cfg->num_clks - 1; i >= 0; i--)
8615b4a452SArchit Taneja 		clk_disable_unprepare(phy->clks[i]);
8715b4a452SArchit Taneja 
8831b3b1f5SDmitry Baryshkov 	regulator_bulk_disable(cfg->num_regs, phy->regs);
8915b4a452SArchit Taneja 
9015b4a452SArchit Taneja 	pm_runtime_put_sync(dev);
9115b4a452SArchit Taneja }
9215b4a452SArchit Taneja 
93fcda50c8SArnd Bergmann void msm_hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long int pixclock)
9415b4a452SArchit Taneja {
9515b4a452SArchit Taneja 	if (!phy || !phy->cfg->powerup)
9615b4a452SArchit Taneja 		return;
9715b4a452SArchit Taneja 
9815b4a452SArchit Taneja 	phy->cfg->powerup(phy, pixclock);
9915b4a452SArchit Taneja }
10015b4a452SArchit Taneja 
101fcda50c8SArnd Bergmann void msm_hdmi_phy_powerdown(struct hdmi_phy *phy)
10215b4a452SArchit Taneja {
10315b4a452SArchit Taneja 	if (!phy || !phy->cfg->powerdown)
10415b4a452SArchit Taneja 		return;
10515b4a452SArchit Taneja 
10615b4a452SArchit Taneja 	phy->cfg->powerdown(phy);
10715b4a452SArchit Taneja }
10815b4a452SArchit Taneja 
109fcda50c8SArnd Bergmann static int msm_hdmi_phy_pll_init(struct platform_device *pdev,
110ea184891SArchit Taneja 			     enum hdmi_phy_type type)
111ea184891SArchit Taneja {
112ea184891SArchit Taneja 	int ret;
113ea184891SArchit Taneja 
114ea184891SArchit Taneja 	switch (type) {
115ea184891SArchit Taneja 	case MSM_HDMI_PHY_8960:
116fcda50c8SArnd Bergmann 		ret = msm_hdmi_pll_8960_init(pdev);
117ea184891SArchit Taneja 		break;
118e17afdceSArchit Taneja 	case MSM_HDMI_PHY_8996:
119fcda50c8SArnd Bergmann 		ret = msm_hdmi_pll_8996_init(pdev);
120e17afdceSArchit Taneja 		break;
121*caedbf17SArnaud Vrac 	case MSM_HDMI_PHY_8998:
122*caedbf17SArnaud Vrac 		ret = msm_hdmi_pll_8998_init(pdev);
123*caedbf17SArnaud Vrac 		break;
124ea184891SArchit Taneja 	/*
125ea184891SArchit Taneja 	 * we don't have PLL support for these, don't report an error for now
126ea184891SArchit Taneja 	 */
127ea184891SArchit Taneja 	case MSM_HDMI_PHY_8x60:
128ea184891SArchit Taneja 	case MSM_HDMI_PHY_8x74:
129ea184891SArchit Taneja 	default:
130ea184891SArchit Taneja 		ret = 0;
131ea184891SArchit Taneja 		break;
132ea184891SArchit Taneja 	}
133ea184891SArchit Taneja 
134ea184891SArchit Taneja 	return ret;
135ea184891SArchit Taneja }
136ea184891SArchit Taneja 
137fcda50c8SArnd Bergmann static int msm_hdmi_phy_probe(struct platform_device *pdev)
13815b4a452SArchit Taneja {
13915b4a452SArchit Taneja 	struct device *dev = &pdev->dev;
14015b4a452SArchit Taneja 	struct hdmi_phy *phy;
14115b4a452SArchit Taneja 	int ret;
14215b4a452SArchit Taneja 
14315b4a452SArchit Taneja 	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
14415b4a452SArchit Taneja 	if (!phy)
14515b4a452SArchit Taneja 		return -ENODEV;
14615b4a452SArchit Taneja 
14715b4a452SArchit Taneja 	phy->cfg = (struct hdmi_phy_cfg *)of_device_get_match_data(dev);
14815b4a452SArchit Taneja 	if (!phy->cfg)
14915b4a452SArchit Taneja 		return -ENODEV;
15015b4a452SArchit Taneja 
151c0e745d7SDmitry Baryshkov 	phy->mmio = msm_ioremap(pdev, "hdmi_phy");
15215b4a452SArchit Taneja 	if (IS_ERR(phy->mmio)) {
1536a41da17SMamta Shukla 		DRM_DEV_ERROR(dev, "%s: failed to map phy base\n", __func__);
15415b4a452SArchit Taneja 		return -ENOMEM;
15515b4a452SArchit Taneja 	}
15615b4a452SArchit Taneja 
15715b4a452SArchit Taneja 	phy->pdev = pdev;
15815b4a452SArchit Taneja 
159fcda50c8SArnd Bergmann 	ret = msm_hdmi_phy_resource_init(phy);
16015b4a452SArchit Taneja 	if (ret)
16115b4a452SArchit Taneja 		return ret;
16215b4a452SArchit Taneja 
16315b4a452SArchit Taneja 	pm_runtime_enable(&pdev->dev);
16415b4a452SArchit Taneja 
165fcda50c8SArnd Bergmann 	ret = msm_hdmi_phy_resource_enable(phy);
166ea184891SArchit Taneja 	if (ret)
167ea184891SArchit Taneja 		return ret;
168ea184891SArchit Taneja 
169fcda50c8SArnd Bergmann 	ret = msm_hdmi_phy_pll_init(pdev, phy->cfg->type);
170ea184891SArchit Taneja 	if (ret) {
1716a41da17SMamta Shukla 		DRM_DEV_ERROR(dev, "couldn't init PLL\n");
172fcda50c8SArnd Bergmann 		msm_hdmi_phy_resource_disable(phy);
173ea184891SArchit Taneja 		return ret;
174ea184891SArchit Taneja 	}
175ea184891SArchit Taneja 
176fcda50c8SArnd Bergmann 	msm_hdmi_phy_resource_disable(phy);
177ea184891SArchit Taneja 
17815b4a452SArchit Taneja 	platform_set_drvdata(pdev, phy);
17915b4a452SArchit Taneja 
18015b4a452SArchit Taneja 	return 0;
18115b4a452SArchit Taneja }
18215b4a452SArchit Taneja 
18301790d5eSUwe Kleine-König static void msm_hdmi_phy_remove(struct platform_device *pdev)
18415b4a452SArchit Taneja {
18515b4a452SArchit Taneja 	pm_runtime_disable(&pdev->dev);
18615b4a452SArchit Taneja }
18715b4a452SArchit Taneja 
188fcda50c8SArnd Bergmann static const struct of_device_id msm_hdmi_phy_dt_match[] = {
18915b4a452SArchit Taneja 	{ .compatible = "qcom,hdmi-phy-8660",
190fcda50c8SArnd Bergmann 	  .data = &msm_hdmi_phy_8x60_cfg },
19115b4a452SArchit Taneja 	{ .compatible = "qcom,hdmi-phy-8960",
192fcda50c8SArnd Bergmann 	  .data = &msm_hdmi_phy_8960_cfg },
19315b4a452SArchit Taneja 	{ .compatible = "qcom,hdmi-phy-8974",
194fcda50c8SArnd Bergmann 	  .data = &msm_hdmi_phy_8x74_cfg },
19515b4a452SArchit Taneja 	{ .compatible = "qcom,hdmi-phy-8084",
196fcda50c8SArnd Bergmann 	  .data = &msm_hdmi_phy_8x74_cfg },
197e17afdceSArchit Taneja 	{ .compatible = "qcom,hdmi-phy-8996",
198fcda50c8SArnd Bergmann 	  .data = &msm_hdmi_phy_8996_cfg },
199*caedbf17SArnaud Vrac 	{ .compatible = "qcom,hdmi-phy-8998",
200*caedbf17SArnaud Vrac 	  .data = &msm_hdmi_phy_8998_cfg },
20115b4a452SArchit Taneja 	{}
20215b4a452SArchit Taneja };
20315b4a452SArchit Taneja 
204fcda50c8SArnd Bergmann static struct platform_driver msm_hdmi_phy_platform_driver = {
205fcda50c8SArnd Bergmann 	.probe      = msm_hdmi_phy_probe,
20601790d5eSUwe Kleine-König 	.remove_new = msm_hdmi_phy_remove,
20715b4a452SArchit Taneja 	.driver     = {
20815b4a452SArchit Taneja 		.name   = "msm_hdmi_phy",
209fcda50c8SArnd Bergmann 		.of_match_table = msm_hdmi_phy_dt_match,
21015b4a452SArchit Taneja 	},
21115b4a452SArchit Taneja };
21215b4a452SArchit Taneja 
213fcda50c8SArnd Bergmann void __init msm_hdmi_phy_driver_register(void)
21415b4a452SArchit Taneja {
215fcda50c8SArnd Bergmann 	platform_driver_register(&msm_hdmi_phy_platform_driver);
21615b4a452SArchit Taneja }
21715b4a452SArchit Taneja 
218fcda50c8SArnd Bergmann void __exit msm_hdmi_phy_driver_unregister(void)
21915b4a452SArchit Taneja {
220fcda50c8SArnd Bergmann 	platform_driver_unregister(&msm_hdmi_phy_platform_driver);
22115b4a452SArchit Taneja }
222