mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-03 19:24:02 +08:00
ASoC: rsnd: add Multi channel support
This patch adds Multi channel support on Renesas R-Car sound. This patch is tested on Salvator-X board, but it can't use Multi channel, because supported format is different between codec chip and R-Car. Thus, it was tested on board which doesn't mount codec chip, with oscilloscope. Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
44bf5361e2
commit
b4c83b1715
@ -308,3 +308,21 @@ Example: simple sound card for TDM
|
|||||||
sound-dai = <&xxx>;
|
sound-dai = <&xxx>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Example: simple sound card for Multi channel
|
||||||
|
|
||||||
|
&rcar_sound {
|
||||||
|
pinctrl-0 = <&sound_pins &sound_clk_pins>;
|
||||||
|
pinctrl-names = "default";
|
||||||
|
|
||||||
|
/* Single DAI */
|
||||||
|
#sound-dai-cells = <0>;
|
||||||
|
|
||||||
|
status = "okay";
|
||||||
|
|
||||||
|
rcar_sound,dai {
|
||||||
|
dai0 {
|
||||||
|
playback = <&ssi0 &ssi1 &ssi2 &src0 &dvc0>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -215,7 +215,11 @@ int rsnd_get_slot_num(struct rsnd_dai_stream *io)
|
|||||||
int rsnd_get_slot_width(struct rsnd_dai_stream *io)
|
int rsnd_get_slot_width(struct rsnd_dai_stream *io)
|
||||||
{
|
{
|
||||||
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
||||||
int chan = runtime->channels / rsnd_get_slot_num(io);
|
int chan = runtime->channels;
|
||||||
|
|
||||||
|
/* Multi channel Mode */
|
||||||
|
if (rsnd_ssi_multi_slaves(io))
|
||||||
|
chan /= rsnd_get_slot_num(io);
|
||||||
|
|
||||||
/* TDM Extend Mode needs 8ch */
|
/* TDM Extend Mode needs 8ch */
|
||||||
if (chan == 6)
|
if (chan == 6)
|
||||||
|
@ -226,6 +226,9 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
|
|||||||
const static struct rsnd_regmap_field_conf conf_ssiu[] = {
|
const static struct rsnd_regmap_field_conf conf_ssiu[] = {
|
||||||
RSND_GEN_S_REG(SSI_MODE0, 0x800),
|
RSND_GEN_S_REG(SSI_MODE0, 0x800),
|
||||||
RSND_GEN_S_REG(SSI_MODE1, 0x804),
|
RSND_GEN_S_REG(SSI_MODE1, 0x804),
|
||||||
|
RSND_GEN_S_REG(SSI_MODE2, 0x808),
|
||||||
|
RSND_GEN_S_REG(SSI_CONTROL, 0x810),
|
||||||
|
|
||||||
/* FIXME: it needs SSI_MODE2/3 in the future */
|
/* FIXME: it needs SSI_MODE2/3 in the future */
|
||||||
RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80),
|
RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80),
|
||||||
RSND_GEN_M_REG(SSI_BUSIF_ADINR, 0x4, 0x80),
|
RSND_GEN_M_REG(SSI_BUSIF_ADINR, 0x4, 0x80),
|
||||||
|
@ -47,6 +47,8 @@ enum rsnd_reg {
|
|||||||
RSND_REG_SSI_MODE, /* Gen2 only */
|
RSND_REG_SSI_MODE, /* Gen2 only */
|
||||||
RSND_REG_SSI_MODE0,
|
RSND_REG_SSI_MODE0,
|
||||||
RSND_REG_SSI_MODE1,
|
RSND_REG_SSI_MODE1,
|
||||||
|
RSND_REG_SSI_MODE2,
|
||||||
|
RSND_REG_SSI_CONTROL,
|
||||||
RSND_REG_SSI_CTRL, /* Gen2 only */
|
RSND_REG_SSI_CTRL, /* Gen2 only */
|
||||||
RSND_REG_SSI_BUSIF_MODE, /* Gen2 only */
|
RSND_REG_SSI_BUSIF_MODE, /* Gen2 only */
|
||||||
RSND_REG_SSI_BUSIF_ADINR, /* Gen2 only */
|
RSND_REG_SSI_BUSIF_ADINR, /* Gen2 only */
|
||||||
@ -181,7 +183,10 @@ enum rsnd_mod_type {
|
|||||||
RSND_MOD_CTU,
|
RSND_MOD_CTU,
|
||||||
RSND_MOD_CMD,
|
RSND_MOD_CMD,
|
||||||
RSND_MOD_SRC,
|
RSND_MOD_SRC,
|
||||||
RSND_MOD_SSIP, /* SSI parent */
|
RSND_MOD_SSIM3, /* SSI multi 3 */
|
||||||
|
RSND_MOD_SSIM2, /* SSI multi 2 */
|
||||||
|
RSND_MOD_SSIM1, /* SSI multi 1 */
|
||||||
|
RSND_MOD_SSIP, /* SSI parent */
|
||||||
RSND_MOD_SSI,
|
RSND_MOD_SSI,
|
||||||
RSND_MOD_SSIU,
|
RSND_MOD_SSIU,
|
||||||
RSND_MOD_MAX,
|
RSND_MOD_MAX,
|
||||||
@ -542,6 +547,7 @@ void rsnd_ssi_remove(struct rsnd_priv *priv);
|
|||||||
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
|
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
|
||||||
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
|
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
|
||||||
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
|
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
|
||||||
|
u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io);
|
||||||
|
|
||||||
#define rsnd_ssi_is_pin_sharing(io) \
|
#define rsnd_ssi_is_pin_sharing(io) \
|
||||||
__rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
|
__rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
|
||||||
@ -549,10 +555,9 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
|
|||||||
|
|
||||||
#define rsnd_ssi_of_node(priv) \
|
#define rsnd_ssi_of_node(priv) \
|
||||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
|
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
|
||||||
#define rsnd_parse_connect_ssi(rdai, playback, capture) \
|
void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
|
||||||
rsnd_parse_connect_common(rdai, rsnd_ssi_mod_get, \
|
struct device_node *playback,
|
||||||
rsnd_ssi_of_node(rsnd_rdai_to_priv(rdai)), \
|
struct device_node *capture);
|
||||||
playback, capture)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* R-Car SSIU
|
* R-Car SSIU
|
||||||
|
@ -96,6 +96,7 @@ struct rsnd_ssi {
|
|||||||
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
|
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
|
||||||
#define rsnd_ssi_mode_flags(p) ((p)->flags)
|
#define rsnd_ssi_mode_flags(p) ((p)->flags)
|
||||||
#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
|
#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
|
||||||
|
#define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io))
|
||||||
|
|
||||||
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
|
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
|
||||||
{
|
{
|
||||||
@ -171,6 +172,41 @@ static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
|
||||||
|
{
|
||||||
|
struct rsnd_mod *mod;
|
||||||
|
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
||||||
|
struct rsnd_priv *priv = rsnd_io_to_priv(io);
|
||||||
|
struct device *dev = rsnd_priv_to_dev(priv);
|
||||||
|
enum rsnd_mod_type types[] = {
|
||||||
|
RSND_MOD_SSIM1,
|
||||||
|
RSND_MOD_SSIM2,
|
||||||
|
RSND_MOD_SSIM3,
|
||||||
|
};
|
||||||
|
int i, mask;
|
||||||
|
|
||||||
|
switch (runtime->channels) {
|
||||||
|
case 2: /* Multi channel is not needed for Stereo */
|
||||||
|
return 0;
|
||||||
|
case 6:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(dev, "unsupported channel\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mask = 0;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(types); i++) {
|
||||||
|
mod = rsnd_io_to_mod(io, types[i]);
|
||||||
|
if (!mod)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
mask |= 1 << rsnd_mod_id(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
|
static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
|
||||||
struct rsnd_dai_stream *io)
|
struct rsnd_dai_stream *io)
|
||||||
{
|
{
|
||||||
@ -194,6 +230,9 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
|
|||||||
if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
|
if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (rsnd_ssi_is_multi_slave(mod, io))
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (ssi->usrcnt > 1) {
|
if (ssi->usrcnt > 1) {
|
||||||
if (ssi->rate != rate) {
|
if (ssi->rate != rate) {
|
||||||
dev_err(dev, "SSI parent/child should use same rate\n");
|
dev_err(dev, "SSI parent/child should use same rate\n");
|
||||||
@ -437,8 +476,14 @@ static int __rsnd_ssi_start(struct rsnd_mod *mod,
|
|||||||
|
|
||||||
cr = ssi->cr_own |
|
cr = ssi->cr_own |
|
||||||
ssi->cr_clk |
|
ssi->cr_clk |
|
||||||
ssi->cr_mode |
|
ssi->cr_mode;
|
||||||
EN;
|
|
||||||
|
/*
|
||||||
|
* EN will be set via SSIU :: SSI_CONTROL
|
||||||
|
* if Multi channel mode
|
||||||
|
*/
|
||||||
|
if (!rsnd_ssi_multi_slaves(io))
|
||||||
|
cr |= EN;
|
||||||
|
|
||||||
rsnd_mod_write(mod, SSICR, cr);
|
rsnd_mod_write(mod, SSICR, cr);
|
||||||
rsnd_mod_write(mod, SSIWSR, ssi->wsr);
|
rsnd_mod_write(mod, SSIWSR, ssi->wsr);
|
||||||
@ -609,6 +654,13 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
|
|||||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SSIP/SSIU/IRQ are not needed on
|
||||||
|
* SSI Multi slaves
|
||||||
|
*/
|
||||||
|
if (rsnd_ssi_is_multi_slave(mod, io))
|
||||||
|
return 0;
|
||||||
|
|
||||||
rsnd_ssi_parent_attach(mod, io, priv);
|
rsnd_ssi_parent_attach(mod, io, priv);
|
||||||
|
|
||||||
ret = rsnd_ssiu_attach(io, mod);
|
ret = rsnd_ssiu_attach(io, mod);
|
||||||
@ -641,6 +693,13 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
|
|||||||
int dma_id = 0; /* not needed */
|
int dma_id = 0; /* not needed */
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SSIP/SSIU/IRQ/DMA are not needed on
|
||||||
|
* SSI Multi slaves
|
||||||
|
*/
|
||||||
|
if (rsnd_ssi_is_multi_slave(mod, io))
|
||||||
|
return 0;
|
||||||
|
|
||||||
ret = rsnd_ssi_common_probe(mod, io, priv);
|
ret = rsnd_ssi_common_probe(mod, io, priv);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@ -732,6 +791,57 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = {
|
|||||||
/*
|
/*
|
||||||
* ssi mod function
|
* ssi mod function
|
||||||
*/
|
*/
|
||||||
|
static void rsnd_ssi_connect(struct rsnd_mod *mod,
|
||||||
|
struct rsnd_dai_stream *io)
|
||||||
|
{
|
||||||
|
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
||||||
|
enum rsnd_mod_type types[] = {
|
||||||
|
RSND_MOD_SSI,
|
||||||
|
RSND_MOD_SSIM1,
|
||||||
|
RSND_MOD_SSIM2,
|
||||||
|
RSND_MOD_SSIM3,
|
||||||
|
};
|
||||||
|
enum rsnd_mod_type type;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* try SSI -> SSIM1 -> SSIM2 -> SSIM3 */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(types); i++) {
|
||||||
|
type = types[i];
|
||||||
|
if (!rsnd_io_to_mod(io, type)) {
|
||||||
|
rsnd_dai_connect(mod, io, type);
|
||||||
|
rsnd_set_slot(rdai, 2 * (i + 1), (i + 1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
|
||||||
|
struct device_node *playback,
|
||||||
|
struct device_node *capture)
|
||||||
|
{
|
||||||
|
struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
|
||||||
|
struct device_node *node;
|
||||||
|
struct device_node *np;
|
||||||
|
struct rsnd_mod *mod;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
node = rsnd_ssi_of_node(priv);
|
||||||
|
if (!node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
for_each_child_of_node(node, np) {
|
||||||
|
mod = rsnd_ssi_mod_get(priv, i);
|
||||||
|
if (np == playback)
|
||||||
|
rsnd_ssi_connect(mod, &rdai->playback);
|
||||||
|
if (np == capture)
|
||||||
|
rsnd_ssi_connect(mod, &rdai->capture);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
of_node_put(node);
|
||||||
|
}
|
||||||
|
|
||||||
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
|
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
|
||||||
{
|
{
|
||||||
if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
|
if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
|
||||||
|
@ -27,8 +27,11 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
|
|||||||
struct rsnd_priv *priv)
|
struct rsnd_priv *priv)
|
||||||
{
|
{
|
||||||
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
||||||
|
u32 multi_ssi_slaves = rsnd_ssi_multi_slaves(io);
|
||||||
int use_busif = rsnd_ssi_use_busif(io);
|
int use_busif = rsnd_ssi_use_busif(io);
|
||||||
int id = rsnd_mod_id(mod);
|
int id = rsnd_mod_id(mod);
|
||||||
|
u32 mask1, val1;
|
||||||
|
u32 mask2, val2;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SSI_MODE0
|
* SSI_MODE0
|
||||||
@ -38,6 +41,9 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
|
|||||||
/*
|
/*
|
||||||
* SSI_MODE1
|
* SSI_MODE1
|
||||||
*/
|
*/
|
||||||
|
mask1 = (1 << 4) | (1 << 20); /* mask sync bit */
|
||||||
|
mask2 = (1 << 4); /* mask sync bit */
|
||||||
|
val1 = val2 = 0;
|
||||||
if (rsnd_ssi_is_pin_sharing(io)) {
|
if (rsnd_ssi_is_pin_sharing(io)) {
|
||||||
int shift = -1;
|
int shift = -1;
|
||||||
|
|
||||||
@ -51,15 +57,36 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
|
|||||||
case 4:
|
case 4:
|
||||||
shift = 16;
|
shift = 16;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shift >= 0)
|
mask1 |= 0x3 << shift;
|
||||||
rsnd_mod_bset(mod, SSI_MODE1,
|
val1 = rsnd_rdai_is_clk_master(rdai) ?
|
||||||
0x3 << shift,
|
0x2 << shift : 0x1 << shift;
|
||||||
rsnd_rdai_is_clk_master(rdai) ?
|
|
||||||
0x2 << shift : 0x1 << shift);
|
} else if (multi_ssi_slaves) {
|
||||||
|
|
||||||
|
mask2 |= 0x00000007;
|
||||||
|
mask1 |= 0x0000000f;
|
||||||
|
|
||||||
|
switch (multi_ssi_slaves) {
|
||||||
|
case 0x0206: /* SSI0/1/2/9 */
|
||||||
|
val2 = (1 << 4) | /* SSI0129 sync */
|
||||||
|
rsnd_rdai_is_clk_master(rdai) ? 0x2 : 0x1;
|
||||||
|
/* fall through */
|
||||||
|
case 0x0006: /* SSI0/1/2 */
|
||||||
|
val1 = rsnd_rdai_is_clk_master(rdai) ?
|
||||||
|
0xa : 0x5;
|
||||||
|
|
||||||
|
if (!val2) /* SSI012 sync */
|
||||||
|
val1 |= (1 << 4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rsnd_mod_bset(mod, SSI_MODE1, mask1, val1);
|
||||||
|
rsnd_mod_bset(mod, SSI_MODE2, mask2, val2);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,8 +131,13 @@ static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
|
|||||||
struct rsnd_dai_stream *io,
|
struct rsnd_dai_stream *io,
|
||||||
struct rsnd_priv *priv)
|
struct rsnd_priv *priv)
|
||||||
{
|
{
|
||||||
if (rsnd_ssi_use_busif(io))
|
if (!rsnd_ssi_use_busif(io))
|
||||||
rsnd_mod_write(mod, SSI_CTRL, 0x1);
|
return 0;
|
||||||
|
|
||||||
|
rsnd_mod_write(mod, SSI_CTRL, 0x1);
|
||||||
|
|
||||||
|
if (rsnd_ssi_multi_slaves(io))
|
||||||
|
rsnd_mod_write(mod, SSI_CONTROL, 0x1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -114,8 +146,13 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
|
|||||||
struct rsnd_dai_stream *io,
|
struct rsnd_dai_stream *io,
|
||||||
struct rsnd_priv *priv)
|
struct rsnd_priv *priv)
|
||||||
{
|
{
|
||||||
if (rsnd_ssi_use_busif(io))
|
if (!rsnd_ssi_use_busif(io))
|
||||||
rsnd_mod_write(mod, SSI_CTRL, 0);
|
return 0;
|
||||||
|
|
||||||
|
rsnd_mod_write(mod, SSI_CTRL, 0);
|
||||||
|
|
||||||
|
if (rsnd_ssi_multi_slaves(io))
|
||||||
|
rsnd_mod_write(mod, SSI_CONTROL, 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user