18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2016, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/of_device.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "hdmi.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_cistatic int msm_hdmi_phy_resource_init(struct hdmi_phy *phy) 118c2ecf20Sopenharmony_ci{ 128c2ecf20Sopenharmony_ci struct hdmi_phy_cfg *cfg = phy->cfg; 138c2ecf20Sopenharmony_ci struct device *dev = &phy->pdev->dev; 148c2ecf20Sopenharmony_ci int i, ret; 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci phy->regs = devm_kcalloc(dev, cfg->num_regs, sizeof(phy->regs[0]), 178c2ecf20Sopenharmony_ci GFP_KERNEL); 188c2ecf20Sopenharmony_ci if (!phy->regs) 198c2ecf20Sopenharmony_ci return -ENOMEM; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci phy->clks = devm_kcalloc(dev, cfg->num_clks, sizeof(phy->clks[0]), 228c2ecf20Sopenharmony_ci GFP_KERNEL); 238c2ecf20Sopenharmony_ci if (!phy->clks) 248c2ecf20Sopenharmony_ci return -ENOMEM; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_regs; i++) { 278c2ecf20Sopenharmony_ci struct regulator *reg; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci reg = devm_regulator_get(dev, cfg->reg_names[i]); 308c2ecf20Sopenharmony_ci if (IS_ERR(reg)) { 318c2ecf20Sopenharmony_ci ret = PTR_ERR(reg); 328c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) { 338c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, 348c2ecf20Sopenharmony_ci "failed to get phy regulator: %s (%d)\n", 358c2ecf20Sopenharmony_ci cfg->reg_names[i], ret); 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci return ret; 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci phy->regs[i] = reg; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_clks; i++) { 458c2ecf20Sopenharmony_ci struct clk *clk; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci clk = msm_clk_get(phy->pdev, cfg->clk_names[i]); 488c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 498c2ecf20Sopenharmony_ci ret = PTR_ERR(clk); 508c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to get phy clock: %s (%d)\n", 518c2ecf20Sopenharmony_ci cfg->clk_names[i], ret); 528c2ecf20Sopenharmony_ci return ret; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci phy->clks[i] = clk; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return 0; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ciint msm_hdmi_phy_resource_enable(struct hdmi_phy *phy) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct hdmi_phy_cfg *cfg = phy->cfg; 648c2ecf20Sopenharmony_ci struct device *dev = &phy->pdev->dev; 658c2ecf20Sopenharmony_ci int i, ret = 0; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_regs; i++) { 708c2ecf20Sopenharmony_ci ret = regulator_enable(phy->regs[i]); 718c2ecf20Sopenharmony_ci if (ret) 728c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to enable regulator: %s (%d)\n", 738c2ecf20Sopenharmony_ci cfg->reg_names[i], ret); 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_clks; i++) { 778c2ecf20Sopenharmony_ci ret = clk_prepare_enable(phy->clks[i]); 788c2ecf20Sopenharmony_ci if (ret) 798c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to enable clock: %s (%d)\n", 808c2ecf20Sopenharmony_ci cfg->clk_names[i], ret); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return ret; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_civoid msm_hdmi_phy_resource_disable(struct hdmi_phy *phy) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct hdmi_phy_cfg *cfg = phy->cfg; 898c2ecf20Sopenharmony_ci struct device *dev = &phy->pdev->dev; 908c2ecf20Sopenharmony_ci int i; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci for (i = cfg->num_clks - 1; i >= 0; i--) 938c2ecf20Sopenharmony_ci clk_disable_unprepare(phy->clks[i]); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci for (i = cfg->num_regs - 1; i >= 0; i--) 968c2ecf20Sopenharmony_ci regulator_disable(phy->regs[i]); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_civoid msm_hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long int pixclock) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci if (!phy || !phy->cfg->powerup) 1048c2ecf20Sopenharmony_ci return; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci phy->cfg->powerup(phy, pixclock); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_civoid msm_hdmi_phy_powerdown(struct hdmi_phy *phy) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci if (!phy || !phy->cfg->powerdown) 1128c2ecf20Sopenharmony_ci return; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci phy->cfg->powerdown(phy); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int msm_hdmi_phy_pll_init(struct platform_device *pdev, 1188c2ecf20Sopenharmony_ci enum hdmi_phy_type type) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci int ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci switch (type) { 1238c2ecf20Sopenharmony_ci case MSM_HDMI_PHY_8960: 1248c2ecf20Sopenharmony_ci ret = msm_hdmi_pll_8960_init(pdev); 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci case MSM_HDMI_PHY_8996: 1278c2ecf20Sopenharmony_ci ret = msm_hdmi_pll_8996_init(pdev); 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci /* 1308c2ecf20Sopenharmony_ci * we don't have PLL support for these, don't report an error for now 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci case MSM_HDMI_PHY_8x60: 1338c2ecf20Sopenharmony_ci case MSM_HDMI_PHY_8x74: 1348c2ecf20Sopenharmony_ci default: 1358c2ecf20Sopenharmony_ci ret = 0; 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int msm_hdmi_phy_probe(struct platform_device *pdev) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1458c2ecf20Sopenharmony_ci struct hdmi_phy *phy; 1468c2ecf20Sopenharmony_ci int ret; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); 1498c2ecf20Sopenharmony_ci if (!phy) 1508c2ecf20Sopenharmony_ci return -ENODEV; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci phy->cfg = (struct hdmi_phy_cfg *)of_device_get_match_data(dev); 1538c2ecf20Sopenharmony_ci if (!phy->cfg) 1548c2ecf20Sopenharmony_ci return -ENODEV; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci phy->mmio = msm_ioremap(pdev, "hdmi_phy", "HDMI_PHY"); 1578c2ecf20Sopenharmony_ci if (IS_ERR(phy->mmio)) { 1588c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "%s: failed to map phy base\n", __func__); 1598c2ecf20Sopenharmony_ci return -ENOMEM; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci phy->pdev = pdev; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ret = msm_hdmi_phy_resource_init(phy); 1658c2ecf20Sopenharmony_ci if (ret) 1668c2ecf20Sopenharmony_ci return ret; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci ret = msm_hdmi_phy_resource_enable(phy); 1718c2ecf20Sopenharmony_ci if (ret) 1728c2ecf20Sopenharmony_ci return ret; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci ret = msm_hdmi_phy_pll_init(pdev, phy->cfg->type); 1758c2ecf20Sopenharmony_ci if (ret) { 1768c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "couldn't init PLL\n"); 1778c2ecf20Sopenharmony_ci msm_hdmi_phy_resource_disable(phy); 1788c2ecf20Sopenharmony_ci return ret; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci msm_hdmi_phy_resource_disable(phy); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, phy); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int msm_hdmi_phy_remove(struct platform_device *pdev) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic const struct of_device_id msm_hdmi_phy_dt_match[] = { 1968c2ecf20Sopenharmony_ci { .compatible = "qcom,hdmi-phy-8660", 1978c2ecf20Sopenharmony_ci .data = &msm_hdmi_phy_8x60_cfg }, 1988c2ecf20Sopenharmony_ci { .compatible = "qcom,hdmi-phy-8960", 1998c2ecf20Sopenharmony_ci .data = &msm_hdmi_phy_8960_cfg }, 2008c2ecf20Sopenharmony_ci { .compatible = "qcom,hdmi-phy-8974", 2018c2ecf20Sopenharmony_ci .data = &msm_hdmi_phy_8x74_cfg }, 2028c2ecf20Sopenharmony_ci { .compatible = "qcom,hdmi-phy-8084", 2038c2ecf20Sopenharmony_ci .data = &msm_hdmi_phy_8x74_cfg }, 2048c2ecf20Sopenharmony_ci { .compatible = "qcom,hdmi-phy-8996", 2058c2ecf20Sopenharmony_ci .data = &msm_hdmi_phy_8996_cfg }, 2068c2ecf20Sopenharmony_ci {} 2078c2ecf20Sopenharmony_ci}; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic struct platform_driver msm_hdmi_phy_platform_driver = { 2108c2ecf20Sopenharmony_ci .probe = msm_hdmi_phy_probe, 2118c2ecf20Sopenharmony_ci .remove = msm_hdmi_phy_remove, 2128c2ecf20Sopenharmony_ci .driver = { 2138c2ecf20Sopenharmony_ci .name = "msm_hdmi_phy", 2148c2ecf20Sopenharmony_ci .of_match_table = msm_hdmi_phy_dt_match, 2158c2ecf20Sopenharmony_ci }, 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_civoid __init msm_hdmi_phy_driver_register(void) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci platform_driver_register(&msm_hdmi_phy_platform_driver); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_civoid __exit msm_hdmi_phy_driver_unregister(void) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci platform_driver_unregister(&msm_hdmi_phy_platform_driver); 2268c2ecf20Sopenharmony_ci} 227