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 1815b4a452SArchit Taneja static int 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 6515b4a452SArchit Taneja int 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 9015b4a452SArchit Taneja void 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 10515b4a452SArchit Taneja void 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 11315b4a452SArchit Taneja void 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 121ea184891SArchit Taneja static int 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: 128ea184891SArchit Taneja ret = hdmi_pll_8960_init(pdev); 129ea184891SArchit Taneja break; 130*e17afdceSArchit Taneja case MSM_HDMI_PHY_8996: 131*e17afdceSArchit Taneja ret = hdmi_pll_8996_init(pdev); 132*e17afdceSArchit 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 14615b4a452SArchit Taneja static int 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 16815b4a452SArchit Taneja ret = hdmi_phy_resource_init(phy); 16915b4a452SArchit Taneja if (ret) 17015b4a452SArchit Taneja return ret; 17115b4a452SArchit Taneja 17215b4a452SArchit Taneja pm_runtime_enable(&pdev->dev); 17315b4a452SArchit Taneja 174ea184891SArchit Taneja ret = hdmi_phy_resource_enable(phy); 175ea184891SArchit Taneja if (ret) 176ea184891SArchit Taneja return ret; 177ea184891SArchit Taneja 178ea184891SArchit Taneja ret = hdmi_phy_pll_init(pdev, phy->cfg->type); 179ea184891SArchit Taneja if (ret) { 180ea184891SArchit Taneja dev_err(dev, "couldn't init PLL\n"); 181ea184891SArchit Taneja hdmi_phy_resource_disable(phy); 182ea184891SArchit Taneja return ret; 183ea184891SArchit Taneja } 184ea184891SArchit Taneja 185ea184891SArchit Taneja hdmi_phy_resource_disable(phy); 186ea184891SArchit Taneja 18715b4a452SArchit Taneja platform_set_drvdata(pdev, phy); 18815b4a452SArchit Taneja 18915b4a452SArchit Taneja return 0; 19015b4a452SArchit Taneja } 19115b4a452SArchit Taneja 19215b4a452SArchit Taneja static int 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 19915b4a452SArchit Taneja static const struct of_device_id hdmi_phy_dt_match[] = { 20015b4a452SArchit Taneja { .compatible = "qcom,hdmi-phy-8660", 20115b4a452SArchit Taneja .data = &hdmi_phy_8x60_cfg }, 20215b4a452SArchit Taneja { .compatible = "qcom,hdmi-phy-8960", 20315b4a452SArchit Taneja .data = &hdmi_phy_8960_cfg }, 20415b4a452SArchit Taneja { .compatible = "qcom,hdmi-phy-8974", 20515b4a452SArchit Taneja .data = &hdmi_phy_8x74_cfg }, 20615b4a452SArchit Taneja { .compatible = "qcom,hdmi-phy-8084", 20715b4a452SArchit Taneja .data = &hdmi_phy_8x74_cfg }, 208*e17afdceSArchit Taneja { .compatible = "qcom,hdmi-phy-8996", 209*e17afdceSArchit Taneja .data = &hdmi_phy_8996_cfg }, 21015b4a452SArchit Taneja {} 21115b4a452SArchit Taneja }; 21215b4a452SArchit Taneja 21315b4a452SArchit Taneja static struct platform_driver hdmi_phy_platform_driver = { 21415b4a452SArchit Taneja .probe = hdmi_phy_probe, 21515b4a452SArchit Taneja .remove = hdmi_phy_remove, 21615b4a452SArchit Taneja .driver = { 21715b4a452SArchit Taneja .name = "msm_hdmi_phy", 21815b4a452SArchit Taneja .of_match_table = hdmi_phy_dt_match, 21915b4a452SArchit Taneja }, 22015b4a452SArchit Taneja }; 22115b4a452SArchit Taneja 22215b4a452SArchit Taneja void __init hdmi_phy_driver_register(void) 22315b4a452SArchit Taneja { 22415b4a452SArchit Taneja platform_driver_register(&hdmi_phy_platform_driver); 22515b4a452SArchit Taneja } 22615b4a452SArchit Taneja 22715b4a452SArchit Taneja void __exit hdmi_phy_driver_unregister(void) 22815b4a452SArchit Taneja { 22915b4a452SArchit Taneja platform_driver_unregister(&hdmi_phy_platform_driver); 23015b4a452SArchit Taneja } 231