mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-17 09:14:19 +08:00
ASoC: rsnd: SSI supports DMA transfer via BUSIF
This patch adds BUSIF support for R-Car sound DMAEngine transfer. The sound data will be transferred via FIFO which can cover blank time which will happen when DMA channel is switching. Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Signed-off-by: Mark Brown <broonie@linaro.org>
This commit is contained in:
parent
849fc82a6f
commit
374a528111
@ -36,6 +36,7 @@
|
||||
#define RSND_SSI_CLK_PIN_SHARE (1 << 31)
|
||||
#define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */
|
||||
#define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */
|
||||
#define RSND_SSI_DEPENDENT (1 << 28) /* SSI needs SRU/SCU */
|
||||
|
||||
#define RSND_SSI_PLAY (1 << 24)
|
||||
|
||||
@ -51,6 +52,11 @@ struct rsnd_ssi_platform_info {
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
/*
|
||||
* flags
|
||||
*/
|
||||
#define RSND_SCU_USB_HPBIF (1 << 31) /* it needs RSND_SSI_DEPENDENT */
|
||||
|
||||
struct rsnd_scu_platform_info {
|
||||
u32 flags;
|
||||
};
|
||||
|
@ -34,9 +34,6 @@ struct rsnd_gen {
|
||||
|
||||
#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen)
|
||||
|
||||
#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1)
|
||||
#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2)
|
||||
|
||||
/*
|
||||
* Gen2
|
||||
* will be filled in the future
|
||||
@ -115,8 +112,15 @@ static struct rsnd_gen_ops rsnd_gen1_ops = {
|
||||
|
||||
static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen)
|
||||
{
|
||||
RSND_GEN1_REG_MAP(gen, SRU, SRC_ROUTE_SEL, 0x0, 0x00);
|
||||
RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL0, 0x0, 0x08);
|
||||
RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL1, 0x0, 0x0c);
|
||||
RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL2, 0x0, 0x10);
|
||||
RSND_GEN1_REG_MAP(gen, SRU, SRC_CTRL, 0x0, 0xc0);
|
||||
RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0);
|
||||
RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4);
|
||||
RSND_GEN1_REG_MAP(gen, SRU, BUSIF_MODE, 0x4, 0x20);
|
||||
RSND_GEN1_REG_MAP(gen, SRU, BUSIF_ADINR, 0x40, 0x214);
|
||||
|
||||
RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00);
|
||||
RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04);
|
||||
|
@ -32,8 +32,15 @@
|
||||
*/
|
||||
enum rsnd_reg {
|
||||
/* SRU/SCU */
|
||||
RSND_REG_SRC_ROUTE_SEL,
|
||||
RSND_REG_SRC_TMG_SEL0,
|
||||
RSND_REG_SRC_TMG_SEL1,
|
||||
RSND_REG_SRC_TMG_SEL2,
|
||||
RSND_REG_SRC_CTRL,
|
||||
RSND_REG_SSI_MODE0,
|
||||
RSND_REG_SSI_MODE1,
|
||||
RSND_REG_BUSIF_MODE,
|
||||
RSND_REG_BUSIF_ADINR,
|
||||
|
||||
/* ADG */
|
||||
RSND_REG_BRRA,
|
||||
@ -213,6 +220,8 @@ int rsnd_gen_path_exit(struct rsnd_priv *priv,
|
||||
void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
|
||||
struct rsnd_mod *mod,
|
||||
enum rsnd_reg reg);
|
||||
#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1)
|
||||
#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2)
|
||||
|
||||
/*
|
||||
* R-Car ADG
|
||||
|
@ -15,6 +15,18 @@ struct rsnd_scu {
|
||||
struct rsnd_mod mod;
|
||||
};
|
||||
|
||||
#define rsnd_scu_mode_flags(p) ((p)->info->flags)
|
||||
|
||||
/*
|
||||
* ADINR
|
||||
*/
|
||||
#define OTBL_24 (0 << 16)
|
||||
#define OTBL_22 (2 << 16)
|
||||
#define OTBL_20 (4 << 16)
|
||||
#define OTBL_18 (6 << 16)
|
||||
#define OTBL_16 (8 << 16)
|
||||
|
||||
|
||||
#define rsnd_mod_to_scu(_mod) \
|
||||
container_of((_mod), struct rsnd_scu, mod)
|
||||
|
||||
@ -24,6 +36,116 @@ struct rsnd_scu {
|
||||
((pos) = (struct rsnd_scu *)(priv)->scu + i); \
|
||||
i++)
|
||||
|
||||
static int rsnd_scu_set_route(struct rsnd_priv *priv,
|
||||
struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai,
|
||||
struct rsnd_dai_stream *io)
|
||||
{
|
||||
struct scu_route_config {
|
||||
u32 mask;
|
||||
int shift;
|
||||
} routes[] = {
|
||||
{ 0xF, 0, }, /* 0 */
|
||||
{ 0xF, 4, }, /* 1 */
|
||||
{ 0xF, 8, }, /* 2 */
|
||||
{ 0x7, 12, }, /* 3 */
|
||||
{ 0x7, 16, }, /* 4 */
|
||||
{ 0x7, 20, }, /* 5 */
|
||||
{ 0x7, 24, }, /* 6 */
|
||||
{ 0x3, 28, }, /* 7 */
|
||||
{ 0x3, 30, }, /* 8 */
|
||||
};
|
||||
|
||||
u32 mask;
|
||||
u32 val;
|
||||
int shift;
|
||||
int id;
|
||||
|
||||
/*
|
||||
* Gen1 only
|
||||
*/
|
||||
if (!rsnd_is_gen1(priv))
|
||||
return 0;
|
||||
|
||||
id = rsnd_mod_id(mod);
|
||||
if (id < 0 || id > ARRAY_SIZE(routes))
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* SRC_ROUTE_SELECT
|
||||
*/
|
||||
val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2;
|
||||
val = val << routes[id].shift;
|
||||
mask = routes[id].mask << routes[id].shift;
|
||||
|
||||
rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val);
|
||||
|
||||
/*
|
||||
* SRC_TIMING_SELECT
|
||||
*/
|
||||
shift = (id % 4) * 8;
|
||||
mask = 0x1F << shift;
|
||||
if (8 == id) /* SRU8 is very special */
|
||||
val = id << shift;
|
||||
else
|
||||
val = (id + 1) << shift;
|
||||
|
||||
switch (id / 4) {
|
||||
case 0:
|
||||
rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val);
|
||||
break;
|
||||
case 1:
|
||||
rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val);
|
||||
break;
|
||||
case 2:
|
||||
rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_scu_set_mode(struct rsnd_priv *priv,
|
||||
struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai,
|
||||
struct rsnd_dai_stream *io)
|
||||
{
|
||||
int id = rsnd_mod_id(mod);
|
||||
u32 val;
|
||||
|
||||
if (rsnd_is_gen1(priv)) {
|
||||
val = (1 << id);
|
||||
rsnd_mod_bset(mod, SRC_CTRL, val, val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_scu_set_hpbif(struct rsnd_priv *priv,
|
||||
struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai,
|
||||
struct rsnd_dai_stream *io)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
||||
u32 adinr = runtime->channels;
|
||||
|
||||
switch (runtime->sample_bits) {
|
||||
case 16:
|
||||
adinr |= OTBL_16;
|
||||
break;
|
||||
case 32:
|
||||
adinr |= OTBL_24;
|
||||
break;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rsnd_mod_write(mod, BUSIF_MODE, 1);
|
||||
rsnd_mod_write(mod, BUSIF_ADINR, adinr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_scu_init(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai,
|
||||
struct rsnd_dai_stream *io)
|
||||
@ -53,9 +175,36 @@ static int rsnd_scu_start(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
u32 flags = rsnd_scu_mode_flags(scu);
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
/*
|
||||
* SCU will be used if it has RSND_SCU_USB_HPBIF flags
|
||||
*/
|
||||
if (!(flags & RSND_SCU_USB_HPBIF)) {
|
||||
/* it use PIO transter */
|
||||
dev_dbg(dev, "%s%d is not used\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* it use DMA transter */
|
||||
ret = rsnd_scu_set_route(priv, mod, rdai, io);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rsnd_scu_set_mode(priv, mod, rdai, io);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rsnd_scu_set_hpbif(priv, mod, rdai, io);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(dev, "%s%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -112,8 +261,9 @@ int rsnd_scu_probe(struct platform_device *pdev,
|
||||
rsnd_mod_init(priv, &scu->mod,
|
||||
&rsnd_scu_ops, i);
|
||||
scu->info = &info->scu_info[i];
|
||||
}
|
||||
|
||||
dev_dbg(dev, "SCU%d probed\n", i);
|
||||
}
|
||||
dev_dbg(dev, "scu probed\n");
|
||||
|
||||
return 0;
|
||||
|
@ -104,6 +104,7 @@ struct rsnd_ssiu {
|
||||
static void rsnd_ssi_mode_init(struct rsnd_priv *priv,
|
||||
struct rsnd_ssiu *ssiu)
|
||||
{
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct rsnd_ssi *ssi;
|
||||
u32 flags;
|
||||
u32 val;
|
||||
@ -113,8 +114,17 @@ static void rsnd_ssi_mode_init(struct rsnd_priv *priv,
|
||||
* SSI_MODE0
|
||||
*/
|
||||
ssiu->ssi_mode0 = 0;
|
||||
for_each_rsnd_ssi(ssi, priv, i)
|
||||
ssiu->ssi_mode0 |= (1 << i);
|
||||
for_each_rsnd_ssi(ssi, priv, i) {
|
||||
flags = rsnd_ssi_mode_flags(ssi);
|
||||
|
||||
/* see also BUSIF_MODE */
|
||||
if (!(flags & RSND_SSI_DEPENDENT)) {
|
||||
ssiu->ssi_mode0 |= (1 << i);
|
||||
dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", i);
|
||||
} else {
|
||||
dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SSI_MODE1
|
||||
@ -670,6 +680,8 @@ int rsnd_ssi_probe(struct platform_device *pdev,
|
||||
dev_info(dev, "SSI DMA failed. try PIO transter\n");
|
||||
else
|
||||
ops = &rsnd_ssi_dma_ops;
|
||||
|
||||
dev_dbg(dev, "SSI%d use DMA transfer\n", i);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -687,6 +699,8 @@ int rsnd_ssi_probe(struct platform_device *pdev,
|
||||
}
|
||||
|
||||
ops = &rsnd_ssi_pio_ops;
|
||||
|
||||
dev_dbg(dev, "SSI%d use PIO transfer\n", i);
|
||||
}
|
||||
|
||||
rsnd_mod_init(priv, &ssi->mod, ops, i);
|
||||
|
Loading…
Reference in New Issue
Block a user