Merge remote-tracking branch 'asoc/topic/rcar' into asoc-next
This commit is contained in:
Коммит
43b64af535
|
@ -1,5 +1,5 @@
|
|||
menu "SoC Audio support for SuperH"
|
||||
depends on SUPERH || ARCH_SHMOBILE
|
||||
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||
|
||||
config SND_SOC_PCM_SH7760
|
||||
tristate "SoC Audio support for Renesas SH7760"
|
||||
|
@ -37,6 +37,7 @@ config SND_SOC_SH4_SIU
|
|||
config SND_SOC_RCAR
|
||||
tristate "R-Car series SRU/SCU/SSIU/SSI support"
|
||||
depends on COMMON_CLK
|
||||
depends on OF || COMPILE_TEST
|
||||
select SND_SIMPLE_CARD
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
|
|
|
@ -34,6 +34,9 @@ struct rsnd_adg {
|
|||
struct clk_onecell_data onecell;
|
||||
struct rsnd_mod mod;
|
||||
u32 flags;
|
||||
u32 ckr;
|
||||
u32 rbga;
|
||||
u32 rbgb;
|
||||
|
||||
int rbga_rate_for_441khz; /* RBGA */
|
||||
int rbgb_rate_for_48khz; /* RBGB */
|
||||
|
@ -316,9 +319,11 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
|
|||
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
|
||||
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
|
||||
struct clk *clk;
|
||||
int i;
|
||||
u32 data;
|
||||
u32 ckr = 0;
|
||||
int sel_table[] = {
|
||||
[CLKA] = 0x1,
|
||||
[CLKB] = 0x2,
|
||||
|
@ -360,15 +365,14 @@ found_clock:
|
|||
rsnd_adg_set_ssi_clk(ssi_mod, data);
|
||||
|
||||
if (!(adg_mode_flags(adg) & LRCLK_ASYNC)) {
|
||||
struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
|
||||
u32 ckr = 0;
|
||||
|
||||
if (0 == (rate % 8000))
|
||||
ckr = 0x80000000;
|
||||
|
||||
rsnd_mod_bset(adg_mod, SSICKR, 0x80000000, ckr);
|
||||
}
|
||||
|
||||
rsnd_mod_bset(adg_mod, BRGCKR, 0x80FF0000, adg->ckr | ckr);
|
||||
rsnd_mod_write(adg_mod, BRRA, adg->rbga);
|
||||
rsnd_mod_write(adg_mod, BRRB, adg->rbgb);
|
||||
|
||||
dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n",
|
||||
rsnd_mod_name(ssi_mod), rsnd_mod_id(ssi_mod),
|
||||
data, rate);
|
||||
|
@ -376,6 +380,25 @@ found_clock:
|
|||
return 0;
|
||||
}
|
||||
|
||||
void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
|
||||
{
|
||||
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct clk *clk;
|
||||
int i, ret;
|
||||
|
||||
for_each_rsnd_clk(clk, adg, i) {
|
||||
ret = 0;
|
||||
if (enable)
|
||||
ret = clk_prepare_enable(clk);
|
||||
else
|
||||
clk_disable_unprepare(clk);
|
||||
|
||||
if (ret < 0)
|
||||
dev_warn(dev, "can't use clk %d\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
|
||||
struct rsnd_adg *adg)
|
||||
{
|
||||
|
@ -387,27 +410,21 @@ static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
|
|||
[CLKC] = "clk_c",
|
||||
[CLKI] = "clk_i",
|
||||
};
|
||||
int i, ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CLKMAX; i++) {
|
||||
clk = devm_clk_get(dev, clk_name[i]);
|
||||
adg->clk[i] = IS_ERR(clk) ? NULL : clk;
|
||||
}
|
||||
|
||||
for_each_rsnd_clk(clk, adg, i) {
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret < 0)
|
||||
dev_warn(dev, "can't use clk %d\n", i);
|
||||
|
||||
for_each_rsnd_clk(clk, adg, i)
|
||||
dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
|
||||
}
|
||||
}
|
||||
|
||||
static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
|
||||
struct rsnd_adg *adg)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 ckr, rbgx, rbga, rbgb;
|
||||
|
@ -532,13 +549,13 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
|
|||
}
|
||||
}
|
||||
|
||||
rsnd_mod_bset(adg_mod, SSICKR, 0x80FF0000, ckr);
|
||||
rsnd_mod_write(adg_mod, BRRA, rbga);
|
||||
rsnd_mod_write(adg_mod, BRRB, rbgb);
|
||||
adg->ckr = ckr;
|
||||
adg->rbga = rbga;
|
||||
adg->rbgb = 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, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
|
||||
ckr, rbga, rbgb);
|
||||
}
|
||||
|
||||
|
@ -565,16 +582,12 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
|
|||
|
||||
priv->adg = adg;
|
||||
|
||||
rsnd_adg_clk_enable(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rsnd_adg_remove(struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for_each_rsnd_clk(clk, adg, i) {
|
||||
clk_disable_unprepare(clk);
|
||||
}
|
||||
rsnd_adg_clk_disable(priv);
|
||||
}
|
||||
|
|
|
@ -306,7 +306,7 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
|
|||
*/
|
||||
u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
|
||||
{
|
||||
struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
|
||||
struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io);
|
||||
struct rsnd_mod *target;
|
||||
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
||||
u32 val = 0x76543210;
|
||||
|
@ -315,11 +315,11 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
|
|||
if (rsnd_io_is_play(io)) {
|
||||
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
|
||||
|
||||
target = src ? src : ssi;
|
||||
target = src ? src : ssiu;
|
||||
} else {
|
||||
struct rsnd_mod *cmd = rsnd_io_to_mod_cmd(io);
|
||||
|
||||
target = cmd ? cmd : ssi;
|
||||
target = cmd ? cmd : ssiu;
|
||||
}
|
||||
|
||||
mask <<= runtime->channels * 4;
|
||||
|
@ -348,32 +348,28 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
|
|||
/*
|
||||
* rsnd_dai functions
|
||||
*/
|
||||
#define rsnd_mod_call(idx, io, func, param...) \
|
||||
({ \
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \
|
||||
struct rsnd_mod *mod = (io)->mod[idx]; \
|
||||
struct device *dev = rsnd_priv_to_dev(priv); \
|
||||
u32 *status = mod->get_status(io, mod, idx); \
|
||||
u32 mask = 0xF << __rsnd_mod_shift_##func; \
|
||||
u8 val = (*status >> __rsnd_mod_shift_##func) & 0xF; \
|
||||
u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \
|
||||
int ret = 0; \
|
||||
int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \
|
||||
if (add == 0xF) \
|
||||
call = 0; \
|
||||
else \
|
||||
*status = (*status & ~mask) + \
|
||||
(add << __rsnd_mod_shift_##func); \
|
||||
dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod), \
|
||||
*status, call ? #func : ""); \
|
||||
if (call) \
|
||||
ret = (mod)->ops->func(mod, io, param); \
|
||||
if (ret) \
|
||||
dev_dbg(dev, "%s[%d] : rsnd_mod_call error %d\n", \
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod), ret); \
|
||||
ret; \
|
||||
})
|
||||
struct rsnd_mod *rsnd_mod_next(int *iterator,
|
||||
struct rsnd_dai_stream *io,
|
||||
enum rsnd_mod_type *array,
|
||||
int array_size)
|
||||
{
|
||||
struct rsnd_mod *mod;
|
||||
enum rsnd_mod_type type;
|
||||
int max = array ? array_size : RSND_MOD_MAX;
|
||||
|
||||
for (; *iterator < max; (*iterator)++) {
|
||||
type = (array) ? array[*iterator] : *iterator;
|
||||
mod = io->mod[type];
|
||||
if (!mod)
|
||||
continue;
|
||||
|
||||
(*iterator)++;
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = {
|
||||
{
|
||||
|
@ -409,19 +405,49 @@ static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = {
|
|||
},
|
||||
};
|
||||
|
||||
#define rsnd_dai_call(fn, io, param...) \
|
||||
({ \
|
||||
struct rsnd_mod *mod; \
|
||||
int type, is_play = rsnd_io_is_play(io); \
|
||||
int ret = 0, i; \
|
||||
for (i = 0; i < RSND_MOD_MAX; i++) { \
|
||||
type = rsnd_mod_sequence[is_play][i]; \
|
||||
mod = (io)->mod[type]; \
|
||||
if (!mod) \
|
||||
continue; \
|
||||
ret |= rsnd_mod_call(type, io, fn, param); \
|
||||
} \
|
||||
ret; \
|
||||
static int rsnd_status_update(u32 *status,
|
||||
int shift, int add, int timing)
|
||||
{
|
||||
u32 mask = 0xF << shift;
|
||||
u8 val = (*status >> shift) & 0xF;
|
||||
u8 next_val = (val + add) & 0xF;
|
||||
int func_call = (val == timing);
|
||||
|
||||
if (next_val == 0xF) /* underflow case */
|
||||
func_call = 0;
|
||||
else
|
||||
*status = (*status & ~mask) + (next_val << shift);
|
||||
|
||||
return func_call;
|
||||
}
|
||||
|
||||
#define rsnd_dai_call(fn, io, param...) \
|
||||
({ \
|
||||
struct rsnd_priv *priv = rsnd_io_to_priv(io); \
|
||||
struct device *dev = rsnd_priv_to_dev(priv); \
|
||||
struct rsnd_mod *mod; \
|
||||
int is_play = rsnd_io_is_play(io); \
|
||||
int ret = 0, i; \
|
||||
enum rsnd_mod_type *types = rsnd_mod_sequence[is_play]; \
|
||||
for_each_rsnd_mod_arrays(i, mod, io, types, RSND_MOD_MAX) { \
|
||||
int tmp = 0; \
|
||||
u32 *status = mod->get_status(io, mod, types[i]); \
|
||||
int func_call = rsnd_status_update(status, \
|
||||
__rsnd_mod_shift_##fn, \
|
||||
__rsnd_mod_add_##fn, \
|
||||
__rsnd_mod_call_##fn); \
|
||||
dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod), *status, \
|
||||
(func_call && (mod)->ops->fn) ? #fn : ""); \
|
||||
if (func_call && (mod)->ops->fn) \
|
||||
tmp = (mod)->ops->fn(mod, io, param); \
|
||||
if (tmp) \
|
||||
dev_err(dev, "%s[%d] : %s error %d\n", \
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod), \
|
||||
#fn, tmp); \
|
||||
ret |= tmp; \
|
||||
} \
|
||||
ret; \
|
||||
})
|
||||
|
||||
int rsnd_dai_connect(struct rsnd_mod *mod,
|
||||
|
@ -690,7 +716,33 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
|
||||
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
|
||||
|
||||
/*
|
||||
* call rsnd_dai_call without spinlock
|
||||
*/
|
||||
return rsnd_dai_call(nolock_start, io, priv);
|
||||
}
|
||||
|
||||
static void rsnd_soc_dai_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
|
||||
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
|
||||
|
||||
/*
|
||||
* call rsnd_dai_call without spinlock
|
||||
*/
|
||||
rsnd_dai_call(nolock_stop, io, priv);
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -993,7 +1045,11 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
|
|||
|
||||
void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg)
|
||||
{
|
||||
snd_ctl_remove(cfg->card, cfg->kctrl);
|
||||
if (cfg->card && cfg->kctrl)
|
||||
snd_ctl_remove(cfg->card, cfg->kctrl);
|
||||
|
||||
cfg->card = NULL;
|
||||
cfg->kctrl = NULL;
|
||||
}
|
||||
|
||||
int rsnd_kctrl_new_m(struct rsnd_mod *mod,
|
||||
|
@ -1070,8 +1126,8 @@ static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
|||
|
||||
return snd_pcm_lib_preallocate_pages_for_all(
|
||||
rtd->pcm,
|
||||
SNDRV_DMA_TYPE_DEV,
|
||||
rtd->card->snd_card->dev,
|
||||
SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
snd_dma_continuous_data(GFP_KERNEL),
|
||||
PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
|
||||
}
|
||||
|
||||
|
@ -1092,6 +1148,7 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
|
|||
ret = rsnd_dai_call(probe, io, priv);
|
||||
if (ret == -EAGAIN) {
|
||||
struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
|
||||
struct rsnd_mod *mod;
|
||||
int i;
|
||||
|
||||
/*
|
||||
|
@ -1111,8 +1168,8 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
|
|||
* remove all mod from io
|
||||
* and, re connect ssi
|
||||
*/
|
||||
for (i = 0; i < RSND_MOD_MAX; i++)
|
||||
rsnd_dai_disconnect((io)->mod[i], io, i);
|
||||
for_each_rsnd_mod(i, mod, io)
|
||||
rsnd_dai_disconnect(mod, io, i);
|
||||
rsnd_dai_connect(ssi_mod, io, RSND_MOD_SSI);
|
||||
|
||||
/*
|
||||
|
@ -1251,9 +1308,33 @@ static int rsnd_remove(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int rsnd_suspend(struct device *dev)
|
||||
{
|
||||
struct rsnd_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
rsnd_adg_clk_disable(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_resume(struct device *dev)
|
||||
{
|
||||
struct rsnd_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
rsnd_adg_clk_enable(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dev_pm_ops rsnd_pm_ops = {
|
||||
.suspend = rsnd_suspend,
|
||||
.resume = rsnd_resume,
|
||||
};
|
||||
|
||||
static struct platform_driver rsnd_driver = {
|
||||
.driver = {
|
||||
.name = "rcar_sound",
|
||||
.pm = &rsnd_pm_ops,
|
||||
.of_match_table = rsnd_of_match,
|
||||
},
|
||||
.probe = rsnd_probe,
|
||||
|
|
|
@ -25,6 +25,10 @@
|
|||
|
||||
struct rsnd_dmaen {
|
||||
struct dma_chan *chan;
|
||||
dma_addr_t dma_buf;
|
||||
unsigned int dma_len;
|
||||
unsigned int dma_period;
|
||||
unsigned int dma_cnt;
|
||||
};
|
||||
|
||||
struct rsnd_dmapp {
|
||||
|
@ -34,6 +38,8 @@ struct rsnd_dmapp {
|
|||
|
||||
struct rsnd_dma {
|
||||
struct rsnd_mod mod;
|
||||
struct rsnd_mod *mod_from;
|
||||
struct rsnd_mod *mod_to;
|
||||
dma_addr_t src_addr;
|
||||
dma_addr_t dst_addr;
|
||||
union {
|
||||
|
@ -56,10 +62,38 @@ struct rsnd_dma_ctrl {
|
|||
/*
|
||||
* Audio DMAC
|
||||
*/
|
||||
#define rsnd_dmaen_sync(dmaen, io, i) __rsnd_dmaen_sync(dmaen, io, i, 1)
|
||||
#define rsnd_dmaen_unsync(dmaen, io, i) __rsnd_dmaen_sync(dmaen, io, i, 0)
|
||||
static void __rsnd_dmaen_sync(struct rsnd_dmaen *dmaen, struct rsnd_dai_stream *io,
|
||||
int i, int sync)
|
||||
{
|
||||
struct device *dev = dmaen->chan->device->dev;
|
||||
enum dma_data_direction dir;
|
||||
int is_play = rsnd_io_is_play(io);
|
||||
dma_addr_t buf;
|
||||
int len, max;
|
||||
size_t period;
|
||||
|
||||
len = dmaen->dma_len;
|
||||
period = dmaen->dma_period;
|
||||
max = len / period;
|
||||
i = i % max;
|
||||
buf = dmaen->dma_buf + (period * i);
|
||||
|
||||
dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
|
||||
|
||||
if (sync)
|
||||
dma_sync_single_for_device(dev, buf, period, dir);
|
||||
else
|
||||
dma_sync_single_for_cpu(dev, buf, period, dir);
|
||||
}
|
||||
|
||||
static void __rsnd_dmaen_complete(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
|
||||
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
|
||||
bool elapsed = false;
|
||||
unsigned long flags;
|
||||
|
||||
|
@ -76,9 +110,22 @@ static void __rsnd_dmaen_complete(struct rsnd_mod *mod,
|
|||
*/
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
if (rsnd_io_is_working(io))
|
||||
if (rsnd_io_is_working(io)) {
|
||||
rsnd_dmaen_unsync(dmaen, io, dmaen->dma_cnt);
|
||||
|
||||
/*
|
||||
* Next period is already started.
|
||||
* Let's sync Next Next period
|
||||
* see
|
||||
* rsnd_dmaen_start()
|
||||
*/
|
||||
rsnd_dmaen_sync(dmaen, io, dmaen->dma_cnt + 2);
|
||||
|
||||
elapsed = rsnd_dai_pointer_update(io, io->byte_per_period);
|
||||
|
||||
dmaen->dma_cnt++;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
if (elapsed)
|
||||
|
@ -92,75 +139,6 @@ static void rsnd_dmaen_complete(void *data)
|
|||
rsnd_mod_interrupt(mod, __rsnd_dmaen_complete);
|
||||
}
|
||||
|
||||
static int rsnd_dmaen_stop(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
|
||||
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
|
||||
|
||||
dmaengine_terminate_all(dmaen->chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_dmaen_start(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
|
||||
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
|
||||
struct snd_pcm_substream *substream = io->substream;
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
int is_play = rsnd_io_is_play(io);
|
||||
|
||||
desc = dmaengine_prep_dma_cyclic(dmaen->chan,
|
||||
substream->runtime->dma_addr,
|
||||
snd_pcm_lib_buffer_bytes(substream),
|
||||
snd_pcm_lib_period_bytes(substream),
|
||||
is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
|
||||
if (!desc) {
|
||||
dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
desc->callback = rsnd_dmaen_complete;
|
||||
desc->callback_param = rsnd_mod_get(dma);
|
||||
|
||||
if (dmaengine_submit(desc) < 0) {
|
||||
dev_err(dev, "dmaengine_submit() fail\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dma_async_issue_pending(dmaen->chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
|
||||
struct rsnd_mod *mod, char *name)
|
||||
{
|
||||
struct dma_chan *chan;
|
||||
struct device_node *np;
|
||||
int i = 0;
|
||||
|
||||
for_each_child_of_node(of_node, np) {
|
||||
if (i == rsnd_mod_id(mod))
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
chan = of_dma_request_slave_channel(np, name);
|
||||
|
||||
of_node_put(np);
|
||||
of_node_put(of_node);
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod_from,
|
||||
struct rsnd_mod *mod_to)
|
||||
|
@ -175,13 +153,37 @@ static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io,
|
|||
return rsnd_mod_dma_req(io, mod_to);
|
||||
}
|
||||
|
||||
static int rsnd_dmaen_remove(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
static int rsnd_dmaen_stop(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
|
||||
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
|
||||
|
||||
if (dmaen->chan) {
|
||||
int is_play = rsnd_io_is_play(io);
|
||||
|
||||
dmaengine_terminate_all(dmaen->chan);
|
||||
dma_unmap_single(dmaen->chan->device->dev,
|
||||
dmaen->dma_buf, dmaen->dma_len,
|
||||
is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_dmaen_nolock_stop(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
|
||||
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
|
||||
|
||||
/*
|
||||
* DMAEngine release uses mutex lock.
|
||||
* Thus, it shouldn't be called under spinlock.
|
||||
* Let's call it under nolock_start
|
||||
*/
|
||||
if (dmaen->chan)
|
||||
dma_release_channel(dmaen->chan);
|
||||
|
||||
|
@ -190,41 +192,55 @@ static int rsnd_dmaen_remove(struct rsnd_mod *mod,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_dmaen_attach(struct rsnd_dai_stream *io,
|
||||
struct rsnd_dma *dma, int id,
|
||||
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
|
||||
static int rsnd_dmaen_nolock_start(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_mod *mod = rsnd_mod_get(dma);
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
|
||||
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
|
||||
struct rsnd_priv *priv = rsnd_io_to_priv(io);
|
||||
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct dma_slave_config cfg = {};
|
||||
int is_play = rsnd_io_is_play(io);
|
||||
int ret;
|
||||
|
||||
if (dmaen->chan) {
|
||||
dev_err(dev, "it already has dma channel\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dev->of_node) {
|
||||
dmaen->chan = rsnd_dmaen_request_channel(io, mod_from, mod_to);
|
||||
} else {
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
dmaen->chan = dma_request_channel(mask, shdma_chan_filter,
|
||||
(void *)(uintptr_t)id);
|
||||
}
|
||||
/*
|
||||
* DMAEngine request uses mutex lock.
|
||||
* Thus, it shouldn't be called under spinlock.
|
||||
* Let's call it under nolock_start
|
||||
*/
|
||||
dmaen->chan = rsnd_dmaen_request_channel(io,
|
||||
dma->mod_from,
|
||||
dma->mod_to);
|
||||
if (IS_ERR_OR_NULL(dmaen->chan)) {
|
||||
int ret = PTR_ERR(dmaen->chan);
|
||||
|
||||
dmaen->chan = NULL;
|
||||
dev_err(dev, "can't get dma channel\n");
|
||||
goto rsnd_dma_channel_err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_dmaen_start(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
|
||||
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
|
||||
struct snd_pcm_substream *substream = io->substream;
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
struct dma_slave_config cfg = {};
|
||||
dma_addr_t buf;
|
||||
size_t len;
|
||||
size_t period;
|
||||
int is_play = rsnd_io_is_play(io);
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
|
||||
cfg.src_addr = dma->src_addr;
|
||||
cfg.dst_addr = dma->dst_addr;
|
||||
|
@ -237,30 +253,107 @@ static int rsnd_dmaen_attach(struct rsnd_dai_stream *io,
|
|||
|
||||
ret = dmaengine_slave_config(dmaen->chan, &cfg);
|
||||
if (ret < 0)
|
||||
goto rsnd_dma_attach_err;
|
||||
return ret;
|
||||
|
||||
len = snd_pcm_lib_buffer_bytes(substream);
|
||||
period = snd_pcm_lib_period_bytes(substream);
|
||||
buf = dma_map_single(dmaen->chan->device->dev,
|
||||
substream->runtime->dma_area,
|
||||
len,
|
||||
is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(dmaen->chan->device->dev, buf)) {
|
||||
dev_err(dev, "dma map failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
desc = dmaengine_prep_dma_cyclic(dmaen->chan,
|
||||
buf, len, period,
|
||||
is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
|
||||
if (!desc) {
|
||||
dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
desc->callback = rsnd_dmaen_complete;
|
||||
desc->callback_param = rsnd_mod_get(dma);
|
||||
|
||||
dmaen->dma_buf = buf;
|
||||
dmaen->dma_len = len;
|
||||
dmaen->dma_period = period;
|
||||
dmaen->dma_cnt = 0;
|
||||
|
||||
/*
|
||||
* synchronize this and next period
|
||||
* see
|
||||
* __rsnd_dmaen_complete()
|
||||
*/
|
||||
for (i = 0; i < 2; i++)
|
||||
rsnd_dmaen_sync(dmaen, io, i);
|
||||
|
||||
if (dmaengine_submit(desc) < 0) {
|
||||
dev_err(dev, "dmaengine_submit() fail\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dma_async_issue_pending(dmaen->chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
|
||||
struct rsnd_mod *mod, char *name)
|
||||
{
|
||||
struct dma_chan *chan = NULL;
|
||||
struct device_node *np;
|
||||
int i = 0;
|
||||
|
||||
for_each_child_of_node(of_node, np) {
|
||||
if (i == rsnd_mod_id(mod) && (!chan))
|
||||
chan = of_dma_request_slave_channel(np, name);
|
||||
i++;
|
||||
}
|
||||
|
||||
/* It should call of_node_put(), since, it is rsnd_xxx_of_node() */
|
||||
of_node_put(of_node);
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
static int rsnd_dmaen_attach(struct rsnd_dai_stream *io,
|
||||
struct rsnd_dma *dma,
|
||||
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_io_to_priv(io);
|
||||
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
|
||||
struct dma_chan *chan;
|
||||
|
||||
/* try to get DMAEngine channel */
|
||||
chan = rsnd_dmaen_request_channel(io, mod_from, mod_to);
|
||||
if (IS_ERR_OR_NULL(chan)) {
|
||||
/*
|
||||
* DMA failed. try to PIO mode
|
||||
* see
|
||||
* rsnd_ssi_fallback()
|
||||
* rsnd_rdai_continuance_probe()
|
||||
*/
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
dma_release_channel(chan);
|
||||
|
||||
dmac->dmaen_num++;
|
||||
|
||||
return 0;
|
||||
|
||||
rsnd_dma_attach_err:
|
||||
rsnd_dmaen_remove(mod, io, priv);
|
||||
rsnd_dma_channel_err:
|
||||
|
||||
/*
|
||||
* DMA failed. try to PIO mode
|
||||
* see
|
||||
* rsnd_ssi_fallback()
|
||||
* rsnd_rdai_continuance_probe()
|
||||
*/
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static struct rsnd_mod_ops rsnd_dmaen_ops = {
|
||||
.name = "audmac",
|
||||
.nolock_start = rsnd_dmaen_nolock_start,
|
||||
.nolock_stop = rsnd_dmaen_nolock_stop,
|
||||
.start = rsnd_dmaen_start,
|
||||
.stop = rsnd_dmaen_stop,
|
||||
.remove = rsnd_dmaen_remove,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -394,7 +487,7 @@ static int rsnd_dmapp_start(struct rsnd_mod *mod,
|
|||
}
|
||||
|
||||
static int rsnd_dmapp_attach(struct rsnd_dai_stream *io,
|
||||
struct rsnd_dma *dma, int id,
|
||||
struct rsnd_dma *dma,
|
||||
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
|
||||
{
|
||||
struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
|
||||
|
@ -627,7 +720,7 @@ static void rsnd_dma_of_path(struct rsnd_mod *this,
|
|||
}
|
||||
|
||||
int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
|
||||
struct rsnd_mod **dma_mod, int id)
|
||||
struct rsnd_mod **dma_mod)
|
||||
{
|
||||
struct rsnd_mod *mod_from = NULL;
|
||||
struct rsnd_mod *mod_to = NULL;
|
||||
|
@ -636,7 +729,7 @@ int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
|
|||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct rsnd_mod_ops *ops;
|
||||
enum rsnd_mod_type type;
|
||||
int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id,
|
||||
int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma,
|
||||
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
|
||||
int is_play = rsnd_io_is_play(io);
|
||||
int ret, dma_id;
|
||||
|
@ -682,9 +775,6 @@ int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
|
|||
|
||||
*dma_mod = rsnd_mod_get(dma);
|
||||
|
||||
dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
|
||||
dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
|
||||
|
||||
ret = rsnd_mod_init(priv, *dma_mod, ops, NULL,
|
||||
rsnd_mod_get_status, type, dma_id);
|
||||
if (ret < 0)
|
||||
|
@ -695,9 +785,14 @@ int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
|
|||
rsnd_mod_name(mod_from), rsnd_mod_id(mod_from),
|
||||
rsnd_mod_name(mod_to), rsnd_mod_id(mod_to));
|
||||
|
||||
ret = attach(io, dma, id, mod_from, mod_to);
|
||||
ret = attach(io, dma, mod_from, mod_to);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
|
||||
dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
|
||||
dma->mod_from = mod_from;
|
||||
dma->mod_to = mod_to;
|
||||
}
|
||||
|
||||
ret = rsnd_dai_connect(*dma_mod, io, type);
|
||||
|
|
|
@ -48,8 +48,6 @@ struct rsnd_dvc {
|
|||
|
||||
#define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id)
|
||||
#define rsnd_dvc_nr(priv) ((priv)->dvc_nr)
|
||||
#define rsnd_dvc_of_node(priv) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc")
|
||||
|
||||
#define rsnd_mod_to_dvc(_mod) \
|
||||
container_of((_mod), struct rsnd_dvc, mod)
|
||||
|
|
|
@ -211,6 +211,14 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
|
|||
RSND_GEN_S_REG(SSI_MODE1, 0x804),
|
||||
RSND_GEN_S_REG(SSI_MODE2, 0x808),
|
||||
RSND_GEN_S_REG(SSI_CONTROL, 0x810),
|
||||
RSND_GEN_S_REG(SSI_SYS_STATUS0, 0x840),
|
||||
RSND_GEN_S_REG(SSI_SYS_STATUS1, 0x844),
|
||||
RSND_GEN_S_REG(SSI_SYS_STATUS2, 0x848),
|
||||
RSND_GEN_S_REG(SSI_SYS_STATUS3, 0x84c),
|
||||
RSND_GEN_S_REG(SSI_SYS_STATUS4, 0x880),
|
||||
RSND_GEN_S_REG(SSI_SYS_STATUS5, 0x884),
|
||||
RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888),
|
||||
RSND_GEN_S_REG(SSI_SYS_STATUS7, 0x88c),
|
||||
|
||||
/* FIXME: it needs SSI_MODE2/3 in the future */
|
||||
RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80),
|
||||
|
@ -311,7 +319,7 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
|
|||
static const struct rsnd_regmap_field_conf conf_adg[] = {
|
||||
RSND_GEN_S_REG(BRRA, 0x00),
|
||||
RSND_GEN_S_REG(BRRB, 0x04),
|
||||
RSND_GEN_S_REG(SSICKR, 0x08),
|
||||
RSND_GEN_S_REG(BRGCKR, 0x08),
|
||||
RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c),
|
||||
RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10),
|
||||
RSND_GEN_S_REG(AUDIO_CLK_SEL2, 0x14),
|
||||
|
@ -362,7 +370,7 @@ static int rsnd_gen1_probe(struct rsnd_priv *priv)
|
|||
static const struct rsnd_regmap_field_conf conf_adg[] = {
|
||||
RSND_GEN_S_REG(BRRA, 0x00),
|
||||
RSND_GEN_S_REG(BRRB, 0x04),
|
||||
RSND_GEN_S_REG(SSICKR, 0x08),
|
||||
RSND_GEN_S_REG(BRGCKR, 0x08),
|
||||
RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c),
|
||||
RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10),
|
||||
};
|
||||
|
|
|
@ -43,17 +43,7 @@
|
|||
* see gen1/gen2 for detail
|
||||
*/
|
||||
enum rsnd_reg {
|
||||
/* SCU (SRC/SSIU/MIX/CTU/DVC) */
|
||||
RSND_REG_SSI_MODE, /* Gen2 only */
|
||||
RSND_REG_SSI_MODE0,
|
||||
RSND_REG_SSI_MODE1,
|
||||
RSND_REG_SSI_MODE2,
|
||||
RSND_REG_SSI_CONTROL,
|
||||
RSND_REG_SSI_CTRL, /* Gen2 only */
|
||||
RSND_REG_SSI_BUSIF_MODE, /* Gen2 only */
|
||||
RSND_REG_SSI_BUSIF_ADINR, /* Gen2 only */
|
||||
RSND_REG_SSI_BUSIF_DALIGN, /* Gen2 only */
|
||||
RSND_REG_SSI_INT_ENABLE, /* Gen2 only */
|
||||
/* SCU (MIX/CTU/DVC) */
|
||||
RSND_REG_SRC_I_BUSIF_MODE,
|
||||
RSND_REG_SRC_O_BUSIF_MODE,
|
||||
RSND_REG_SRC_ROUTE_MODE0,
|
||||
|
@ -63,29 +53,29 @@ enum rsnd_reg {
|
|||
RSND_REG_SRC_IFSCR,
|
||||
RSND_REG_SRC_IFSVR,
|
||||
RSND_REG_SRC_SRCCR,
|
||||
RSND_REG_SRC_CTRL, /* Gen2 only */
|
||||
RSND_REG_SRC_BSDSR, /* Gen2 only */
|
||||
RSND_REG_SRC_BSISR, /* Gen2 only */
|
||||
RSND_REG_SRC_INT_ENABLE0, /* Gen2 only */
|
||||
RSND_REG_SRC_BUSIF_DALIGN, /* Gen2 only */
|
||||
RSND_REG_SRCIN_TIMSEL0, /* Gen2 only */
|
||||
RSND_REG_SRCIN_TIMSEL1, /* Gen2 only */
|
||||
RSND_REG_SRCIN_TIMSEL2, /* Gen2 only */
|
||||
RSND_REG_SRCIN_TIMSEL3, /* Gen2 only */
|
||||
RSND_REG_SRCIN_TIMSEL4, /* Gen2 only */
|
||||
RSND_REG_SRCOUT_TIMSEL0, /* Gen2 only */
|
||||
RSND_REG_SRCOUT_TIMSEL1, /* Gen2 only */
|
||||
RSND_REG_SRCOUT_TIMSEL2, /* Gen2 only */
|
||||
RSND_REG_SRCOUT_TIMSEL3, /* Gen2 only */
|
||||
RSND_REG_SRCOUT_TIMSEL4, /* Gen2 only */
|
||||
RSND_REG_SRC_CTRL,
|
||||
RSND_REG_SRC_BSDSR,
|
||||
RSND_REG_SRC_BSISR,
|
||||
RSND_REG_SRC_INT_ENABLE0,
|
||||
RSND_REG_SRC_BUSIF_DALIGN,
|
||||
RSND_REG_SRCIN_TIMSEL0,
|
||||
RSND_REG_SRCIN_TIMSEL1,
|
||||
RSND_REG_SRCIN_TIMSEL2,
|
||||
RSND_REG_SRCIN_TIMSEL3,
|
||||
RSND_REG_SRCIN_TIMSEL4,
|
||||
RSND_REG_SRCOUT_TIMSEL0,
|
||||
RSND_REG_SRCOUT_TIMSEL1,
|
||||
RSND_REG_SRCOUT_TIMSEL2,
|
||||
RSND_REG_SRCOUT_TIMSEL3,
|
||||
RSND_REG_SRCOUT_TIMSEL4,
|
||||
RSND_REG_SCU_SYS_STATUS0,
|
||||
RSND_REG_SCU_SYS_STATUS1, /* Gen2 only */
|
||||
RSND_REG_SCU_SYS_STATUS1,
|
||||
RSND_REG_SCU_SYS_INT_EN0,
|
||||
RSND_REG_SCU_SYS_INT_EN1, /* Gen2 only */
|
||||
RSND_REG_CMD_CTRL, /* Gen2 only */
|
||||
RSND_REG_CMD_BUSIF_DALIGN, /* Gen2 only */
|
||||
RSND_REG_SCU_SYS_INT_EN1,
|
||||
RSND_REG_CMD_CTRL,
|
||||
RSND_REG_CMD_BUSIF_DALIGN,
|
||||
RSND_REG_CMD_ROUTE_SLCT,
|
||||
RSND_REG_CMDOUT_TIMSEL, /* Gen2 only */
|
||||
RSND_REG_CMDOUT_TIMSEL,
|
||||
RSND_REG_CTU_SWRSR,
|
||||
RSND_REG_CTU_CTUIR,
|
||||
RSND_REG_CTU_ADINR,
|
||||
|
@ -147,18 +137,38 @@ enum rsnd_reg {
|
|||
RSND_REG_DVC_VOL6R,
|
||||
RSND_REG_DVC_VOL7R,
|
||||
RSND_REG_DVC_DVUER,
|
||||
RSND_REG_DVC_VRCTR, /* Gen2 only */
|
||||
RSND_REG_DVC_VRPDR, /* Gen2 only */
|
||||
RSND_REG_DVC_VRDBR, /* Gen2 only */
|
||||
RSND_REG_DVC_VRCTR,
|
||||
RSND_REG_DVC_VRPDR,
|
||||
RSND_REG_DVC_VRDBR,
|
||||
|
||||
/* ADG */
|
||||
RSND_REG_BRRA,
|
||||
RSND_REG_BRRB,
|
||||
RSND_REG_SSICKR,
|
||||
RSND_REG_DIV_EN, /* Gen2 only */
|
||||
RSND_REG_BRGCKR,
|
||||
RSND_REG_DIV_EN,
|
||||
RSND_REG_AUDIO_CLK_SEL0,
|
||||
RSND_REG_AUDIO_CLK_SEL1,
|
||||
RSND_REG_AUDIO_CLK_SEL2, /* Gen2 only */
|
||||
RSND_REG_AUDIO_CLK_SEL2,
|
||||
|
||||
/* SSIU */
|
||||
RSND_REG_SSI_MODE,
|
||||
RSND_REG_SSI_MODE0,
|
||||
RSND_REG_SSI_MODE1,
|
||||
RSND_REG_SSI_MODE2,
|
||||
RSND_REG_SSI_CONTROL,
|
||||
RSND_REG_SSI_CTRL,
|
||||
RSND_REG_SSI_BUSIF_MODE,
|
||||
RSND_REG_SSI_BUSIF_ADINR,
|
||||
RSND_REG_SSI_BUSIF_DALIGN,
|
||||
RSND_REG_SSI_INT_ENABLE,
|
||||
RSND_REG_SSI_SYS_STATUS0,
|
||||
RSND_REG_SSI_SYS_STATUS1,
|
||||
RSND_REG_SSI_SYS_STATUS2,
|
||||
RSND_REG_SSI_SYS_STATUS3,
|
||||
RSND_REG_SSI_SYS_STATUS4,
|
||||
RSND_REG_SSI_SYS_STATUS5,
|
||||
RSND_REG_SSI_SYS_STATUS6,
|
||||
RSND_REG_SSI_SYS_STATUS7,
|
||||
|
||||
/* SSI */
|
||||
RSND_REG_SSICR,
|
||||
|
@ -199,7 +209,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
|
|||
* R-Car DMA
|
||||
*/
|
||||
int rsnd_dma_attach(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod, struct rsnd_mod **dma_mod, int id);
|
||||
struct rsnd_mod *mod, struct rsnd_mod **dma_mod);
|
||||
int rsnd_dma_probe(struct rsnd_priv *priv);
|
||||
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
|
||||
struct rsnd_mod *mod, char *name);
|
||||
|
@ -259,6 +269,12 @@ struct rsnd_mod_ops {
|
|||
int (*fallback)(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv);
|
||||
int (*nolock_start)(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv);
|
||||
int (*nolock_stop)(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv);
|
||||
};
|
||||
|
||||
struct rsnd_dai_stream;
|
||||
|
@ -278,7 +294,7 @@ struct rsnd_mod {
|
|||
*
|
||||
* 0xH0000CBA
|
||||
*
|
||||
* A 0: probe 1: remove
|
||||
* A 0: nolock_start 1: nolock_stop
|
||||
* B 0: init 1: quit
|
||||
* C 0: start 1: stop
|
||||
*
|
||||
|
@ -288,19 +304,23 @@ struct rsnd_mod {
|
|||
* H 0: fallback
|
||||
* H 0: hw_params
|
||||
*/
|
||||
#define __rsnd_mod_shift_probe 0
|
||||
#define __rsnd_mod_shift_remove 0
|
||||
#define __rsnd_mod_shift_nolock_start 0
|
||||
#define __rsnd_mod_shift_nolock_stop 0
|
||||
#define __rsnd_mod_shift_init 4
|
||||
#define __rsnd_mod_shift_quit 4
|
||||
#define __rsnd_mod_shift_start 8
|
||||
#define __rsnd_mod_shift_stop 8
|
||||
#define __rsnd_mod_shift_probe 28 /* always called */
|
||||
#define __rsnd_mod_shift_remove 28 /* always called */
|
||||
#define __rsnd_mod_shift_irq 28 /* always called */
|
||||
#define __rsnd_mod_shift_pcm_new 28 /* always called */
|
||||
#define __rsnd_mod_shift_fallback 28 /* always called */
|
||||
#define __rsnd_mod_shift_hw_params 28 /* always called */
|
||||
|
||||
#define __rsnd_mod_add_probe 1
|
||||
#define __rsnd_mod_add_remove -1
|
||||
#define __rsnd_mod_add_probe 0
|
||||
#define __rsnd_mod_add_remove 0
|
||||
#define __rsnd_mod_add_nolock_start 1
|
||||
#define __rsnd_mod_add_nolock_stop -1
|
||||
#define __rsnd_mod_add_init 1
|
||||
#define __rsnd_mod_add_quit -1
|
||||
#define __rsnd_mod_add_start 1
|
||||
|
@ -311,7 +331,7 @@ struct rsnd_mod {
|
|||
#define __rsnd_mod_add_hw_params 0
|
||||
|
||||
#define __rsnd_mod_call_probe 0
|
||||
#define __rsnd_mod_call_remove 1
|
||||
#define __rsnd_mod_call_remove 0
|
||||
#define __rsnd_mod_call_init 0
|
||||
#define __rsnd_mod_call_quit 1
|
||||
#define __rsnd_mod_call_start 0
|
||||
|
@ -320,6 +340,8 @@ struct rsnd_mod {
|
|||
#define __rsnd_mod_call_pcm_new 0
|
||||
#define __rsnd_mod_call_fallback 0
|
||||
#define __rsnd_mod_call_hw_params 0
|
||||
#define __rsnd_mod_call_nolock_start 0
|
||||
#define __rsnd_mod_call_nolock_stop 1
|
||||
|
||||
#define rsnd_mod_to_priv(mod) ((mod)->priv)
|
||||
#define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1)
|
||||
|
@ -346,6 +368,18 @@ void rsnd_mod_interrupt(struct rsnd_mod *mod,
|
|||
u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod,
|
||||
enum rsnd_mod_type type);
|
||||
struct rsnd_mod *rsnd_mod_next(int *iterator,
|
||||
struct rsnd_dai_stream *io,
|
||||
enum rsnd_mod_type *array,
|
||||
int array_size);
|
||||
#define for_each_rsnd_mod(iterator, pos, io) \
|
||||
for (iterator = 0; \
|
||||
(pos = rsnd_mod_next(&iterator, io, NULL, 0));)
|
||||
#define for_each_rsnd_mod_arrays(iterator, pos, io, array, size) \
|
||||
for (iterator = 0; \
|
||||
(pos = rsnd_mod_next(&iterator, io, array, size));)
|
||||
#define for_each_rsnd_mod_array(iterator, pos, io, array) \
|
||||
for_each_rsnd_mod_arrays(iterator, pos, io, array, ARRAY_SIZE(array))
|
||||
|
||||
void rsnd_parse_connect_common(struct rsnd_dai *rdai,
|
||||
struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
|
||||
|
@ -364,6 +398,18 @@ int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io);
|
|||
int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io);
|
||||
int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io);
|
||||
|
||||
/*
|
||||
* DT
|
||||
*/
|
||||
#define rsnd_parse_of_node(priv, node) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, node)
|
||||
#define RSND_NODE_DAI "rcar_sound,dai"
|
||||
#define RSND_NODE_SSI "rcar_sound,ssi"
|
||||
#define RSND_NODE_SRC "rcar_sound,src"
|
||||
#define RSND_NODE_CTU "rcar_sound,ctu"
|
||||
#define RSND_NODE_MIX "rcar_sound,mix"
|
||||
#define RSND_NODE_DVC "rcar_sound,dvc"
|
||||
|
||||
/*
|
||||
* R-Car sound DAI
|
||||
*/
|
||||
|
@ -382,6 +428,7 @@ struct rsnd_dai_stream {
|
|||
};
|
||||
#define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL)
|
||||
#define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI)
|
||||
#define rsnd_io_to_mod_ssiu(io) rsnd_io_to_mod((io), RSND_MOD_SSIU)
|
||||
#define rsnd_io_to_mod_ssip(io) rsnd_io_to_mod((io), RSND_MOD_SSIP)
|
||||
#define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC)
|
||||
#define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU)
|
||||
|
@ -428,8 +475,7 @@ int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
|
|||
int rsnd_dai_connect(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
enum rsnd_mod_type type);
|
||||
#define rsnd_dai_of_node(priv) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dai")
|
||||
#define rsnd_dai_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DAI)
|
||||
|
||||
/*
|
||||
* R-Car Gen1/Gen2
|
||||
|
@ -453,6 +499,9 @@ int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,
|
|||
unsigned int out_rate);
|
||||
int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io);
|
||||
#define rsnd_adg_clk_enable(priv) rsnd_adg_clk_control(priv, 1)
|
||||
#define rsnd_adg_clk_disable(priv) rsnd_adg_clk_control(priv, 0)
|
||||
void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable);
|
||||
|
||||
/*
|
||||
* R-Car sound priv
|
||||
|
@ -606,8 +655,7 @@ u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io);
|
|||
__rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
|
||||
int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
|
||||
|
||||
#define rsnd_ssi_of_node(priv) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
|
||||
#define rsnd_ssi_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SSI)
|
||||
void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
|
||||
struct device_node *playback,
|
||||
struct device_node *capture);
|
||||
|
@ -633,8 +681,7 @@ unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
|
|||
struct rsnd_dai_stream *io,
|
||||
int is_in);
|
||||
|
||||
#define rsnd_src_of_node(priv) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
|
||||
#define rsnd_src_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SRC)
|
||||
#define rsnd_parse_connect_src(rdai, playback, capture) \
|
||||
rsnd_parse_connect_common(rdai, rsnd_src_mod_get, \
|
||||
rsnd_src_of_node(rsnd_rdai_to_priv(rdai)), \
|
||||
|
@ -647,8 +694,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv);
|
|||
void rsnd_ctu_remove(struct rsnd_priv *priv);
|
||||
int rsnd_ctu_converted_channel(struct rsnd_mod *mod);
|
||||
struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
|
||||
#define rsnd_ctu_of_node(priv) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu")
|
||||
#define rsnd_ctu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_CTU)
|
||||
#define rsnd_parse_connect_ctu(rdai, playback, capture) \
|
||||
rsnd_parse_connect_common(rdai, rsnd_ctu_mod_get, \
|
||||
rsnd_ctu_of_node(rsnd_rdai_to_priv(rdai)), \
|
||||
|
@ -660,8 +706,7 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
|
|||
int rsnd_mix_probe(struct rsnd_priv *priv);
|
||||
void rsnd_mix_remove(struct rsnd_priv *priv);
|
||||
struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id);
|
||||
#define rsnd_mix_of_node(priv) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,mix")
|
||||
#define rsnd_mix_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_MIX)
|
||||
#define rsnd_parse_connect_mix(rdai, playback, capture) \
|
||||
rsnd_parse_connect_common(rdai, rsnd_mix_mod_get, \
|
||||
rsnd_mix_of_node(rsnd_rdai_to_priv(rdai)), \
|
||||
|
@ -673,8 +718,7 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id);
|
|||
int rsnd_dvc_probe(struct rsnd_priv *priv);
|
||||
void rsnd_dvc_remove(struct rsnd_priv *priv);
|
||||
struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
|
||||
#define rsnd_dvc_of_node(priv) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc")
|
||||
#define rsnd_dvc_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DVC)
|
||||
#define rsnd_parse_connect_dvc(rdai, playback, capture) \
|
||||
rsnd_parse_connect_common(rdai, rsnd_dvc_mod_get, \
|
||||
rsnd_dvc_of_node(rsnd_rdai_to_priv(rdai)), \
|
||||
|
|
|
@ -189,6 +189,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
|
|||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
||||
int use_src = 0;
|
||||
u32 fin, fout;
|
||||
u32 ifscr, fsrate, adinr;
|
||||
u32 cr, route;
|
||||
|
@ -214,6 +215,8 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
|
|||
return;
|
||||
}
|
||||
|
||||
use_src = (fin != fout) | rsnd_src_sync_is_enabled(mod);
|
||||
|
||||
/*
|
||||
* SRC_ADINR
|
||||
*/
|
||||
|
@ -225,7 +228,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
|
|||
*/
|
||||
ifscr = 0;
|
||||
fsrate = 0;
|
||||
if (fin != fout) {
|
||||
if (use_src) {
|
||||
u64 n;
|
||||
|
||||
ifscr = 1;
|
||||
|
@ -239,7 +242,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
|
|||
*/
|
||||
cr = 0x00011110;
|
||||
route = 0x0;
|
||||
if (fin != fout) {
|
||||
if (use_src) {
|
||||
route = 0x1;
|
||||
|
||||
if (rsnd_src_sync_is_enabled(mod)) {
|
||||
|
@ -327,8 +330,8 @@ static void rsnd_src_status_clear(struct rsnd_mod *mod)
|
|||
{
|
||||
u32 val = OUF_SRC(rsnd_mod_id(mod));
|
||||
|
||||
rsnd_mod_bset(mod, SCU_SYS_STATUS0, val, val);
|
||||
rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val);
|
||||
rsnd_mod_write(mod, SCU_SYS_STATUS0, val);
|
||||
rsnd_mod_write(mod, SCU_SYS_STATUS1, val);
|
||||
}
|
||||
|
||||
static bool rsnd_src_error_occurred(struct rsnd_mod *mod)
|
||||
|
@ -475,7 +478,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = rsnd_dma_attach(io, mod, &src->dma, 0);
|
||||
ret = rsnd_dma_attach(io, mod, &src->dma);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -417,11 +417,14 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
|
|||
int chan = params_channels(params);
|
||||
|
||||
/*
|
||||
* Already working.
|
||||
* It will happen if SSI has parent/child connection.
|
||||
* snd_pcm_ops::hw_params will be called *before*
|
||||
* snd_soc_dai_ops::trigger. Thus, ssi->usrcnt is 0
|
||||
* in 1st call.
|
||||
*/
|
||||
if (ssi->usrcnt > 1) {
|
||||
if (ssi->usrcnt) {
|
||||
/*
|
||||
* Already working.
|
||||
* It will happen if SSI has parent/child connection.
|
||||
* it is error if child <-> parent SSI uses
|
||||
* different channels.
|
||||
*/
|
||||
|
@ -644,10 +647,14 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_irq(dev, ssi->irq,
|
||||
rsnd_ssi_interrupt,
|
||||
IRQF_SHARED,
|
||||
dev_name(dev), mod);
|
||||
/*
|
||||
* SSI might be called again as PIO fallback
|
||||
* It is easy to manual handling for IRQ request/free
|
||||
*/
|
||||
ret = request_irq(ssi->irq,
|
||||
rsnd_ssi_interrupt,
|
||||
IRQF_SHARED,
|
||||
dev_name(dev), mod);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -669,7 +676,6 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
|
|||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
int dma_id = 0; /* not needed */
|
||||
int ret;
|
||||
|
||||
/*
|
||||
|
@ -684,7 +690,7 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
|
|||
return ret;
|
||||
|
||||
/* SSI probe might be called many times in MUX multi path */
|
||||
ret = rsnd_dma_attach(io, mod, &ssi->dma, dma_id);
|
||||
ret = rsnd_dma_attach(io, mod, &ssi->dma);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -694,11 +700,9 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
|
|||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
int irq = ssi->irq;
|
||||
|
||||
/* PIO will request IRQ again */
|
||||
devm_free_irq(dev, irq, mod);
|
||||
free_irq(ssi->irq, mod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,26 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
|
|||
u32 mask1, val1;
|
||||
u32 mask2, val2;
|
||||
|
||||
/* clear status */
|
||||
switch (id) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
rsnd_mod_write(mod, SSI_SYS_STATUS0, 0xf << (id * 4));
|
||||
rsnd_mod_write(mod, SSI_SYS_STATUS2, 0xf << (id * 4));
|
||||
rsnd_mod_write(mod, SSI_SYS_STATUS4, 0xf << (id * 4));
|
||||
rsnd_mod_write(mod, SSI_SYS_STATUS6, 0xf << (id * 4));
|
||||
break;
|
||||
case 9:
|
||||
rsnd_mod_write(mod, SSI_SYS_STATUS1, 0xf << 4);
|
||||
rsnd_mod_write(mod, SSI_SYS_STATUS3, 0xf << 4);
|
||||
rsnd_mod_write(mod, SSI_SYS_STATUS5, 0xf << 4);
|
||||
rsnd_mod_write(mod, SSI_SYS_STATUS7, 0xf << 4);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* SSI_MODE0
|
||||
*/
|
||||
|
|
Загрузка…
Ссылка в новой задаче