All of lore.kernel.org
 help / color / mirror / Atom feed
From: <jiada_wang@mentor.com>
To: <broonie@kernel.org>, <perex@perex.cz>, <tiwai@suse.com>,
	<kuninori.morimoto.gx@renesas.com>,
	<vladimir_zapolskiy@mentor.com>
Cc: <alsa-devel@alsa-project.org>, <linux-kernel@vger.kernel.org>,
	<jiada_wang@mentor.com>
Subject: [PATCH linux-next v2 6/6] ASoC: rsnd: add avb clocks
Date: Mon, 3 Dec 2018 20:24:27 +0900	[thread overview]
Message-ID: <20181203112427.18324-1-jiada_wang@mentor.com> (raw)

From: Jiada Wang <jiada_wang@mentor.com>

There are AVB Counter Clocks in ADG, each clock has 12bits integral
and 8 bits fractional dividers which operates with S0D1ϕ clock.

This patch registers 8 AVB Counter Clocks when clock-cells of
rcar_sound node is 2,

Signed-off-by: Jiada Wang <jiada_wang@mentor.com>
---
 sound/soc/sh/rcar/adg.c  | 306 +++++++++++++++++++++++++++++++++++++--
 sound/soc/sh/rcar/gen.c  |   9 ++
 sound/soc/sh/rcar/rsnd.h |   9 ++
 3 files changed, 315 insertions(+), 9 deletions(-)

diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 6768a66588eb..2c03d420ae76 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -5,6 +5,8 @@
 //  Copyright (C) 2013  Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
 
 #include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <dt-bindings/clock/renesas-adg.h>
 #include "rsnd.h"
 
 #define CLKA	0
@@ -21,13 +23,33 @@
 
 #define BRRx_MASK(x) (0x3FF & x)
 
+#define ADG_CLK_NAME		"adg"
+#define AVB_CLK_NAME		"avb"
+#define AVB_CLK_NUM		8
+#define AVB_CLK_NAME_SIZE	10
+#define AVB_MAX_RATE		25000000
+#define AVB_DIV_EN_COM		BIT(31)
+#define AVB_DIV_MASK		0x3ffff
+#define AVB_MAX_DIV		0x3ffc0
+
 static struct rsnd_mod_ops adg_ops = {
 	.name = "adg",
 };
 
+struct clk_avb {
+	struct clk_hw hw;
+	unsigned int idx;
+	struct rsnd_mod *mod;
+	/* lock reg access */
+	spinlock_t *lock;
+};
+
+#define to_clk_avb(_hw) container_of(_hw, struct clk_avb, hw)
+
 struct rsnd_adg {
 	struct clk *clk[CLKMAX];
 	struct clk *clkout[CLKOUTMAX];
+	struct clk *clkavb[AVB_CLK_NUM];
 	struct clk_onecell_data onecell;
 	struct rsnd_mod mod;
 	u32 flags;
@@ -37,6 +59,7 @@ struct rsnd_adg {
 
 	int rbga_rate_for_441khz; /* RBGA */
 	int rbgb_rate_for_48khz;  /* RBGB */
+	spinlock_t avb_lock;
 };
 
 #define LRCLK_ASYNC	(1 << 0)
@@ -408,6 +431,239 @@ void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
 	}
 }
 
+static struct clk *rsnd_adg_clk_src_twocell_get(struct of_phandle_args *clkspec,
+						void *data)
+{
+	unsigned int clkidx = clkspec->args[1];
+	struct rsnd_adg *adg = data;
+	const char *type;
+	struct clk *clk;
+
+	switch (clkspec->args[0]) {
+	case ADG_FIX:
+		type = "fixed";
+		if (clkidx >= CLKOUTMAX) {
+			pr_err("Invalid %s clock index %u\n", type,
+			       clkidx);
+			return ERR_PTR(-EINVAL);
+		}
+		clk = adg->clkout[clkidx];
+		break;
+	case ADG_AVB:
+		type = "avb";
+		if (clkidx >= AVB_CLK_NUM) {
+			pr_err("Invalid %s clock index %u\n", type,
+			       clkidx);
+			return ERR_PTR(-EINVAL);
+		}
+		clk = adg->clkavb[clkidx];
+		break;
+	default:
+		pr_err("Invalid ADG clock type %u\n", clkspec->args[0]);
+		return ERR_PTR(-EINVAL);
+	}
+
+	return clk;
+}
+
+static void clk_avb_div_write(struct rsnd_mod *mod, u32 data, int idx)
+{
+	switch (idx) {
+	case 0:
+		rsnd_mod_write(mod, AVB_CLK_DIV0, data);
+		break;
+	case 1:
+		rsnd_mod_write(mod, AVB_CLK_DIV1, data);
+		break;
+	case 2:
+		rsnd_mod_write(mod, AVB_CLK_DIV2, data);
+		break;
+	case 3:
+		rsnd_mod_write(mod, AVB_CLK_DIV3, data);
+		break;
+	case 4:
+		rsnd_mod_write(mod, AVB_CLK_DIV4, data);
+		break;
+	case 5:
+		rsnd_mod_write(mod, AVB_CLK_DIV5, data);
+		break;
+	case 6:
+		rsnd_mod_write(mod, AVB_CLK_DIV6, data);
+		break;
+	case 7:
+		rsnd_mod_write(mod, AVB_CLK_DIV7, data);
+		break;
+	}
+}
+
+static u32 clk_avb_div_read(struct rsnd_mod *mod, int idx)
+{
+	u32 val = 0;
+
+	switch (idx) {
+	case 0:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV0);
+		break;
+	case 1:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV1);
+		break;
+	case 2:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV2);
+		break;
+	case 3:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV3);
+		break;
+	case 4:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV4);
+		break;
+	case 5:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV5);
+		break;
+	case 6:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV6);
+		break;
+	case 7:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV7);
+		break;
+	}
+
+	return val;
+}
+
+static int clk_avb_is_enabled(struct clk_hw *hw)
+{
+	struct clk_avb *avb = to_clk_avb(hw);
+
+	return rsnd_mod_read(avb->mod, AVB_CLK_CONFIG) & BIT(avb->idx);
+}
+
+static int clk_avb_enabledisable(struct clk_hw *hw, int enable)
+{
+	struct clk_avb *avb = to_clk_avb(hw);
+	u32 val;
+
+	spin_lock(avb->lock);
+
+	val = rsnd_mod_read(avb->mod, AVB_CLK_CONFIG);
+	if (enable)
+		val |= BIT(avb->idx);
+	else
+		val &= ~BIT(avb->idx);
+	rsnd_mod_write(avb->mod, AVB_CLK_CONFIG,  val);
+
+	spin_unlock(avb->lock);
+
+	return 0;
+}
+
+static int clk_avb_enable(struct clk_hw *hw)
+{
+	return clk_avb_enabledisable(hw, 1);
+}
+
+static void clk_avb_disable(struct clk_hw *hw)
+{
+	clk_avb_enabledisable(hw, 0);
+}
+
+static unsigned long clk_avb_recalc_rate(struct clk_hw *hw,
+					 unsigned long parent_rate)
+{
+	struct clk_avb *avb = to_clk_avb(hw);
+	u32 div;
+
+	div = clk_avb_div_read(avb->mod, avb->idx) & AVB_DIV_MASK;
+	if (!div)
+		return parent_rate;
+
+	return parent_rate * 32 / div;
+}
+
+static unsigned int clk_avb_calc_div(unsigned long rate,
+				     unsigned long parent_rate)
+{
+	unsigned int div;
+
+	if (!rate)
+		rate = 1;
+
+	if (rate > AVB_MAX_RATE)
+		rate = AVB_MAX_RATE;
+
+	div = DIV_ROUND_CLOSEST(parent_rate * 32, rate);
+
+	if (div > AVB_MAX_DIV)
+		div = AVB_MAX_DIV;
+
+	return div;
+}
+
+static long clk_avb_round_rate(struct clk_hw *hw, unsigned long rate,
+			       unsigned long *parent_rate)
+{
+	unsigned int div = clk_avb_calc_div(rate, *parent_rate);
+
+	return (*parent_rate * 32) / div;
+}
+
+static int clk_avb_set_rate(struct clk_hw *hw, unsigned long rate,
+			    unsigned long parent_rate)
+{
+	struct clk_avb *avb = to_clk_avb(hw);
+	unsigned int div = clk_avb_calc_div(rate, parent_rate);
+	u32 val;
+
+	val = clk_avb_div_read(avb->mod, avb->idx) & ~AVB_DIV_MASK;
+	clk_avb_div_write(avb->mod, val | div, avb->idx);
+
+	return 0;
+}
+
+static const struct clk_ops clk_avb_ops = {
+	.enable = clk_avb_enable,
+	.disable = clk_avb_disable,
+	.is_enabled = clk_avb_is_enabled,
+	.recalc_rate = clk_avb_recalc_rate,
+	.round_rate = clk_avb_round_rate,
+	.set_rate = clk_avb_set_rate,
+};
+
+static struct clk *clk_register_avb(struct device *dev, struct rsnd_mod *mod,
+				    unsigned int id, spinlock_t *lock)
+{
+	struct clk_init_data init;
+	struct clk_avb *avb;
+	struct clk *clk;
+	char name[AVB_CLK_NAME_SIZE];
+	const char *parent_name = ADG_CLK_NAME;
+
+	avb = devm_kzalloc(dev, sizeof(*avb), GFP_KERNEL);
+	if (!avb)
+		return ERR_PTR(-ENOMEM);
+
+	snprintf(name, AVB_CLK_NAME_SIZE, "%s%u", AVB_CLK_NAME, id);
+
+	avb->idx = id;
+	avb->lock = lock;
+	avb->mod = mod;
+
+	/* Register the clock. */
+	init.name = name;
+	init.ops = &clk_avb_ops;
+	init.flags = CLK_IS_BASIC;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+
+	avb->hw.init = &init;
+
+	/* init DIV to a valid state */
+	clk_avb_div_write(avb->mod, avb->idx, AVB_MAX_DIV);
+
+	clk = devm_clk_register(dev, &avb->hw);
+
+	return clk;
+}
+
 static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
 			       struct rsnd_adg *adg)
 {
@@ -436,6 +692,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
 	unsigned long req_48kHz_rate, req_441kHz_rate;
 	int i, req_size;
 	const char *parent_clk_name = NULL;
+	struct rsnd_mod *mod = rsnd_mod_get(adg);
 	static const char * const clkout_name[] = {
 		[CLKOUT]  = "audio_clkout",
 		[CLKOUT1] = "audio_clkout1",
@@ -540,21 +797,23 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
 	 */
 
 	of_property_read_u32(np, "#clock-cells", &count);
-	/*
-	 * for clkout
-	 */
-	if (!count) {
+
+	switch (count) {
+	case 0:
+		/*
+		 * for clkout
+		 */
 		clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],
 					      parent_clk_name, 0, req_rate[0]);
 		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 {
+		break;
+	case 1:
+		/*
+		 * for clkout0/1/2/3
+		 */
 		for (i = 0; i < CLKOUTMAX; i++) {
 			clk = clk_register_fixed_rate(dev, clkout_name[i],
 						      parent_clk_name, 0,
@@ -566,6 +825,33 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
 		adg->onecell.clk_num	= CLKOUTMAX;
 		of_clk_add_provider(np, of_clk_src_onecell_get,
 				    &adg->onecell);
+		break;
+	case 2:
+		/*
+		 * for clkout0/1/2/3 and avb clocks
+		 */
+		for (i = 0; i < CLKOUTMAX; i++) {
+			clk = clk_register_fixed_rate(dev, clkout_name[i],
+						      parent_clk_name, 0,
+						      req_rate[0]);
+			if (!IS_ERR(clk))
+				adg->clkout[i] = clk;
+		}
+
+		for (i = 0; i < AVB_CLK_NUM; i++) {
+			clk = clk_register_avb(dev, mod, i, &adg->avb_lock);
+			if (!IS_ERR(clk))
+				adg->clkavb[i] = clk;
+		}
+
+		of_clk_add_provider(np, rsnd_adg_clk_src_twocell_get, adg);
+
+		rsnd_mod_write(mod, AVB_CLK_CONFIG, AVB_DIV_EN_COM);
+
+		break;
+	default:
+		dev_err(dev, "Invalid clock-cell %d\n", count);
+		break;
 	}
 
 rsnd_adg_get_clkout_end:
@@ -612,6 +898,8 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
 	if (!adg)
 		return -ENOMEM;
 
+	spin_lock_init(&adg->avb_lock);
+
 	ret = rsnd_mod_init(priv, &adg->mod, &adg_ops,
 		      NULL, 0, 0);
 	if (ret)
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index ca639404f2cd..1b000d03b76e 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -355,6 +355,15 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
 		RSND_GEN_S_REG(SRCOUT_TIMSEL3,	0x54),
 		RSND_GEN_S_REG(SRCOUT_TIMSEL4,	0x58),
 		RSND_GEN_S_REG(CMDOUT_TIMSEL,	0x5c),
+		RSND_GEN_S_REG(AVB_CLK_DIV0,	0x11c),
+		RSND_GEN_S_REG(AVB_CLK_DIV1,	0x120),
+		RSND_GEN_S_REG(AVB_CLK_DIV2,	0x124),
+		RSND_GEN_S_REG(AVB_CLK_DIV3,	0x128),
+		RSND_GEN_S_REG(AVB_CLK_DIV4,	0x12c),
+		RSND_GEN_S_REG(AVB_CLK_DIV5,	0x130),
+		RSND_GEN_S_REG(AVB_CLK_DIV6,	0x134),
+		RSND_GEN_S_REG(AVB_CLK_DIV7,	0x138),
+		RSND_GEN_S_REG(AVB_CLK_CONFIG,	0x13c),
 	};
 	static const struct rsnd_regmap_field_conf conf_ssi[] = {
 		RSND_GEN_M_REG(SSICR,		0x00,	0x40),
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 3c57129af6d1..d31b8a65985f 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -148,6 +148,15 @@ enum rsnd_reg {
 	RSND_REG_AUDIO_CLK_SEL0,
 	RSND_REG_AUDIO_CLK_SEL1,
 	RSND_REG_AUDIO_CLK_SEL2,
+	RSND_REG_AVB_CLK_DIV0,
+	RSND_REG_AVB_CLK_DIV1,
+	RSND_REG_AVB_CLK_DIV2,
+	RSND_REG_AVB_CLK_DIV3,
+	RSND_REG_AVB_CLK_DIV4,
+	RSND_REG_AVB_CLK_DIV5,
+	RSND_REG_AVB_CLK_DIV6,
+	RSND_REG_AVB_CLK_DIV7,
+	RSND_REG_AVB_CLK_CONFIG,
 
 	/* SSIU */
 	RSND_REG_SSI_MODE,
-- 
2.17.0


WARNING: multiple messages have this Message-ID (diff)
From: <jiada_wang@mentor.com>
To: broonie@kernel.org, perex@perex.cz, tiwai@suse.com,
	kuninori.morimoto.gx@renesas.com, vladimir_zapolskiy@mentor.com
Cc: jiada_wang@mentor.com, alsa-devel@alsa-project.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH linux-next v2 6/6] ASoC: rsnd: add avb clocks
Date: Mon, 3 Dec 2018 20:24:27 +0900	[thread overview]
Message-ID: <20181203112427.18324-1-jiada_wang@mentor.com> (raw)

From: Jiada Wang <jiada_wang@mentor.com>

There are AVB Counter Clocks in ADG, each clock has 12bits integral
and 8 bits fractional dividers which operates with S0D1ϕ clock.

This patch registers 8 AVB Counter Clocks when clock-cells of
rcar_sound node is 2,

Signed-off-by: Jiada Wang <jiada_wang@mentor.com>
---
 sound/soc/sh/rcar/adg.c  | 306 +++++++++++++++++++++++++++++++++++++--
 sound/soc/sh/rcar/gen.c  |   9 ++
 sound/soc/sh/rcar/rsnd.h |   9 ++
 3 files changed, 315 insertions(+), 9 deletions(-)

diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 6768a66588eb..2c03d420ae76 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -5,6 +5,8 @@
 //  Copyright (C) 2013  Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
 
 #include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <dt-bindings/clock/renesas-adg.h>
 #include "rsnd.h"
 
 #define CLKA	0
@@ -21,13 +23,33 @@
 
 #define BRRx_MASK(x) (0x3FF & x)
 
+#define ADG_CLK_NAME		"adg"
+#define AVB_CLK_NAME		"avb"
+#define AVB_CLK_NUM		8
+#define AVB_CLK_NAME_SIZE	10
+#define AVB_MAX_RATE		25000000
+#define AVB_DIV_EN_COM		BIT(31)
+#define AVB_DIV_MASK		0x3ffff
+#define AVB_MAX_DIV		0x3ffc0
+
 static struct rsnd_mod_ops adg_ops = {
 	.name = "adg",
 };
 
+struct clk_avb {
+	struct clk_hw hw;
+	unsigned int idx;
+	struct rsnd_mod *mod;
+	/* lock reg access */
+	spinlock_t *lock;
+};
+
+#define to_clk_avb(_hw) container_of(_hw, struct clk_avb, hw)
+
 struct rsnd_adg {
 	struct clk *clk[CLKMAX];
 	struct clk *clkout[CLKOUTMAX];
+	struct clk *clkavb[AVB_CLK_NUM];
 	struct clk_onecell_data onecell;
 	struct rsnd_mod mod;
 	u32 flags;
@@ -37,6 +59,7 @@ struct rsnd_adg {
 
 	int rbga_rate_for_441khz; /* RBGA */
 	int rbgb_rate_for_48khz;  /* RBGB */
+	spinlock_t avb_lock;
 };
 
 #define LRCLK_ASYNC	(1 << 0)
@@ -408,6 +431,239 @@ void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
 	}
 }
 
+static struct clk *rsnd_adg_clk_src_twocell_get(struct of_phandle_args *clkspec,
+						void *data)
+{
+	unsigned int clkidx = clkspec->args[1];
+	struct rsnd_adg *adg = data;
+	const char *type;
+	struct clk *clk;
+
+	switch (clkspec->args[0]) {
+	case ADG_FIX:
+		type = "fixed";
+		if (clkidx >= CLKOUTMAX) {
+			pr_err("Invalid %s clock index %u\n", type,
+			       clkidx);
+			return ERR_PTR(-EINVAL);
+		}
+		clk = adg->clkout[clkidx];
+		break;
+	case ADG_AVB:
+		type = "avb";
+		if (clkidx >= AVB_CLK_NUM) {
+			pr_err("Invalid %s clock index %u\n", type,
+			       clkidx);
+			return ERR_PTR(-EINVAL);
+		}
+		clk = adg->clkavb[clkidx];
+		break;
+	default:
+		pr_err("Invalid ADG clock type %u\n", clkspec->args[0]);
+		return ERR_PTR(-EINVAL);
+	}
+
+	return clk;
+}
+
+static void clk_avb_div_write(struct rsnd_mod *mod, u32 data, int idx)
+{
+	switch (idx) {
+	case 0:
+		rsnd_mod_write(mod, AVB_CLK_DIV0, data);
+		break;
+	case 1:
+		rsnd_mod_write(mod, AVB_CLK_DIV1, data);
+		break;
+	case 2:
+		rsnd_mod_write(mod, AVB_CLK_DIV2, data);
+		break;
+	case 3:
+		rsnd_mod_write(mod, AVB_CLK_DIV3, data);
+		break;
+	case 4:
+		rsnd_mod_write(mod, AVB_CLK_DIV4, data);
+		break;
+	case 5:
+		rsnd_mod_write(mod, AVB_CLK_DIV5, data);
+		break;
+	case 6:
+		rsnd_mod_write(mod, AVB_CLK_DIV6, data);
+		break;
+	case 7:
+		rsnd_mod_write(mod, AVB_CLK_DIV7, data);
+		break;
+	}
+}
+
+static u32 clk_avb_div_read(struct rsnd_mod *mod, int idx)
+{
+	u32 val = 0;
+
+	switch (idx) {
+	case 0:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV0);
+		break;
+	case 1:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV1);
+		break;
+	case 2:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV2);
+		break;
+	case 3:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV3);
+		break;
+	case 4:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV4);
+		break;
+	case 5:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV5);
+		break;
+	case 6:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV6);
+		break;
+	case 7:
+		val = rsnd_mod_read(mod, AVB_CLK_DIV7);
+		break;
+	}
+
+	return val;
+}
+
+static int clk_avb_is_enabled(struct clk_hw *hw)
+{
+	struct clk_avb *avb = to_clk_avb(hw);
+
+	return rsnd_mod_read(avb->mod, AVB_CLK_CONFIG) & BIT(avb->idx);
+}
+
+static int clk_avb_enabledisable(struct clk_hw *hw, int enable)
+{
+	struct clk_avb *avb = to_clk_avb(hw);
+	u32 val;
+
+	spin_lock(avb->lock);
+
+	val = rsnd_mod_read(avb->mod, AVB_CLK_CONFIG);
+	if (enable)
+		val |= BIT(avb->idx);
+	else
+		val &= ~BIT(avb->idx);
+	rsnd_mod_write(avb->mod, AVB_CLK_CONFIG,  val);
+
+	spin_unlock(avb->lock);
+
+	return 0;
+}
+
+static int clk_avb_enable(struct clk_hw *hw)
+{
+	return clk_avb_enabledisable(hw, 1);
+}
+
+static void clk_avb_disable(struct clk_hw *hw)
+{
+	clk_avb_enabledisable(hw, 0);
+}
+
+static unsigned long clk_avb_recalc_rate(struct clk_hw *hw,
+					 unsigned long parent_rate)
+{
+	struct clk_avb *avb = to_clk_avb(hw);
+	u32 div;
+
+	div = clk_avb_div_read(avb->mod, avb->idx) & AVB_DIV_MASK;
+	if (!div)
+		return parent_rate;
+
+	return parent_rate * 32 / div;
+}
+
+static unsigned int clk_avb_calc_div(unsigned long rate,
+				     unsigned long parent_rate)
+{
+	unsigned int div;
+
+	if (!rate)
+		rate = 1;
+
+	if (rate > AVB_MAX_RATE)
+		rate = AVB_MAX_RATE;
+
+	div = DIV_ROUND_CLOSEST(parent_rate * 32, rate);
+
+	if (div > AVB_MAX_DIV)
+		div = AVB_MAX_DIV;
+
+	return div;
+}
+
+static long clk_avb_round_rate(struct clk_hw *hw, unsigned long rate,
+			       unsigned long *parent_rate)
+{
+	unsigned int div = clk_avb_calc_div(rate, *parent_rate);
+
+	return (*parent_rate * 32) / div;
+}
+
+static int clk_avb_set_rate(struct clk_hw *hw, unsigned long rate,
+			    unsigned long parent_rate)
+{
+	struct clk_avb *avb = to_clk_avb(hw);
+	unsigned int div = clk_avb_calc_div(rate, parent_rate);
+	u32 val;
+
+	val = clk_avb_div_read(avb->mod, avb->idx) & ~AVB_DIV_MASK;
+	clk_avb_div_write(avb->mod, val | div, avb->idx);
+
+	return 0;
+}
+
+static const struct clk_ops clk_avb_ops = {
+	.enable = clk_avb_enable,
+	.disable = clk_avb_disable,
+	.is_enabled = clk_avb_is_enabled,
+	.recalc_rate = clk_avb_recalc_rate,
+	.round_rate = clk_avb_round_rate,
+	.set_rate = clk_avb_set_rate,
+};
+
+static struct clk *clk_register_avb(struct device *dev, struct rsnd_mod *mod,
+				    unsigned int id, spinlock_t *lock)
+{
+	struct clk_init_data init;
+	struct clk_avb *avb;
+	struct clk *clk;
+	char name[AVB_CLK_NAME_SIZE];
+	const char *parent_name = ADG_CLK_NAME;
+
+	avb = devm_kzalloc(dev, sizeof(*avb), GFP_KERNEL);
+	if (!avb)
+		return ERR_PTR(-ENOMEM);
+
+	snprintf(name, AVB_CLK_NAME_SIZE, "%s%u", AVB_CLK_NAME, id);
+
+	avb->idx = id;
+	avb->lock = lock;
+	avb->mod = mod;
+
+	/* Register the clock. */
+	init.name = name;
+	init.ops = &clk_avb_ops;
+	init.flags = CLK_IS_BASIC;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+
+	avb->hw.init = &init;
+
+	/* init DIV to a valid state */
+	clk_avb_div_write(avb->mod, avb->idx, AVB_MAX_DIV);
+
+	clk = devm_clk_register(dev, &avb->hw);
+
+	return clk;
+}
+
 static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
 			       struct rsnd_adg *adg)
 {
@@ -436,6 +692,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
 	unsigned long req_48kHz_rate, req_441kHz_rate;
 	int i, req_size;
 	const char *parent_clk_name = NULL;
+	struct rsnd_mod *mod = rsnd_mod_get(adg);
 	static const char * const clkout_name[] = {
 		[CLKOUT]  = "audio_clkout",
 		[CLKOUT1] = "audio_clkout1",
@@ -540,21 +797,23 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
 	 */
 
 	of_property_read_u32(np, "#clock-cells", &count);
-	/*
-	 * for clkout
-	 */
-	if (!count) {
+
+	switch (count) {
+	case 0:
+		/*
+		 * for clkout
+		 */
 		clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],
 					      parent_clk_name, 0, req_rate[0]);
 		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 {
+		break;
+	case 1:
+		/*
+		 * for clkout0/1/2/3
+		 */
 		for (i = 0; i < CLKOUTMAX; i++) {
 			clk = clk_register_fixed_rate(dev, clkout_name[i],
 						      parent_clk_name, 0,
@@ -566,6 +825,33 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
 		adg->onecell.clk_num	= CLKOUTMAX;
 		of_clk_add_provider(np, of_clk_src_onecell_get,
 				    &adg->onecell);
+		break;
+	case 2:
+		/*
+		 * for clkout0/1/2/3 and avb clocks
+		 */
+		for (i = 0; i < CLKOUTMAX; i++) {
+			clk = clk_register_fixed_rate(dev, clkout_name[i],
+						      parent_clk_name, 0,
+						      req_rate[0]);
+			if (!IS_ERR(clk))
+				adg->clkout[i] = clk;
+		}
+
+		for (i = 0; i < AVB_CLK_NUM; i++) {
+			clk = clk_register_avb(dev, mod, i, &adg->avb_lock);
+			if (!IS_ERR(clk))
+				adg->clkavb[i] = clk;
+		}
+
+		of_clk_add_provider(np, rsnd_adg_clk_src_twocell_get, adg);
+
+		rsnd_mod_write(mod, AVB_CLK_CONFIG, AVB_DIV_EN_COM);
+
+		break;
+	default:
+		dev_err(dev, "Invalid clock-cell %d\n", count);
+		break;
 	}
 
 rsnd_adg_get_clkout_end:
@@ -612,6 +898,8 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
 	if (!adg)
 		return -ENOMEM;
 
+	spin_lock_init(&adg->avb_lock);
+
 	ret = rsnd_mod_init(priv, &adg->mod, &adg_ops,
 		      NULL, 0, 0);
 	if (ret)
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index ca639404f2cd..1b000d03b76e 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -355,6 +355,15 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
 		RSND_GEN_S_REG(SRCOUT_TIMSEL3,	0x54),
 		RSND_GEN_S_REG(SRCOUT_TIMSEL4,	0x58),
 		RSND_GEN_S_REG(CMDOUT_TIMSEL,	0x5c),
+		RSND_GEN_S_REG(AVB_CLK_DIV0,	0x11c),
+		RSND_GEN_S_REG(AVB_CLK_DIV1,	0x120),
+		RSND_GEN_S_REG(AVB_CLK_DIV2,	0x124),
+		RSND_GEN_S_REG(AVB_CLK_DIV3,	0x128),
+		RSND_GEN_S_REG(AVB_CLK_DIV4,	0x12c),
+		RSND_GEN_S_REG(AVB_CLK_DIV5,	0x130),
+		RSND_GEN_S_REG(AVB_CLK_DIV6,	0x134),
+		RSND_GEN_S_REG(AVB_CLK_DIV7,	0x138),
+		RSND_GEN_S_REG(AVB_CLK_CONFIG,	0x13c),
 	};
 	static const struct rsnd_regmap_field_conf conf_ssi[] = {
 		RSND_GEN_M_REG(SSICR,		0x00,	0x40),
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 3c57129af6d1..d31b8a65985f 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -148,6 +148,15 @@ enum rsnd_reg {
 	RSND_REG_AUDIO_CLK_SEL0,
 	RSND_REG_AUDIO_CLK_SEL1,
 	RSND_REG_AUDIO_CLK_SEL2,
+	RSND_REG_AVB_CLK_DIV0,
+	RSND_REG_AVB_CLK_DIV1,
+	RSND_REG_AVB_CLK_DIV2,
+	RSND_REG_AVB_CLK_DIV3,
+	RSND_REG_AVB_CLK_DIV4,
+	RSND_REG_AVB_CLK_DIV5,
+	RSND_REG_AVB_CLK_DIV6,
+	RSND_REG_AVB_CLK_DIV7,
+	RSND_REG_AVB_CLK_CONFIG,
 
 	/* SSIU */
 	RSND_REG_SSI_MODE,
-- 
2.17.0

_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

             reply	other threads:[~2018-12-03 11:25 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-12-03 11:24 jiada_wang [this message]
2018-12-03 11:24 ` [PATCH linux-next v2 6/6] ASoC: rsnd: add avb clocks jiada_wang
2018-12-03 12:53 ` Vladimir Zapolskiy
2018-12-03 12:53   ` Vladimir Zapolskiy
2018-12-04  8:27   ` Jiada Wang
2018-12-04  8:27     ` Jiada Wang
2018-12-04  1:52 ` Kuninori Morimoto
2018-12-04  8:35   ` Jiada Wang
2018-12-04  1:54 ` Kuninori Morimoto

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181203112427.18324-1-jiada_wang@mentor.com \
    --to=jiada_wang@mentor.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=broonie@kernel.org \
    --cc=kuninori.morimoto.gx@renesas.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=perex@perex.cz \
    --cc=tiwai@suse.com \
    --cc=vladimir_zapolskiy@mentor.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.