18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci* Copyright (C) 2014 Texas Instruments Ltd
48c2ecf20Sopenharmony_ci*/
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/clk.h>
78c2ecf20Sopenharmony_ci#include <linux/delay.h>
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/io.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/sched.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <video/omapfb_dss.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "dss.h"
178c2ecf20Sopenharmony_ci#include "dss_features.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistruct dss_video_pll {
208c2ecf20Sopenharmony_ci	struct dss_pll pll;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	struct device *dev;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	void __iomem *clkctrl_base;
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define REG_MOD(reg, val, start, end) \
288c2ecf20Sopenharmony_ci	writel_relaxed(FLD_MOD(readl_relaxed(reg), val, start, end), reg)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void dss_dpll_enable_scp_clk(struct dss_video_pll *vpll)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	REG_MOD(vpll->clkctrl_base, 1, 14, 14); /* CIO_CLK_ICG */
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic void dss_dpll_disable_scp_clk(struct dss_video_pll *vpll)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	REG_MOD(vpll->clkctrl_base, 0, 14, 14); /* CIO_CLK_ICG */
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic void dss_dpll_power_enable(struct dss_video_pll *vpll)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	REG_MOD(vpll->clkctrl_base, 2, 31, 30); /* PLL_POWER_ON_ALL */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	/*
458c2ecf20Sopenharmony_ci	 * DRA7x PLL CTRL's PLL_PWR_STATUS seems to always return 0,
468c2ecf20Sopenharmony_ci	 * so we have to use fixed delay here.
478c2ecf20Sopenharmony_ci	 */
488c2ecf20Sopenharmony_ci	msleep(1);
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void dss_dpll_power_disable(struct dss_video_pll *vpll)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	REG_MOD(vpll->clkctrl_base, 0, 31, 30);	/* PLL_POWER_OFF */
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int dss_video_pll_enable(struct dss_pll *pll)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	struct dss_video_pll *vpll = container_of(pll, struct dss_video_pll, pll);
598c2ecf20Sopenharmony_ci	int r;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	r = dss_runtime_get();
628c2ecf20Sopenharmony_ci	if (r)
638c2ecf20Sopenharmony_ci		return r;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	dss_ctrl_pll_enable(pll->id, true);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	dss_dpll_enable_scp_clk(vpll);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	r = dss_pll_wait_reset_done(pll);
708c2ecf20Sopenharmony_ci	if (r)
718c2ecf20Sopenharmony_ci		goto err_reset;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	dss_dpll_power_enable(vpll);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	return 0;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cierr_reset:
788c2ecf20Sopenharmony_ci	dss_dpll_disable_scp_clk(vpll);
798c2ecf20Sopenharmony_ci	dss_ctrl_pll_enable(pll->id, false);
808c2ecf20Sopenharmony_ci	dss_runtime_put();
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return r;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic void dss_video_pll_disable(struct dss_pll *pll)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct dss_video_pll *vpll = container_of(pll, struct dss_video_pll, pll);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	dss_dpll_power_disable(vpll);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	dss_dpll_disable_scp_clk(vpll);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	dss_ctrl_pll_enable(pll->id, false);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	dss_runtime_put();
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic const struct dss_pll_ops dss_pll_ops = {
998c2ecf20Sopenharmony_ci	.enable = dss_video_pll_enable,
1008c2ecf20Sopenharmony_ci	.disable = dss_video_pll_disable,
1018c2ecf20Sopenharmony_ci	.set_config = dss_pll_write_config_type_a,
1028c2ecf20Sopenharmony_ci};
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic const struct dss_pll_hw dss_dra7_video_pll_hw = {
1058c2ecf20Sopenharmony_ci	.n_max = (1 << 8) - 1,
1068c2ecf20Sopenharmony_ci	.m_max = (1 << 12) - 1,
1078c2ecf20Sopenharmony_ci	.mX_max = (1 << 5) - 1,
1088c2ecf20Sopenharmony_ci	.fint_min = 500000,
1098c2ecf20Sopenharmony_ci	.fint_max = 2500000,
1108c2ecf20Sopenharmony_ci	.clkdco_max = 1800000000,
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	.n_msb = 8,
1138c2ecf20Sopenharmony_ci	.n_lsb = 1,
1148c2ecf20Sopenharmony_ci	.m_msb = 20,
1158c2ecf20Sopenharmony_ci	.m_lsb = 9,
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	.mX_msb[0] = 25,
1188c2ecf20Sopenharmony_ci	.mX_lsb[0] = 21,
1198c2ecf20Sopenharmony_ci	.mX_msb[1] = 30,
1208c2ecf20Sopenharmony_ci	.mX_lsb[1] = 26,
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	.has_refsel = true,
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistruct dss_pll *dss_video_pll_init(struct platform_device *pdev, int id,
1268c2ecf20Sopenharmony_ci	struct regulator *regulator)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	const char * const reg_name[] = { "pll1", "pll2" };
1298c2ecf20Sopenharmony_ci	const char * const clkctrl_name[] = { "pll1_clkctrl", "pll2_clkctrl" };
1308c2ecf20Sopenharmony_ci	const char * const clkin_name[] = { "video1_clk", "video2_clk" };
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	struct resource *res;
1338c2ecf20Sopenharmony_ci	struct dss_video_pll *vpll;
1348c2ecf20Sopenharmony_ci	void __iomem *pll_base, *clkctrl_base;
1358c2ecf20Sopenharmony_ci	struct clk *clk;
1368c2ecf20Sopenharmony_ci	struct dss_pll *pll;
1378c2ecf20Sopenharmony_ci	int r;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* PLL CONTROL */
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, reg_name[id]);
1428c2ecf20Sopenharmony_ci	if (!res) {
1438c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
1448c2ecf20Sopenharmony_ci			"missing platform resource data for pll%d\n", id);
1458c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	pll_base = devm_ioremap_resource(&pdev->dev, res);
1498c2ecf20Sopenharmony_ci	if (IS_ERR(pll_base)) {
1508c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to ioremap pll%d reg_name\n", id);
1518c2ecf20Sopenharmony_ci		return ERR_CAST(pll_base);
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/* CLOCK CONTROL */
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
1578c2ecf20Sopenharmony_ci		clkctrl_name[id]);
1588c2ecf20Sopenharmony_ci	if (!res) {
1598c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
1608c2ecf20Sopenharmony_ci			"missing platform resource data for pll%d\n", id);
1618c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	clkctrl_base = devm_ioremap_resource(&pdev->dev, res);
1658c2ecf20Sopenharmony_ci	if (IS_ERR(clkctrl_base)) {
1668c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to ioremap pll%d clkctrl\n", id);
1678c2ecf20Sopenharmony_ci		return ERR_CAST(clkctrl_base);
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	/* CLKIN */
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	clk = devm_clk_get(&pdev->dev, clkin_name[id]);
1738c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
1748c2ecf20Sopenharmony_ci		DSSERR("can't get video pll clkin\n");
1758c2ecf20Sopenharmony_ci		return ERR_CAST(clk);
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	vpll = devm_kzalloc(&pdev->dev, sizeof(*vpll), GFP_KERNEL);
1798c2ecf20Sopenharmony_ci	if (!vpll)
1808c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	vpll->dev = &pdev->dev;
1838c2ecf20Sopenharmony_ci	vpll->clkctrl_base = clkctrl_base;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	pll = &vpll->pll;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	pll->name = id == 0 ? "video0" : "video1";
1888c2ecf20Sopenharmony_ci	pll->id = id == 0 ? DSS_PLL_VIDEO1 : DSS_PLL_VIDEO2;
1898c2ecf20Sopenharmony_ci	pll->clkin = clk;
1908c2ecf20Sopenharmony_ci	pll->regulator = regulator;
1918c2ecf20Sopenharmony_ci	pll->base = pll_base;
1928c2ecf20Sopenharmony_ci	pll->hw = &dss_dra7_video_pll_hw;
1938c2ecf20Sopenharmony_ci	pll->ops = &dss_pll_ops;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	r = dss_pll_register(pll);
1968c2ecf20Sopenharmony_ci	if (r)
1978c2ecf20Sopenharmony_ci		return ERR_PTR(r);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	return pll;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_civoid dss_video_pll_uninit(struct dss_pll *pll)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	dss_pll_unregister(pll);
2058c2ecf20Sopenharmony_ci}
206