brintos

brintos / linux-shallow public Read only

0
0
Text · 4.6 KiB · 3056ac5 Raw
194 lines · c
1// SPDX-License-Identifier: GPL-2.02#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 parent27	 * 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		else59			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, which93	 * 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 that118	 * 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}194