Linux Audio

Check our new training course

Loading...
v5.14.15
  1// SPDX-License-Identifier: GPL-2.0
  2#include <linux/clk-provider.h>
 
  3#include <linux/regulator/consumer.h>
  4
  5#include "mcde_drm.h"
  6#include "mcde_display_regs.h"
  7
  8/* The MCDE internal clock dividers for FIFO A and B */
  9struct mcde_clk_div {
 10	struct clk_hw hw;
 11	struct mcde *mcde;
 12	u32 cr;
 13	u32 cr_div;
 14};
 15
 16static int mcde_clk_div_enable(struct clk_hw *hw)
 17{
 18	struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
 19	struct mcde *mcde = cdiv->mcde;
 20	u32 val;
 21
 22	spin_lock(&mcde->fifo_crx1_lock);
 23	val = readl(mcde->regs + cdiv->cr);
 24	/*
 25	 * Select the PLL72 (LCD) clock as parent
 26	 * FIXME: implement other parents.
 27	 */
 28	val &= ~MCDE_CRX1_CLKSEL_MASK;
 29	val |= MCDE_CRX1_CLKSEL_CLKPLL72 << MCDE_CRX1_CLKSEL_SHIFT;
 30	/* Internal clock */
 31	val |= MCDE_CRA1_CLKTYPE_TVXCLKSEL1;
 32
 33	/* Clear then set the divider */
 34	val &= ~(MCDE_CRX1_BCD | MCDE_CRX1_PCD_MASK);
 35	val |= cdiv->cr_div;
 36
 37	writel(val, mcde->regs + cdiv->cr);
 38	spin_unlock(&mcde->fifo_crx1_lock);
 39
 40	return 0;
 41}
 42
 43static int mcde_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
 44				   unsigned long *prate, bool set_parent)
 45{
 46	int best_div = 1, div;
 47	struct clk_hw *parent = clk_hw_get_parent(hw);
 48	unsigned long best_prate = 0;
 49	unsigned long best_diff = ~0ul;
 50	int max_div = (1 << MCDE_CRX1_PCD_BITS) - 1;
 51
 52	for (div = 1; div < max_div; div++) {
 53		unsigned long this_prate, div_rate, diff;
 54
 55		if (set_parent)
 56			this_prate = clk_hw_round_rate(parent, rate * div);
 57		else
 58			this_prate = *prate;
 59		div_rate = DIV_ROUND_UP_ULL(this_prate, div);
 60		diff = abs(rate - div_rate);
 61
 62		if (diff < best_diff) {
 63			best_div = div;
 64			best_diff = diff;
 65			best_prate = this_prate;
 66		}
 67	}
 68
 69	*prate = best_prate;
 70	return best_div;
 71}
 72
 73static long mcde_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
 74				     unsigned long *prate)
 75{
 76	int div = mcde_clk_div_choose_div(hw, rate, prate, true);
 77
 78	return DIV_ROUND_UP_ULL(*prate, div);
 79}
 80
 81static unsigned long mcde_clk_div_recalc_rate(struct clk_hw *hw,
 82					       unsigned long prate)
 83{
 84	struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
 85	struct mcde *mcde = cdiv->mcde;
 86	u32 cr;
 87	int div;
 88
 89	/*
 90	 * If the MCDE is not powered we can't access registers.
 91	 * It will come up with 0 in the divider register bits, which
 92	 * means "divide by 2".
 93	 */
 94	if (!regulator_is_enabled(mcde->epod))
 95		return DIV_ROUND_UP_ULL(prate, 2);
 96
 97	cr = readl(mcde->regs + cdiv->cr);
 98	if (cr & MCDE_CRX1_BCD)
 99		return prate;
100
101	/* 0 in the PCD means "divide by 2", 1 means "divide by 3" etc */
102	div = cr & MCDE_CRX1_PCD_MASK;
103	div += 2;
104
105	return DIV_ROUND_UP_ULL(prate, div);
106}
107
108static int mcde_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
109				  unsigned long prate)
110{
111	struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
112	int div = mcde_clk_div_choose_div(hw, rate, &prate, false);
113	u32 cr = 0;
114
115	/*
116	 * We cache the CR bits to set the divide in the state so that
117	 * we can call this before we can even write to the hardware.
118	 */
119	if (div == 1) {
120		/* Bypass clock divider */
121		cr |= MCDE_CRX1_BCD;
122	} else {
123		div -= 2;
124		cr |= div & MCDE_CRX1_PCD_MASK;
125	}
126	cdiv->cr_div = cr;
127
128	return 0;
129}
130
131static const struct clk_ops mcde_clk_div_ops = {
132	.enable = mcde_clk_div_enable,
133	.recalc_rate = mcde_clk_div_recalc_rate,
134	.round_rate = mcde_clk_div_round_rate,
135	.set_rate = mcde_clk_div_set_rate,
136};
137
138int mcde_init_clock_divider(struct mcde *mcde)
139{
140	struct device *dev = mcde->dev;
141	struct mcde_clk_div *fifoa;
142	struct mcde_clk_div *fifob;
143	const char *parent_name;
144	struct clk_init_data fifoa_init = {
145		.name = "fifoa",
146		.ops = &mcde_clk_div_ops,
147		.parent_names = &parent_name,
148		.num_parents = 1,
149		.flags = CLK_SET_RATE_PARENT,
150	};
151	struct clk_init_data fifob_init = {
152		.name = "fifob",
153		.ops = &mcde_clk_div_ops,
154		.parent_names = &parent_name,
155		.num_parents = 1,
156		.flags = CLK_SET_RATE_PARENT,
157	};
158	int ret;
159
160	spin_lock_init(&mcde->fifo_crx1_lock);
161	parent_name = __clk_get_name(mcde->lcd_clk);
162
163	/* Allocate 2 clocks */
164	fifoa = devm_kzalloc(dev, sizeof(*fifoa), GFP_KERNEL);
165	if (!fifoa)
166		return -ENOMEM;
167	fifob = devm_kzalloc(dev, sizeof(*fifob), GFP_KERNEL);
168	if (!fifob)
169		return -ENOMEM;
170
171	fifoa->mcde = mcde;
172	fifoa->cr = MCDE_CRA1;
173	fifoa->hw.init = &fifoa_init;
174	ret = devm_clk_hw_register(dev, &fifoa->hw);
175	if (ret) {
176		dev_err(dev, "error registering FIFO A clock divider\n");
177		return ret;
178	}
179	mcde->fifoa_clk = fifoa->hw.clk;
180
181	fifob->mcde = mcde;
182	fifob->cr = MCDE_CRB1;
183	fifob->hw.init = &fifob_init;
184	ret = devm_clk_hw_register(dev, &fifob->hw);
185	if (ret) {
186		dev_err(dev, "error registering FIFO B clock divider\n");
187		return ret;
188	}
189	mcde->fifob_clk = fifob->hw.clk;
190
191	return 0;
192}
v6.8
  1// SPDX-License-Identifier: GPL-2.0
  2#include <linux/clk-provider.h>
  3#include <linux/io.h>
  4#include <linux/regulator/consumer.h>
  5
  6#include "mcde_drm.h"
  7#include "mcde_display_regs.h"
  8
  9/* The MCDE internal clock dividers for FIFO A and B */
 10struct mcde_clk_div {
 11	struct clk_hw hw;
 12	struct mcde *mcde;
 13	u32 cr;
 14	u32 cr_div;
 15};
 16
 17static int mcde_clk_div_enable(struct clk_hw *hw)
 18{
 19	struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
 20	struct mcde *mcde = cdiv->mcde;
 21	u32 val;
 22
 23	spin_lock(&mcde->fifo_crx1_lock);
 24	val = readl(mcde->regs + cdiv->cr);
 25	/*
 26	 * Select the PLL72 (LCD) clock as parent
 27	 * FIXME: implement other parents.
 28	 */
 29	val &= ~MCDE_CRX1_CLKSEL_MASK;
 30	val |= MCDE_CRX1_CLKSEL_CLKPLL72 << MCDE_CRX1_CLKSEL_SHIFT;
 31	/* Internal clock */
 32	val |= MCDE_CRA1_CLKTYPE_TVXCLKSEL1;
 33
 34	/* Clear then set the divider */
 35	val &= ~(MCDE_CRX1_BCD | MCDE_CRX1_PCD_MASK);
 36	val |= cdiv->cr_div;
 37
 38	writel(val, mcde->regs + cdiv->cr);
 39	spin_unlock(&mcde->fifo_crx1_lock);
 40
 41	return 0;
 42}
 43
 44static int mcde_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
 45				   unsigned long *prate, bool set_parent)
 46{
 47	int best_div = 1, div;
 48	struct clk_hw *parent = clk_hw_get_parent(hw);
 49	unsigned long best_prate = 0;
 50	unsigned long best_diff = ~0ul;
 51	int max_div = (1 << MCDE_CRX1_PCD_BITS) - 1;
 52
 53	for (div = 1; div < max_div; div++) {
 54		unsigned long this_prate, div_rate, diff;
 55
 56		if (set_parent)
 57			this_prate = clk_hw_round_rate(parent, rate * div);
 58		else
 59			this_prate = *prate;
 60		div_rate = DIV_ROUND_UP_ULL(this_prate, div);
 61		diff = abs(rate - div_rate);
 62
 63		if (diff < best_diff) {
 64			best_div = div;
 65			best_diff = diff;
 66			best_prate = this_prate;
 67		}
 68	}
 69
 70	*prate = best_prate;
 71	return best_div;
 72}
 73
 74static long mcde_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
 75				     unsigned long *prate)
 76{
 77	int div = mcde_clk_div_choose_div(hw, rate, prate, true);
 78
 79	return DIV_ROUND_UP_ULL(*prate, div);
 80}
 81
 82static unsigned long mcde_clk_div_recalc_rate(struct clk_hw *hw,
 83					       unsigned long prate)
 84{
 85	struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
 86	struct mcde *mcde = cdiv->mcde;
 87	u32 cr;
 88	int div;
 89
 90	/*
 91	 * If the MCDE is not powered we can't access registers.
 92	 * It will come up with 0 in the divider register bits, which
 93	 * means "divide by 2".
 94	 */
 95	if (!regulator_is_enabled(mcde->epod))
 96		return DIV_ROUND_UP_ULL(prate, 2);
 97
 98	cr = readl(mcde->regs + cdiv->cr);
 99	if (cr & MCDE_CRX1_BCD)
100		return prate;
101
102	/* 0 in the PCD means "divide by 2", 1 means "divide by 3" etc */
103	div = cr & MCDE_CRX1_PCD_MASK;
104	div += 2;
105
106	return DIV_ROUND_UP_ULL(prate, div);
107}
108
109static int mcde_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
110				  unsigned long prate)
111{
112	struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
113	int div = mcde_clk_div_choose_div(hw, rate, &prate, false);
114	u32 cr = 0;
115
116	/*
117	 * We cache the CR bits to set the divide in the state so that
118	 * we can call this before we can even write to the hardware.
119	 */
120	if (div == 1) {
121		/* Bypass clock divider */
122		cr |= MCDE_CRX1_BCD;
123	} else {
124		div -= 2;
125		cr |= div & MCDE_CRX1_PCD_MASK;
126	}
127	cdiv->cr_div = cr;
128
129	return 0;
130}
131
132static const struct clk_ops mcde_clk_div_ops = {
133	.enable = mcde_clk_div_enable,
134	.recalc_rate = mcde_clk_div_recalc_rate,
135	.round_rate = mcde_clk_div_round_rate,
136	.set_rate = mcde_clk_div_set_rate,
137};
138
139int mcde_init_clock_divider(struct mcde *mcde)
140{
141	struct device *dev = mcde->dev;
142	struct mcde_clk_div *fifoa;
143	struct mcde_clk_div *fifob;
144	const char *parent_name;
145	struct clk_init_data fifoa_init = {
146		.name = "fifoa",
147		.ops = &mcde_clk_div_ops,
148		.parent_names = &parent_name,
149		.num_parents = 1,
150		.flags = CLK_SET_RATE_PARENT,
151	};
152	struct clk_init_data fifob_init = {
153		.name = "fifob",
154		.ops = &mcde_clk_div_ops,
155		.parent_names = &parent_name,
156		.num_parents = 1,
157		.flags = CLK_SET_RATE_PARENT,
158	};
159	int ret;
160
161	spin_lock_init(&mcde->fifo_crx1_lock);
162	parent_name = __clk_get_name(mcde->lcd_clk);
163
164	/* Allocate 2 clocks */
165	fifoa = devm_kzalloc(dev, sizeof(*fifoa), GFP_KERNEL);
166	if (!fifoa)
167		return -ENOMEM;
168	fifob = devm_kzalloc(dev, sizeof(*fifob), GFP_KERNEL);
169	if (!fifob)
170		return -ENOMEM;
171
172	fifoa->mcde = mcde;
173	fifoa->cr = MCDE_CRA1;
174	fifoa->hw.init = &fifoa_init;
175	ret = devm_clk_hw_register(dev, &fifoa->hw);
176	if (ret) {
177		dev_err(dev, "error registering FIFO A clock divider\n");
178		return ret;
179	}
180	mcde->fifoa_clk = fifoa->hw.clk;
181
182	fifob->mcde = mcde;
183	fifob->cr = MCDE_CRB1;
184	fifob->hw.init = &fifob_init;
185	ret = devm_clk_hw_register(dev, &fifob->hw);
186	if (ret) {
187		dev_err(dev, "error registering FIFO B clock divider\n");
188		return ret;
189	}
190	mcde->fifob_clk = fifob->hw.clk;
191
192	return 0;
193}