summaryrefslogtreecommitdiffstats
path: root/sound/soc/sh/rcar
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sh/rcar')
-rw-r--r--sound/soc/sh/rcar/adg.c68
-rw-r--r--sound/soc/sh/rcar/core.c175
-rw-r--r--sound/soc/sh/rcar/rsnd.h4
3 files changed, 173 insertions, 74 deletions
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 0b8926600d90..230c48648af3 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -358,8 +358,6 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
ckr = 0x80000000; /* BRGB output = 48kHz */
rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr | ckr);
- rsnd_mod_write(adg_mod, BRRA, adg->brga);
- rsnd_mod_write(adg_mod, BRRB, adg->brgb);
dev_dbg(dev, "CLKOUT is based on BRG%c (= %dHz)\n",
(ckr) ? 'B' : 'A',
@@ -372,9 +370,16 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
{
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
struct clk *clk;
int i;
+ if (enable) {
+ rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr);
+ rsnd_mod_write(adg_mod, BRRA, adg->brga);
+ rsnd_mod_write(adg_mod, BRRB, adg->brgb);
+ }
+
for_each_rsnd_clkin(clk, adg, i) {
if (enable) {
clk_prepare_enable(clk);
@@ -485,12 +490,12 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv)
struct device_node *np = dev->of_node;
struct property *prop;
u32 ckr, brgx, brga, brgb;
- u32 rate, div;
u32 req_rate[ADG_HZ_SIZE] = {};
uint32_t count = 0;
unsigned long req_Hz[ADG_HZ_SIZE];
int clkout_size;
int i, req_size;
+ int approximate = 0;
const char *parent_clk_name = NULL;
const char * const *clkout_name;
int brg_table[] = {
@@ -501,8 +506,8 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv)
};
ckr = 0;
- brga = 2; /* default 1/6 */
- brgb = 2; /* default 1/6 */
+ brga = 0xff; /* default */
+ brgb = 0xff; /* default */
/*
* ADG supports BRRA/BRRB output only
@@ -537,17 +542,41 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv)
* rsnd_adg_ssi_clk_try_start()
* rsnd_ssi_master_clk_start()
*/
+
+ /*
+ * [APPROXIMATE]
+ *
+ * clk_i (internal clock) can't create accurate rate, it will be approximate rate.
+ *
+ * <Note>
+ *
+ * clk_i needs x2 of required maximum rate.
+ * see
+ * - Minimum division of BRRA/BRRB
+ * - rsnd_ssi_clk_query()
+ *
+ * Sample Settings for TDM 8ch, 32bit width
+ *
+ * 8(ch) x 32(bit) x 44100(Hz) x 2<Note> = 22579200
+ * 8(ch) x 32(bit) x 48000(Hz) x 2<Note> = 24576000
+ *
+ * clock-frequency = <22579200 24576000>;
+ */
for_each_rsnd_clkin(clk, adg, i) {
+ u32 rate, div;
+
rate = clk_get_rate(clk);
if (0 == rate) /* not used */
continue;
/* BRGA */
- if (!adg->brg_rate[ADG_HZ_441] && (0 == rate % 44100)) {
- div = 6;
- if (req_Hz[ADG_HZ_441])
- div = rate / req_Hz[ADG_HZ_441];
+
+ if (i == CLKI)
+ /* see [APPROXIMATE] */
+ rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_441]) * req_Hz[ADG_HZ_441];
+ if (!adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441] && (0 == rate % 44100)) {
+ div = rate / req_Hz[ADG_HZ_441];
brgx = rsnd_adg_calculate_brgx(div);
if (BRRx_MASK(brgx) == brgx) {
brga = brgx;
@@ -555,14 +584,18 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv)
ckr |= brg_table[i] << 20;
if (req_Hz[ADG_HZ_441])
parent_clk_name = __clk_get_name(clk);
+ if (i == CLKI)
+ approximate = 1;
}
}
/* BRGB */
- if (!adg->brg_rate[ADG_HZ_48] && (0 == rate % 48000)) {
- div = 6;
- if (req_Hz[ADG_HZ_48])
- div = rate / req_Hz[ADG_HZ_48];
+
+ if (i == CLKI)
+ /* see [APPROXIMATE] */
+ rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_48]) * req_Hz[ADG_HZ_48];
+ if (!adg->brg_rate[ADG_HZ_48] && req_Hz[ADG_HZ_48] && (0 == rate % 48000)) {
+ div = rate / req_Hz[ADG_HZ_48];
brgx = rsnd_adg_calculate_brgx(div);
if (BRRx_MASK(brgx) == brgx) {
brgb = brgx;
@@ -570,10 +603,19 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv)
ckr |= brg_table[i] << 16;
if (req_Hz[ADG_HZ_48])
parent_clk_name = __clk_get_name(clk);
+ if (i == CLKI)
+ approximate = 1;
}
}
}
+ if (!(adg->brg_rate[ADG_HZ_48] && req_Hz[ADG_HZ_48]) &&
+ !(adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441]))
+ goto rsnd_adg_get_clkout_end;
+
+ if (approximate)
+ dev_info(dev, "It uses CLK_I as approximate rate");
+
clkout_name = clkout_name_gen2;
clkout_size = ARRAY_SIZE(clkout_name_gen2);
if (rsnd_is_gen4(priv))
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 6a522e6dd85a..e29c2fee9521 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -1085,17 +1085,6 @@ static u64 rsnd_soc_dai_formats[] = {
SND_SOC_POSSIBLE_DAIFMT_DSP_B,
};
-static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
- .startup = rsnd_soc_dai_startup,
- .shutdown = rsnd_soc_dai_shutdown,
- .trigger = rsnd_soc_dai_trigger,
- .set_fmt = rsnd_soc_dai_set_fmt,
- .set_tdm_slot = rsnd_soc_set_dai_tdm_slot,
- .prepare = rsnd_soc_dai_prepare,
- .auto_selectable_formats = rsnd_soc_dai_formats,
- .num_auto_selectable_formats = ARRAY_SIZE(rsnd_soc_dai_formats),
-};
-
static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv,
struct rsnd_dai_stream *io,
struct device_node *dai_np)
@@ -1260,13 +1249,13 @@ int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name
return i;
}
-static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv,
- int *is_graph)
+static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph)
{
struct device *dev = rsnd_priv_to_dev(priv);
struct device_node *np = dev->of_node;
- struct device_node *dai_node;
- struct device_node *ret;
+ struct device_node *ports, *node;
+ int nr = 0;
+ int i = 0;
*is_graph = 0;
@@ -1274,26 +1263,53 @@ static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv,
* parse both previous dai (= rcar_sound,dai), and
* graph dai (= ports/port)
*/
- dai_node = of_get_child_by_name(np, RSND_NODE_DAI);
- if (dai_node) {
- ret = dai_node;
- goto of_node_compatible;
- }
- ret = np;
+ /*
+ * Simple-Card
+ */
+ node = of_get_child_by_name(np, RSND_NODE_DAI);
+ if (!node)
+ goto audio_graph;
- dai_node = of_graph_get_next_endpoint(np, NULL);
- if (dai_node)
- goto of_node_graph;
+ of_node_put(node);
- return NULL;
+ for_each_child_of_node(np, node) {
+ if (!of_node_name_eq(node, RSND_NODE_DAI))
+ continue;
+
+ priv->component_dais[i] = of_get_child_count(node);
+ nr += priv->component_dais[i];
+ i++;
+ if (i >= RSND_MAX_COMPONENT) {
+ dev_info(dev, "reach to max component\n");
+ of_node_put(node);
+ break;
+ }
+ }
+
+ return nr;
+
+audio_graph:
+ /*
+ * Audio-Graph-Card
+ */
+ for_each_child_of_node(np, ports) {
+ if (!of_node_name_eq(ports, "ports") &&
+ !of_node_name_eq(ports, "port"))
+ continue;
+ priv->component_dais[i] = of_graph_get_endpoint_count(ports);
+ nr += priv->component_dais[i];
+ i++;
+ if (i >= RSND_MAX_COMPONENT) {
+ dev_info(dev, "reach to max component\n");
+ of_node_put(node);
+ break;
+ }
+ }
-of_node_graph:
*is_graph = 1;
-of_node_compatible:
- of_node_put(dai_node);
- return ret;
+ return nr;
}
@@ -1328,8 +1344,7 @@ static int rsnd_preallocate_pages(struct snd_soc_pcm_runtime *rtd,
return 0;
}
-static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd,
- struct snd_soc_dai *dai)
+static int rsnd_soc_dai_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
int ret;
@@ -1355,8 +1370,22 @@ static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd,
return 0;
}
+static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
+ .pcm_new = rsnd_soc_dai_pcm_new,
+ .startup = rsnd_soc_dai_startup,
+ .shutdown = rsnd_soc_dai_shutdown,
+ .trigger = rsnd_soc_dai_trigger,
+ .set_fmt = rsnd_soc_dai_set_fmt,
+ .set_tdm_slot = rsnd_soc_set_dai_tdm_slot,
+ .prepare = rsnd_soc_dai_prepare,
+ .auto_selectable_formats = rsnd_soc_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(rsnd_soc_dai_formats),
+};
+
static void __rsnd_dai_probe(struct rsnd_priv *priv,
struct device_node *dai_np,
+ struct device_node *node_np,
+ uint32_t node_arg,
int dai_i)
{
struct rsnd_dai_stream *io_playback;
@@ -1374,10 +1403,16 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv,
snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i);
+ /* for multi Component */
+ rdai->dai_args.np = node_np;
+ rdai->dai_args.args_count = 1;
+ rdai->dai_args.args[0] = node_arg;
+
rdai->priv = priv;
drv->name = rdai->name;
drv->ops = &rsnd_soc_dai_ops;
- drv->pcm_new = rsnd_pcm_new;
+ drv->id = dai_i;
+ drv->dai_args = &rdai->dai_args;
io_playback->rdai = rdai;
io_capture->rdai = rdai;
@@ -1441,21 +1476,15 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv,
static int rsnd_dai_probe(struct rsnd_priv *priv)
{
- struct device_node *dai_node;
- struct device_node *dai_np;
struct snd_soc_dai_driver *rdrv;
struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *np = dev->of_node;
struct rsnd_dai *rdai;
- int nr;
+ int nr = 0;
int is_graph;
int dai_i;
- dai_node = rsnd_dai_of_node(priv, &is_graph);
- if (is_graph)
- nr = of_graph_get_endpoint_count(dai_node);
- else
- nr = of_get_child_count(dai_node);
-
+ nr = rsnd_dai_of_node(priv, &is_graph);
if (!nr)
return -EINVAL;
@@ -1473,26 +1502,42 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
*/
dai_i = 0;
if (is_graph) {
- for_each_endpoint_of_node(dai_node, dai_np) {
- __rsnd_dai_probe(priv, dai_np, dai_i);
- if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) {
- rdai = rsnd_rdai_get(priv, dai_i);
-
- rsnd_parse_connect_graph(priv, &rdai->playback, dai_np);
- rsnd_parse_connect_graph(priv, &rdai->capture, dai_np);
+ struct device_node *ports;
+ struct device_node *dai_np;
+
+ for_each_child_of_node(np, ports) {
+ if (!of_node_name_eq(ports, "ports") &&
+ !of_node_name_eq(ports, "port"))
+ continue;
+ for_each_endpoint_of_node(ports, dai_np) {
+ __rsnd_dai_probe(priv, dai_np, dai_np, 0, dai_i);
+ if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) {
+ rdai = rsnd_rdai_get(priv, dai_i);
+
+ rsnd_parse_connect_graph(priv, &rdai->playback, dai_np);
+ rsnd_parse_connect_graph(priv, &rdai->capture, dai_np);
+ }
+ dai_i++;
}
- dai_i++;
}
} else {
- for_each_child_of_node(dai_node, dai_np) {
- __rsnd_dai_probe(priv, dai_np, dai_i);
- if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) {
- rdai = rsnd_rdai_get(priv, dai_i);
+ struct device_node *node;
+ struct device_node *dai_np;
+
+ for_each_child_of_node(np, node) {
+ if (!of_node_name_eq(node, RSND_NODE_DAI))
+ continue;
- rsnd_parse_connect_simple(priv, &rdai->playback, dai_np);
- rsnd_parse_connect_simple(priv, &rdai->capture, dai_np);
+ for_each_child_of_node(node, dai_np) {
+ __rsnd_dai_probe(priv, dai_np, np, dai_i, dai_i);
+ if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) {
+ rdai = rsnd_rdai_get(priv, dai_i);
+
+ rsnd_parse_connect_simple(priv, &rdai->playback, dai_np);
+ rsnd_parse_connect_simple(priv, &rdai->capture, dai_np);
+ }
+ dai_i++;
}
- dai_i++;
}
}
@@ -1922,6 +1967,7 @@ static int rsnd_probe(struct platform_device *pdev)
rsnd_dai_probe,
};
int ret, i;
+ int ci;
/*
* init priv data
@@ -1958,11 +2004,18 @@ static int rsnd_probe(struct platform_device *pdev)
/*
* asoc register
*/
- ret = devm_snd_soc_register_component(dev, &rsnd_soc_component,
- priv->daidrv, rsnd_rdai_nr(priv));
- if (ret < 0) {
- dev_err(dev, "cannot snd dai register\n");
- goto exit_snd_probe;
+ ci = 0;
+ for (i = 0; priv->component_dais[i] > 0; i++) {
+ int nr = priv->component_dais[i];
+
+ ret = devm_snd_soc_register_component(dev, &rsnd_soc_component,
+ priv->daidrv + ci, nr);
+ if (ret < 0) {
+ dev_err(dev, "cannot snd component register\n");
+ goto exit_snd_probe;
+ }
+
+ ci += nr;
}
pm_runtime_enable(dev);
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 239705d52517..43c0d675cc34 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -545,6 +545,7 @@ struct rsnd_dai {
struct rsnd_dai_stream capture;
struct rsnd_priv *priv;
struct snd_pcm_hw_constraint_list constraint;
+ struct of_phandle_args dai_args;
int max_channels; /* 2ch - 16ch */
int ssi_lane; /* 1lane - 4lane */
@@ -702,6 +703,9 @@ struct rsnd_priv {
struct snd_soc_dai_driver *daidrv;
struct rsnd_dai *rdai;
int rdai_nr;
+
+#define RSND_MAX_COMPONENT 3
+ int component_dais[RSND_MAX_COMPONENT];
};
#define rsnd_priv_to_pdev(priv) ((priv)->pdev)