| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * HDMI PLL | |
| * | |
| * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ | |
| */ | |
| #define DSS_SUBSYS_NAME "HDMIPLL" | |
| #include <linux/kernel.h> | |
| #include <linux/module.h> | |
| #include <linux/err.h> | |
| #include <linux/io.h> | |
| #include <linux/platform_device.h> | |
| #include <linux/clk.h> | |
| #include <linux/seq_file.h> | |
| #include <linux/pm_runtime.h> | |
| #include "omapdss.h" | |
| #include "dss.h" | |
| #include "hdmi.h" | |
| void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s) | |
| { | |
| #define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\ | |
| hdmi_read_reg(pll->base, r)) | |
| DUMPPLL(PLLCTRL_PLL_CONTROL); | |
| DUMPPLL(PLLCTRL_PLL_STATUS); | |
| DUMPPLL(PLLCTRL_PLL_GO); | |
| DUMPPLL(PLLCTRL_CFG1); | |
| DUMPPLL(PLLCTRL_CFG2); | |
| DUMPPLL(PLLCTRL_CFG3); | |
| DUMPPLL(PLLCTRL_SSC_CFG1); | |
| DUMPPLL(PLLCTRL_SSC_CFG2); | |
| DUMPPLL(PLLCTRL_CFG4); | |
| } | |
| static int hdmi_pll_enable(struct dss_pll *dsspll) | |
| { | |
| struct hdmi_pll_data *pll = container_of(dsspll, struct hdmi_pll_data, pll); | |
| struct hdmi_wp_data *wp = pll->wp; | |
| int r; | |
| r = pm_runtime_get_sync(&pll->pdev->dev); | |
| WARN_ON(r < 0); | |
| dss_ctrl_pll_enable(dsspll, true); | |
| r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); | |
| if (r) | |
| return r; | |
| return 0; | |
| } | |
| static void hdmi_pll_disable(struct dss_pll *dsspll) | |
| { | |
| struct hdmi_pll_data *pll = container_of(dsspll, struct hdmi_pll_data, pll); | |
| struct hdmi_wp_data *wp = pll->wp; | |
| int r; | |
| hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); | |
| dss_ctrl_pll_enable(dsspll, false); | |
| r = pm_runtime_put_sync(&pll->pdev->dev); | |
| WARN_ON(r < 0 && r != -ENOSYS); | |
| } | |
| static const struct dss_pll_ops hdmi_pll_ops = { | |
| .enable = hdmi_pll_enable, | |
| .disable = hdmi_pll_disable, | |
| .set_config = dss_pll_write_config_type_b, | |
| }; | |
| static const struct dss_pll_hw dss_omap4_hdmi_pll_hw = { | |
| .type = DSS_PLL_TYPE_B, | |
| .n_max = 255, | |
| .m_min = 20, | |
| .m_max = 4095, | |
| .mX_max = 127, | |
| .fint_min = 500000, | |
| .fint_max = 2500000, | |
| .clkdco_min = 500000000, | |
| .clkdco_low = 1000000000, | |
| .clkdco_max = 2000000000, | |
| .n_msb = 8, | |
| .n_lsb = 1, | |
| .m_msb = 20, | |
| .m_lsb = 9, | |
| .mX_msb[0] = 24, | |
| .mX_lsb[0] = 18, | |
| .has_selfreqdco = true, | |
| }; | |
| static const struct dss_pll_hw dss_omap5_hdmi_pll_hw = { | |
| .type = DSS_PLL_TYPE_B, | |
| .n_max = 255, | |
| .m_min = 20, | |
| .m_max = 2045, | |
| .mX_max = 127, | |
| .fint_min = 620000, | |
| .fint_max = 2500000, | |
| .clkdco_min = 750000000, | |
| .clkdco_low = 1500000000, | |
| .clkdco_max = 2500000000UL, | |
| .n_msb = 8, | |
| .n_lsb = 1, | |
| .m_msb = 20, | |
| .m_lsb = 9, | |
| .mX_msb[0] = 24, | |
| .mX_lsb[0] = 18, | |
| .has_selfreqdco = true, | |
| .has_refsel = true, | |
| }; | |
| static int hdmi_init_pll_data(struct dss_device *dss, | |
| struct platform_device *pdev, | |
| struct hdmi_pll_data *hpll) | |
| { | |
| struct dss_pll *pll = &hpll->pll; | |
| struct clk *clk; | |
| int r; | |
| clk = devm_clk_get(&pdev->dev, "sys_clk"); | |
| if (IS_ERR(clk)) { | |
| DSSERR("can't get sys_clk\n"); | |
| return PTR_ERR(clk); | |
| } | |
| pll->name = "hdmi"; | |
| pll->id = DSS_PLL_HDMI; | |
| pll->base = hpll->base; | |
| pll->clkin = clk; | |
| if (hpll->wp->version == 4) | |
| pll->hw = &dss_omap4_hdmi_pll_hw; | |
| else | |
| pll->hw = &dss_omap5_hdmi_pll_hw; | |
| pll->ops = &hdmi_pll_ops; | |
| r = dss_pll_register(dss, pll); | |
| if (r) | |
| return r; | |
| return 0; | |
| } | |
| int hdmi_pll_init(struct dss_device *dss, struct platform_device *pdev, | |
| struct hdmi_pll_data *pll, struct hdmi_wp_data *wp) | |
| { | |
| int r; | |
| struct resource *res; | |
| pll->pdev = pdev; | |
| pll->wp = wp; | |
| res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll"); | |
| pll->base = devm_ioremap_resource(&pdev->dev, res); | |
| if (IS_ERR(pll->base)) | |
| return PTR_ERR(pll->base); | |
| r = hdmi_init_pll_data(dss, pdev, pll); | |
| if (r) { | |
| DSSERR("failed to init HDMI PLL\n"); | |
| return r; | |
| } | |
| return 0; | |
| } | |
| void hdmi_pll_uninit(struct hdmi_pll_data *hpll) | |
| { | |
| struct dss_pll *pll = &hpll->pll; | |
| dss_pll_unregister(pll); | |
| } |