File Index
Files and directories
Sign in
FILE

mdp4_lvds_pll.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/msm/disp/mdp4/mdp4_lvds_pll.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2014 Red Hat
  4 * Author: Rob Clark <robdclark@gmail.com>
  5 */
  6
  7#include <linux/clk.h>
  8#include <linux/clk-provider.h>
  9
 10#include "mdp4_kms.h"
 11
 12struct mdp4_lvds_pll {
 13	struct clk_hw pll_hw;
 14	struct drm_device *dev;
 15	unsigned long pixclk;
 16};
 17#define to_mdp4_lvds_pll(x) container_of(x, struct mdp4_lvds_pll, pll_hw)
 18
 19static struct mdp4_kms *get_kms(struct mdp4_lvds_pll *lvds_pll)
 20{
 21	struct msm_drm_private *priv = lvds_pll->dev->dev_private;
 22	return to_mdp4_kms(to_mdp_kms(priv->kms));
 23}
 24
 25struct pll_rate {
 26	unsigned long rate;
 27	struct {
 28		uint32_t val;
 29		uint32_t reg;
 30	} conf[32];
 31};
 32
 33/* NOTE: keep sorted highest freq to lowest: */
 34static const struct pll_rate freqtbl[] = {
 35	{ 72000000, {
 36		{ 0x8f, REG_MDP4_LVDS_PHY_PLL_CTRL_1 },
 37		{ 0x30, REG_MDP4_LVDS_PHY_PLL_CTRL_2 },
 38		{ 0xc6, REG_MDP4_LVDS_PHY_PLL_CTRL_3 },
 39		{ 0x10, REG_MDP4_LVDS_PHY_PLL_CTRL_5 },
 40		{ 0x07, REG_MDP4_LVDS_PHY_PLL_CTRL_6 },
 41		{ 0x62, REG_MDP4_LVDS_PHY_PLL_CTRL_7 },
 42		{ 0x41, REG_MDP4_LVDS_PHY_PLL_CTRL_8 },
 43		{ 0x0d, REG_MDP4_LVDS_PHY_PLL_CTRL_9 },
 44		{ 0, 0 } }
 45	},
 46};
 47
 48static const struct pll_rate *find_rate(unsigned long rate)
 49{
 50	int i;
 51	for (i = 1; i < ARRAY_SIZE(freqtbl); i++)
 52		if (rate > freqtbl[i].rate)
 53			return &freqtbl[i-1];
 54	return &freqtbl[i-1];
 55}
 56
 57static int mdp4_lvds_pll_enable(struct clk_hw *hw)
 58{
 59	struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw);
 60	struct mdp4_kms *mdp4_kms = get_kms(lvds_pll);
 61	const struct pll_rate *pll_rate = find_rate(lvds_pll->pixclk);
 62	int i;
 63
 64	DBG("pixclk=%lu (%lu)", lvds_pll->pixclk, pll_rate->rate);
 65
 66	if (WARN_ON(!pll_rate))
 67		return -EINVAL;
 68
 69	mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_PHY_RESET, 0x33);
 70
 71	for (i = 0; pll_rate->conf[i].reg; i++)
 72		mdp4_write(mdp4_kms, pll_rate->conf[i].reg, pll_rate->conf[i].val);
 73
 74	mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_PLL_CTRL_0, 0x01);
 75
 76	/* Wait until LVDS PLL is locked and ready */
 77	while (!mdp4_read(mdp4_kms, REG_MDP4_LVDS_PHY_PLL_LOCKED))
 78		cpu_relax();
 79
 80	return 0;
 81}
 82
 83static void mdp4_lvds_pll_disable(struct clk_hw *hw)
 84{
 85	struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw);
 86	struct mdp4_kms *mdp4_kms = get_kms(lvds_pll);
 87
 88	DBG("");
 89
 90	mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, 0x0);
 91	mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_PLL_CTRL_0, 0x0);
 92}
 93
 94static unsigned long mdp4_lvds_pll_recalc_rate(struct clk_hw *hw,
 95				unsigned long parent_rate)
 96{
 97	struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw);
 98	return lvds_pll->pixclk;
 99}
100
101static int mdp4_lvds_pll_determine_rate(struct clk_hw *hw,
102					struct clk_rate_request *req)
103{
104	const struct pll_rate *pll_rate = find_rate(req->rate);
105
106	req->rate = pll_rate->rate;
107
108	return 0;
109}
110
111static int mdp4_lvds_pll_set_rate(struct clk_hw *hw, unsigned long rate,
112		unsigned long parent_rate)
113{
114	struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw);
115	lvds_pll->pixclk = rate;
116	return 0;
117}
118
119
120static const struct clk_ops mdp4_lvds_pll_ops = {
121	.enable = mdp4_lvds_pll_enable,
122	.disable = mdp4_lvds_pll_disable,
123	.recalc_rate = mdp4_lvds_pll_recalc_rate,
124	.determine_rate = mdp4_lvds_pll_determine_rate,
125	.set_rate = mdp4_lvds_pll_set_rate,
126};
127
128static const struct clk_parent_data mdp4_lvds_pll_parents[] = {
129	{ .fw_name = "pxo", .name = "pxo", },
130};
131
132static struct clk_init_data pll_init = {
133	.name = "mdp4_lvds_pll",
134	.ops = &mdp4_lvds_pll_ops,
135	.parent_data = mdp4_lvds_pll_parents,
136	.num_parents = ARRAY_SIZE(mdp4_lvds_pll_parents),
137};
138
139static struct clk_hw *mdp4_lvds_pll_init(struct drm_device *dev)
140{
141	struct mdp4_lvds_pll *lvds_pll;
142	int ret;
143
144	lvds_pll = devm_kzalloc(dev->dev, sizeof(*lvds_pll), GFP_KERNEL);
145	if (!lvds_pll)
146		return ERR_PTR(-ENOMEM);
147
148	lvds_pll->dev = dev;
149
150	lvds_pll->pll_hw.init = &pll_init;
151	ret = devm_clk_hw_register(dev->dev, &lvds_pll->pll_hw);
152	if (ret)
153		return ERR_PTR(ret);
154
155	ret = devm_of_clk_add_hw_provider(dev->dev, of_clk_hw_simple_get, &lvds_pll->pll_hw);
156	if (ret)
157		return ERR_PTR(ret);
158
159	return &lvds_pll->pll_hw;
160}
161
162struct clk *mdp4_get_lcdc_clock(struct drm_device *dev)
163{
164	struct clk_hw *hw;
165	struct clk *clk;
166
167
168	/* TODO: do we need different pll in other cases? */
169	hw = mdp4_lvds_pll_init(dev);
170	if (IS_ERR(hw)) {
171		DRM_DEV_ERROR(dev->dev, "failed to register LVDS PLL\n");
172		return ERR_CAST(hw);
173	}
174
175	clk = devm_clk_get(dev->dev, "lcdc_clk");
176	if (clk == ERR_PTR(-ENOENT)) {
177		drm_warn(dev, "can't get LCDC clock, using PLL directly\n");
178
179		return devm_clk_hw_get_clk(dev->dev, hw, "lcdc_clk");
180	}
181
182	return clk;
183}
~/ Read-only file preview.