Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2a46db4a authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Mark Brown
Browse files

ASoC: rsnd: add AUDIO_CLKOUT support



Renesas sound has AUDIO_CLKOUT (in Gen1/Gen2) AUDIO_CLKOUT1/2/3 (in Gen3)
This patch support these patches as clock provider.

Signed-off-by: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 248e88c2
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -34,6 +34,9 @@ Required properties:
				  see below for detail.
				  see below for detail.
- #sound-dai-cells		: it must be 0 if your system is using single DAI
- #sound-dai-cells		: it must be 0 if your system is using single DAI
				  it must be 1 if your system is using multi  DAI
				  it must be 1 if your system is using multi  DAI
- #clock-cells			: it must be 0 if your system has audio_clkout
				  it must be 1 if your system has audio_clkout0/1/2/3
- clock-frequency		: for all audio_clkout0/1/2/3


SSI subnode properties:
SSI subnode properties:
- interrupts			: Should contain SSI interrupt for PIO transfer
- interrupts			: Should contain SSI interrupt for PIO transfer
+94 −4
Original line number Original line Diff line number Diff line
@@ -7,6 +7,7 @@
 * License.  See the file "COPYING" in the main directory of this archive
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 * for more details.
 */
 */
#include <linux/clk-provider.h>
#include "rsnd.h"
#include "rsnd.h"


#define CLKA	0
#define CLKA	0
@@ -15,6 +16,12 @@
#define CLKI	3
#define CLKI	3
#define CLKMAX	4
#define CLKMAX	4


#define CLKOUT	0
#define CLKOUT1	1
#define CLKOUT2	2
#define CLKOUT3	3
#define CLKOUTMAX 4

#define BRRx_MASK(x) (0x3FF & x)
#define BRRx_MASK(x) (0x3FF & x)


static struct rsnd_mod_ops adg_ops = {
static struct rsnd_mod_ops adg_ops = {
@@ -23,6 +30,8 @@ static struct rsnd_mod_ops adg_ops = {


struct rsnd_adg {
struct rsnd_adg {
	struct clk *clk[CLKMAX];
	struct clk *clk[CLKMAX];
	struct clk *clkout[CLKOUTMAX];
	struct clk_onecell_data onecell;
	struct rsnd_mod mod;
	struct rsnd_mod mod;


	int rbga_rate_for_441khz; /* RBGA */
	int rbga_rate_for_441khz; /* RBGA */
@@ -34,6 +43,11 @@ struct rsnd_adg {
	     (i < CLKMAX) &&			\
	     (i < CLKMAX) &&			\
	     ((pos) = adg->clk[i]);		\
	     ((pos) = adg->clk[i]);		\
	     i++)
	     i++)
#define for_each_rsnd_clkout(pos, adg, i)	\
	for (i = 0;				\
	     (i < CLKOUTMAX) &&			\
	     ((pos) = adg->clkout[i]);	\
	     i++)
#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)


static u32 rsnd_adg_calculate_rbgx(unsigned long div)
static u32 rsnd_adg_calculate_rbgx(unsigned long div)
@@ -416,14 +430,25 @@ static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
		dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
		dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
}
}


static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
				struct rsnd_adg *adg)
{
{
	struct clk *clk;
	struct clk *clk;
	struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
	struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
	struct device *dev = rsnd_priv_to_dev(priv);
	struct device *dev = rsnd_priv_to_dev(priv);
	unsigned long rate, div;
	struct device_node *np = dev->of_node;
	u32 ckr, rbgx, rbga, rbgb;
	u32 ckr, rbgx, rbga, rbgb;
	u32 rate, req_rate, div;
	uint32_t count = 0;
	unsigned long req_48kHz_rate, req_441kHz_rate;
	int i;
	int i;
	const char *parent_clk_name = NULL;
	static const char * const clkout_name[] = {
		[CLKOUT]  = "audio_clkout",
		[CLKOUT1] = "audio_clkout1",
		[CLKOUT2] = "audio_clkout2",
		[CLKOUT3] = "audio_clkout3",
	};
	int brg_table[] = {
	int brg_table[] = {
		[CLKA] = 0x0,
		[CLKA] = 0x0,
		[CLKB] = 0x1,
		[CLKB] = 0x1,
@@ -431,6 +456,20 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
		[CLKI] = 0x2,
		[CLKI] = 0x2,
	};
	};


	of_property_read_u32(np, "#clock-cells", &count);

	/*
	 * ADG supports BRRA/BRRB output only
	 * this means all clkout0/1/2/3 will be same rate
	 */
	of_property_read_u32(np, "clock-frequency", &req_rate);
	req_48kHz_rate = 0;
	req_441kHz_rate = 0;
	if (0 == (req_rate % 44100))
		req_441kHz_rate = req_rate;
	if (0 == (req_rate % 48000))
		req_48kHz_rate = req_rate;

	/*
	/*
	 * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
	 * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
	 * have 44.1kHz or 48kHz base clocks for now.
	 * have 44.1kHz or 48kHz base clocks for now.
@@ -454,22 +493,72 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
		/* RBGA */
		/* RBGA */
		if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) {
		if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) {
			div = 6;
			div = 6;
			if (req_441kHz_rate)
				div = rate / req_441kHz_rate;
			rbgx = rsnd_adg_calculate_rbgx(div);
			rbgx = rsnd_adg_calculate_rbgx(div);
			if (BRRx_MASK(rbgx) == rbgx) {
			if (BRRx_MASK(rbgx) == rbgx) {
				rbga = rbgx;
				rbga = rbgx;
				adg->rbga_rate_for_441khz = rate / div;
				adg->rbga_rate_for_441khz = rate / div;
				ckr |= brg_table[i] << 20;
				ckr |= brg_table[i] << 20;
				if (req_441kHz_rate)
					parent_clk_name = __clk_get_name(clk);
			}
			}
		}
		}


		/* RBGB */
		/* RBGB */
		if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) {
		if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) {
			div = 6;
			div = 6;
			if (req_48kHz_rate)
				div = rate / req_48kHz_rate;
			rbgx = rsnd_adg_calculate_rbgx(div);
			rbgx = rsnd_adg_calculate_rbgx(div);
			if (BRRx_MASK(rbgx) == rbgx) {
			if (BRRx_MASK(rbgx) == rbgx) {
				rbgb = rbgx;
				rbgb = rbgx;
				adg->rbgb_rate_for_48khz = rate / div;
				adg->rbgb_rate_for_48khz = rate / div;
				ckr |= brg_table[i] << 16;
				ckr |= brg_table[i] << 16;
				if (req_48kHz_rate) {
					parent_clk_name = __clk_get_name(clk);
					ckr |= 0x80000000;
				}
			}
		}
	}

	/*
	 * ADG supports BRRA/BRRB output only.
	 * this means all clkout0/1/2/3 will be * same rate
	 */

	/*
	 * for clkout
	 */
	if (!count) {
		clk = clk_register_fixed_rate(dev, clkout_name[i],
					      parent_clk_name,
					      (parent_clk_name) ?
					      0 : CLK_IS_ROOT, req_rate);
		if (!IS_ERR(clk)) {
			adg->clkout[CLKOUT] = clk;
			of_clk_add_provider(np, of_clk_src_simple_get, clk);
		}
	}
	/*
	 * for clkout0/1/2/3
	 */
	else {
		for (i = 0; i < CLKOUTMAX; i++) {
			clk = clk_register_fixed_rate(dev, clkout_name[i],
						      parent_clk_name,
						      (parent_clk_name) ?
						      0 : CLK_IS_ROOT,
						      req_rate);
			if (!IS_ERR(clk)) {
				adg->onecell.clks	= adg->clkout;
				adg->onecell.clk_num	= CLKOUTMAX;

				adg->clkout[i] = clk;

				of_clk_add_provider(np, of_clk_src_onecell_get,
						    &adg->onecell);
			}
			}
		}
		}
	}
	}
@@ -478,6 +567,8 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
	rsnd_mod_write(adg_mod, BRRA,  rbga);
	rsnd_mod_write(adg_mod, BRRA,  rbga);
	rsnd_mod_write(adg_mod, BRRB,  rbgb);
	rsnd_mod_write(adg_mod, BRRB,  rbgb);


	for_each_rsnd_clkout(clk, adg, i)
		dev_dbg(dev, "clkout %d : %p : %ld\n", i, clk, clk_get_rate(clk));
	dev_dbg(dev, "SSICKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
	dev_dbg(dev, "SSICKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
		ckr, rbga, rbgb);
		ckr, rbga, rbgb);
}
}
@@ -504,8 +595,7 @@ int rsnd_adg_probe(struct platform_device *pdev,
	adg->mod.priv = priv;
	adg->mod.priv = priv;


	rsnd_adg_get_clkin(priv, adg);
	rsnd_adg_get_clkin(priv, adg);

	rsnd_adg_get_clkout(priv, adg);
	rsnd_adg_ssi_clk_init(priv, adg);


	priv->adg = adg;
	priv->adg = adg;