aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/sh
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sh')
-rw-r--r--sound/soc/sh/fsi.c12
-rw-r--r--sound/soc/sh/hac.c2
-rw-r--r--sound/soc/sh/rcar/adg.c2
-rw-r--r--sound/soc/sh/rcar/core.c239
-rw-r--r--sound/soc/sh/rcar/dvc.c215
-rw-r--r--sound/soc/sh/rcar/gen.c30
-rw-r--r--sound/soc/sh/rcar/rsnd.h92
-rw-r--r--sound/soc/sh/rcar/src.c101
-rw-r--r--sound/soc/sh/rcar/ssi.c233
9 files changed, 669 insertions, 257 deletions
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 66fddec9543d..8869971d7884 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -842,12 +842,9 @@ static int fsi_clk_disable(struct device *dev,
return -EINVAL;
if (1 == clock->count--) {
- if (clock->xck)
- clk_disable(clock->xck);
- if (clock->ick)
- clk_disable(clock->ick);
- if (clock->div)
- clk_disable(clock->div);
+ clk_disable(clock->xck);
+ clk_disable(clock->ick);
+ clk_disable(clock->div);
}
return 0;
@@ -1711,8 +1708,7 @@ static const struct snd_soc_dai_ops fsi_dai_ops = {
static struct snd_pcm_hardware fsi_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE,
+ SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8192,
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
index 0e0115cf2959..84c51037a7d0 100644
--- a/sound/soc/sh/hac.c
+++ b/sound/soc/sh/hac.c
@@ -272,7 +272,7 @@ static const struct snd_soc_dai_ops hac_dai_ops = {
static struct snd_soc_dai_driver sh4_hac_dai[] = {
{
.name = "hac-dai.0",
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index fc41a0e8b09f..14d1a7193469 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -430,7 +430,7 @@ int rsnd_adg_probe(struct platform_device *pdev,
adg->clk[CLKI] = devm_clk_get(dev, "clk_i");
for_each_rsnd_clk(clk, adg, i)
- dev_dbg(dev, "clk %d : %p\n", i, clk);
+ dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
rsnd_adg_ssi_clk_init(priv, adg);
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 1922ec57d10a..75308bbc2ce8 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -349,7 +349,7 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
dma_name);
if (!dma->chan) {
dev_err(dev, "can't get dma channel\n");
- return -EIO;
+ goto rsnd_dma_channel_err;
}
ret = dmaengine_slave_config(dma->chan, &cfg);
@@ -363,8 +363,15 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
rsnd_dma_init_err:
rsnd_dma_quit(priv, dma);
+rsnd_dma_channel_err:
- return ret;
+ /*
+ * DMA failed. try to PIO mode
+ * see
+ * rsnd_ssi_fallback()
+ * rsnd_rdai_continuance_probe()
+ */
+ return -EAGAIN;
}
void rsnd_dma_quit(struct rsnd_priv *priv,
@@ -409,9 +416,16 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
({ \
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \
struct device *dev = rsnd_priv_to_dev(priv); \
- dev_dbg(dev, "%s [%d] %s\n", \
- rsnd_mod_name(mod), rsnd_mod_id(mod), #func); \
- (mod)->ops->func(mod, rdai); \
+ u32 mask = 1 << __rsnd_mod_shift_##func; \
+ u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \
+ int ret = 0; \
+ if ((mod->status & mask) == call) { \
+ dev_dbg(dev, "%s[%d] %s\n", \
+ rsnd_mod_name(mod), rsnd_mod_id(mod), #func); \
+ ret = (mod)->ops->func(mod, rdai); \
+ mod->status = (mod->status & ~mask) | (~call & mask); \
+ } \
+ ret; \
})
#define rsnd_mod_call(mod, func, rdai...) \
@@ -456,6 +470,13 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
return 0;
}
+static void rsnd_dai_disconnect(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
+{
+ mod->io = NULL;
+ io->mod[mod->type] = NULL;
+}
+
int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai)
{
int id = rdai - priv->rdai;
@@ -686,6 +707,20 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
ret; \
})
+#define rsnd_path_break(priv, io, type) \
+{ \
+ struct rsnd_mod *mod; \
+ int id = -1; \
+ \
+ if (rsnd_is_enable_path(io, type)) { \
+ id = rsnd_info_id(priv, io, type); \
+ if (id >= 0) { \
+ mod = rsnd_##type##_mod_get(priv, id); \
+ rsnd_dai_disconnect(mod, io); \
+ } \
+ } \
+}
+
static int rsnd_path_init(struct rsnd_priv *priv,
struct rsnd_dai *rdai,
struct rsnd_dai_stream *io)
@@ -886,8 +921,7 @@ static int rsnd_dai_probe(struct platform_device *pdev,
static struct snd_pcm_hardware rsnd_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE,
+ SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8192,
@@ -935,6 +969,150 @@ static struct snd_pcm_ops rsnd_pcm_ops = {
};
/*
+ * snd_kcontrol
+ */
+#define kcontrol_to_cfg(kctrl) ((struct rsnd_kctrl_cfg *)kctrl->private_value)
+static int rsnd_kctrl_info(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
+
+ if (cfg->texts) {
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = cfg->size;
+ uinfo->value.enumerated.items = cfg->max;
+ if (uinfo->value.enumerated.item >= cfg->max)
+ uinfo->value.enumerated.item = cfg->max - 1;
+ strlcpy(uinfo->value.enumerated.name,
+ cfg->texts[uinfo->value.enumerated.item],
+ sizeof(uinfo->value.enumerated.name));
+ } else {
+ uinfo->count = cfg->size;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = cfg->max;
+ uinfo->type = (cfg->max == 1) ?
+ SNDRV_CTL_ELEM_TYPE_BOOLEAN :
+ SNDRV_CTL_ELEM_TYPE_INTEGER;
+ }
+
+ return 0;
+}
+
+static int rsnd_kctrl_get(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *uc)
+{
+ struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
+ int i;
+
+ for (i = 0; i < cfg->size; i++)
+ if (cfg->texts)
+ uc->value.enumerated.item[i] = cfg->val[i];
+ else
+ uc->value.integer.value[i] = cfg->val[i];
+
+ return 0;
+}
+
+static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
+ struct snd_ctl_elem_value *uc)
+{
+ struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
+ struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
+ int i, change = 0;
+
+ for (i = 0; i < cfg->size; i++) {
+ if (cfg->texts) {
+ change |= (uc->value.enumerated.item[i] != cfg->val[i]);
+ cfg->val[i] = uc->value.enumerated.item[i];
+ } else {
+ change |= (uc->value.integer.value[i] != cfg->val[i]);
+ cfg->val[i] = uc->value.integer.value[i];
+ }
+ }
+
+ if (change)
+ cfg->update(mod);
+
+ return change;
+}
+
+static int __rsnd_kctrl_new(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct snd_soc_pcm_runtime *rtd,
+ const unsigned char *name,
+ struct rsnd_kctrl_cfg *cfg,
+ void (*update)(struct rsnd_mod *mod))
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_kcontrol *kctrl;
+ struct snd_kcontrol_new knew = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = name,
+ .info = rsnd_kctrl_info,
+ .get = rsnd_kctrl_get,
+ .put = rsnd_kctrl_put,
+ .private_value = (unsigned long)cfg,
+ };
+ int ret;
+
+ kctrl = snd_ctl_new1(&knew, mod);
+ if (!kctrl)
+ return -ENOMEM;
+
+ ret = snd_ctl_add(card, kctrl);
+ if (ret < 0)
+ return ret;
+
+ cfg->update = update;
+
+ return 0;
+}
+
+int rsnd_kctrl_new_m(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct snd_soc_pcm_runtime *rtd,
+ const unsigned char *name,
+ void (*update)(struct rsnd_mod *mod),
+ struct rsnd_kctrl_cfg_m *_cfg,
+ u32 max)
+{
+ _cfg->cfg.max = max;
+ _cfg->cfg.size = RSND_DVC_CHANNELS;
+ _cfg->cfg.val = _cfg->val;
+ return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
+}
+
+int rsnd_kctrl_new_s(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct snd_soc_pcm_runtime *rtd,
+ const unsigned char *name,
+ void (*update)(struct rsnd_mod *mod),
+ struct rsnd_kctrl_cfg_s *_cfg,
+ u32 max)
+{
+ _cfg->cfg.max = max;
+ _cfg->cfg.size = 1;
+ _cfg->cfg.val = &_cfg->val;
+ return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
+}
+
+int rsnd_kctrl_new_e(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct snd_soc_pcm_runtime *rtd,
+ const unsigned char *name,
+ struct rsnd_kctrl_cfg_s *_cfg,
+ void (*update)(struct rsnd_mod *mod),
+ const char * const *texts,
+ u32 max)
+{
+ _cfg->cfg.max = max;
+ _cfg->cfg.size = 1;
+ _cfg->cfg.val = &_cfg->val;
+ _cfg->cfg.texts = texts;
+ return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
+}
+
+/*
* snd_soc_platform
*/
@@ -977,6 +1155,49 @@ static const struct snd_soc_component_driver rsnd_soc_component = {
.name = "rsnd",
};
+static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
+ struct rsnd_dai *rdai,
+ int is_play)
+{
+ struct rsnd_dai_stream *io = is_play ? &rdai->playback : &rdai->capture;
+ int ret;
+
+ ret = rsnd_dai_call(probe, io, rdai);
+ if (ret == -EAGAIN) {
+ /*
+ * Fallback to PIO mode
+ */
+
+ /*
+ * call "remove" for SSI/SRC/DVC
+ * SSI will be switch to PIO mode if it was DMA mode
+ * see
+ * rsnd_dma_init()
+ * rsnd_ssi_fallback()
+ */
+ rsnd_dai_call(remove, io, rdai);
+
+ /*
+ * remove SRC/DVC from DAI,
+ */
+ rsnd_path_break(priv, io, src);
+ rsnd_path_break(priv, io, dvc);
+
+ /*
+ * fallback
+ */
+ rsnd_dai_call(fallback, io, rdai);
+
+ /*
+ * retry to "probe".
+ * DAI has SSI which is PIO mode only now.
+ */
+ ret = rsnd_dai_call(probe, io, rdai);
+ }
+
+ return ret;
+}
+
/*
* rsnd probe
*/
@@ -1038,11 +1259,11 @@ static int rsnd_probe(struct platform_device *pdev)
}
for_each_rsnd_dai(rdai, priv, i) {
- ret = rsnd_dai_call(probe, &rdai->playback, rdai);
+ ret = rsnd_rdai_continuance_probe(priv, rdai, 1);
if (ret)
goto exit_snd_probe;
- ret = rsnd_dai_call(probe, &rdai->capture, rdai);
+ ret = rsnd_rdai_continuance_probe(priv, rdai, 0);
if (ret)
goto exit_snd_probe;
}
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
index 3f443930c2b1..5380a4827ba7 100644
--- a/sound/soc/sh/rcar/dvc.c
+++ b/sound/soc/sh/rcar/dvc.c
@@ -11,8 +11,6 @@
#include "rsnd.h"
#define RSND_DVC_NAME_SIZE 16
-#define RSND_DVC_VOLUME_MAX 100
-#define RSND_DVC_VOLUME_NUM 2
#define DVC_NAME "dvc"
@@ -20,8 +18,11 @@ struct rsnd_dvc {
struct rsnd_dvc_platform_info *info; /* rcar_snd.h */
struct rsnd_mod mod;
struct clk *clk;
- u8 volume[RSND_DVC_VOLUME_NUM];
- u8 mute[RSND_DVC_VOLUME_NUM];
+ struct rsnd_kctrl_cfg_m volume;
+ struct rsnd_kctrl_cfg_m mute;
+ struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */
+ struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */
+ struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */
};
#define rsnd_mod_to_dvc(_mod) \
@@ -33,23 +34,87 @@ struct rsnd_dvc {
((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \
i++)
+static const char const *dvc_ramp_rate[] = {
+ "128 dB/1 step", /* 00000 */
+ "64 dB/1 step", /* 00001 */
+ "32 dB/1 step", /* 00010 */
+ "16 dB/1 step", /* 00011 */
+ "8 dB/1 step", /* 00100 */
+ "4 dB/1 step", /* 00101 */
+ "2 dB/1 step", /* 00110 */
+ "1 dB/1 step", /* 00111 */
+ "0.5 dB/1 step", /* 01000 */
+ "0.25 dB/1 step", /* 01001 */
+ "0.125 dB/1 step", /* 01010 */
+ "0.125 dB/2 steps", /* 01011 */
+ "0.125 dB/4 steps", /* 01100 */
+ "0.125 dB/8 steps", /* 01101 */
+ "0.125 dB/16 steps", /* 01110 */
+ "0.125 dB/32 steps", /* 01111 */
+ "0.125 dB/64 steps", /* 10000 */
+ "0.125 dB/128 steps", /* 10001 */
+ "0.125 dB/256 steps", /* 10010 */
+ "0.125 dB/512 steps", /* 10011 */
+ "0.125 dB/1024 steps", /* 10100 */
+ "0.125 dB/2048 steps", /* 10101 */
+ "0.125 dB/4096 steps", /* 10110 */
+ "0.125 dB/8192 steps", /* 10111 */
+};
+
static void rsnd_dvc_volume_update(struct rsnd_mod *mod)
{
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
- u32 max = (0x00800000 - 1);
- u32 vol[RSND_DVC_VOLUME_NUM];
+ u32 val[RSND_DVC_CHANNELS];
+ u32 dvucr = 0;
u32 mute = 0;
int i;
- for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) {
- vol[i] = max / RSND_DVC_VOLUME_MAX * dvc->volume[i];
- mute |= (!!dvc->mute[i]) << i;
+ for (i = 0; i < dvc->mute.cfg.size; i++)
+ mute |= (!!dvc->mute.cfg.val[i]) << i;
+
+ /* Disable DVC Register access */
+ rsnd_mod_write(mod, DVC_DVUER, 0);
+
+ /* Enable Ramp */
+ if (dvc->ren.val) {
+ dvucr |= 0x10;
+
+ /* Digital Volume Max */
+ for (i = 0; i < RSND_DVC_CHANNELS; i++)
+ val[i] = dvc->volume.cfg.max;
+
+ rsnd_mod_write(mod, DVC_VRCTR, 0xff);
+ rsnd_mod_write(mod, DVC_VRPDR, dvc->rup.val << 8 |
+ dvc->rdown.val);
+ /*
+ * FIXME !!
+ * use scale-downed Digital Volume
+ * as Volume Ramp
+ * 7F FFFF -> 3FF
+ */
+ rsnd_mod_write(mod, DVC_VRDBR,
+ 0x3ff - (dvc->volume.val[0] >> 13));
+
+ } else {
+ for (i = 0; i < RSND_DVC_CHANNELS; i++)
+ val[i] = dvc->volume.val[i];
+ }
+
+ /* Enable Digital Volume */
+ dvucr |= 0x100;
+ rsnd_mod_write(mod, DVC_VOL0R, val[0]);
+ rsnd_mod_write(mod, DVC_VOL1R, val[1]);
+
+ /* Enable Mute */
+ if (mute) {
+ dvucr |= 0x1;
+ rsnd_mod_write(mod, DVC_ZCMCR, mute);
}
- rsnd_mod_write(mod, DVC_VOL0R, vol[0]);
- rsnd_mod_write(mod, DVC_VOL1R, vol[1]);
+ rsnd_mod_write(mod, DVC_DVUCR, dvucr);
- rsnd_mod_write(mod, DVC_ZCMCR, mute);
+ /* Enable DVC Register access */
+ rsnd_mod_write(mod, DVC_DVUER, 1);
}
static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
@@ -58,7 +123,8 @@ static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
- dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
+ dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
return 0;
}
@@ -102,16 +168,11 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod));
- /* enable Volume / Mute */
- rsnd_mod_write(dvc_mod, DVC_DVUCR, 0x101);
-
/* ch0/ch1 Volume */
rsnd_dvc_volume_update(dvc_mod);
rsnd_mod_write(dvc_mod, DVC_DVUIR, 0);
- rsnd_mod_write(dvc_mod, DVC_DVUER, 1);
-
rsnd_adg_set_cmd_timsel_gen2(rdai, dvc_mod, io);
return 0;
@@ -143,86 +204,6 @@ static int rsnd_dvc_stop(struct rsnd_mod *mod,
return 0;
}
-static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl,
- struct snd_ctl_elem_info *uinfo)
-{
- struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
- struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
- u8 *val = (u8 *)kctrl->private_value;
-
- uinfo->count = RSND_DVC_VOLUME_NUM;
- uinfo->value.integer.min = 0;
-
- if (val == dvc->volume) {
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->value.integer.max = RSND_DVC_VOLUME_MAX;
- } else {
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- uinfo->value.integer.max = 1;
- }
-
- return 0;
-}
-
-static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl,
- struct snd_ctl_elem_value *ucontrol)
-{
- u8 *val = (u8 *)kctrl->private_value;
- int i;
-
- for (i = 0; i < RSND_DVC_VOLUME_NUM; i++)
- ucontrol->value.integer.value[i] = val[i];
-
- return 0;
-}
-
-static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
- u8 *val = (u8 *)kctrl->private_value;
- int i, change = 0;
-
- for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) {
- change |= (ucontrol->value.integer.value[i] != val[i]);
- val[i] = ucontrol->value.integer.value[i];
- }
-
- if (change)
- rsnd_dvc_volume_update(mod);
-
- return change;
-}
-
-static int __rsnd_dvc_pcm_new(struct rsnd_mod *mod,
- struct rsnd_dai *rdai,
- struct snd_soc_pcm_runtime *rtd,
- const unsigned char *name,
- u8 *private)
-{
- struct snd_card *card = rtd->card->snd_card;
- struct snd_kcontrol *kctrl;
- struct snd_kcontrol_new knew = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = name,
- .info = rsnd_dvc_volume_info,
- .get = rsnd_dvc_volume_get,
- .put = rsnd_dvc_volume_put,
- .private_value = (unsigned long)private,
- };
- int ret;
-
- kctrl = snd_ctl_new1(&knew, mod);
- if (!kctrl)
- return -ENOMEM;
-
- ret = snd_ctl_add(card, kctrl);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
struct rsnd_dai *rdai,
struct snd_soc_pcm_runtime *rtd)
@@ -232,18 +213,48 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
int ret;
/* Volume */
- ret = __rsnd_dvc_pcm_new(mod, rdai, rtd,
+ ret = rsnd_kctrl_new_m(mod, rdai, rtd,
rsnd_dai_is_play(rdai, io) ?
"DVC Out Playback Volume" : "DVC In Capture Volume",
- dvc->volume);
+ rsnd_dvc_volume_update,
+ &dvc->volume, 0x00800000 - 1);
if (ret < 0)
return ret;
/* Mute */
- ret = __rsnd_dvc_pcm_new(mod, rdai, rtd,
+ ret = rsnd_kctrl_new_m(mod, rdai, rtd,
rsnd_dai_is_play(rdai, io) ?
"DVC Out Mute Switch" : "DVC In Mute Switch",
- dvc->mute);
+ rsnd_dvc_volume_update,
+ &dvc->mute, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Ramp */
+ ret = rsnd_kctrl_new_s(mod, rdai, rtd,
+ rsnd_dai_is_play(rdai, io) ?
+ "DVC Out Ramp Switch" : "DVC In Ramp Switch",
+ rsnd_dvc_volume_update,
+ &dvc->ren, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = rsnd_kctrl_new_e(mod, rdai, rtd,
+ rsnd_dai_is_play(rdai, io) ?
+ "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
+ &dvc->rup,
+ rsnd_dvc_volume_update,
+ dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+ if (ret < 0)
+ return ret;
+
+ ret = rsnd_kctrl_new_e(mod, rdai, rtd,
+ rsnd_dai_is_play(rdai, io) ?
+ "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
+ &dvc->rdown,
+ rsnd_dvc_volume_update,
+ dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+
if (ret < 0)
return ret;
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index f95e7ab135e8..87a6f2d62775 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -8,6 +8,17 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+
+/*
+ * #define DEBUG
+ *
+ * you can also add below in
+ * ${LINUX}/drivers/base/regmap/regmap.c
+ * for regmap debug
+ *
+ * #define LOG_DEVICE "xxxx.rcar_sound"
+ */
+
#include "rsnd.h"
struct rsnd_gen {
@@ -67,9 +78,10 @@ u32 rsnd_read(struct rsnd_priv *priv,
if (!rsnd_is_accessible_reg(priv, gen, reg))
return 0;
- regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
+ dev_dbg(dev, "r %s[%d] - %4d : %08x\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val);
- dev_dbg(dev, "r %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, val);
+ regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
return val;
}
@@ -84,9 +96,10 @@ void rsnd_write(struct rsnd_priv *priv,
if (!rsnd_is_accessible_reg(priv, gen, reg))
return;
- regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
+ dev_dbg(dev, "w %s[%d] - %4d : %08x\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data);
- dev_dbg(dev, "w %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, data);
+ regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
}
void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
@@ -98,11 +111,11 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
if (!rsnd_is_accessible_reg(priv, gen, reg))
return;
+ dev_dbg(dev, "b %s[%d] - %4d : %08x/%08x\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data, mask);
+
regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
mask, data);
-
- dev_dbg(dev, "b %s - 0x%04d : %08x/%08x\n",
- rsnd_mod_name(mod), reg, data, mask);
}
#define rsnd_gen_regmap_init(priv, id_size, reg_id, conf) \
@@ -311,6 +324,9 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
RSND_GEN_M_REG(DVC_ADINR, 0xe08, 0x100),
RSND_GEN_M_REG(DVC_DVUCR, 0xe10, 0x100),
RSND_GEN_M_REG(DVC_ZCMCR, 0xe14, 0x100),
+ RSND_GEN_M_REG(DVC_VRCTR, 0xe18, 0x100),
+ RSND_GEN_M_REG(DVC_VRPDR, 0xe1c, 0x100),
+ RSND_GEN_M_REG(DVC_VRDBR, 0xe20, 0x100),
RSND_GEN_M_REG(DVC_VOL0R, 0xe28, 0x100),
RSND_GEN_M_REG(DVC_VOL1R, 0xe2c, 0x100),
RSND_GEN_M_REG(DVC_DVUER, 0xe48, 0x100),
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index d119adf97c9c..5826c8abf794 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -91,6 +91,9 @@ enum rsnd_reg {
RSND_REG_SHARE20,
RSND_REG_SHARE21,
RSND_REG_SHARE22,
+ RSND_REG_SHARE23,
+ RSND_REG_SHARE24,
+ RSND_REG_SHARE25,
RSND_REG_MAX,
};
@@ -129,6 +132,9 @@ enum rsnd_reg {
#define RSND_REG_CMD_CTRL RSND_REG_SHARE20
#define RSND_REG_CMDOUT_TIMSEL RSND_REG_SHARE21
#define RSND_REG_BUSIF_DALIGN RSND_REG_SHARE22
+#define RSND_REG_DVC_VRCTR RSND_REG_SHARE23
+#define RSND_REG_DVC_VRPDR RSND_REG_SHARE24
+#define RSND_REG_DVC_VRDBR RSND_REG_SHARE25
struct rsnd_of_data;
struct rsnd_priv;
@@ -200,6 +206,8 @@ struct rsnd_mod_ops {
int (*pcm_new)(struct rsnd_mod *mod,
struct rsnd_dai *rdai,
struct snd_soc_pcm_runtime *rtd);
+ int (*fallback)(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai);
};
struct rsnd_dai_stream;
@@ -210,7 +218,35 @@ struct rsnd_mod {
struct rsnd_mod_ops *ops;
struct rsnd_dma dma;
struct rsnd_dai_stream *io;
+ u32 status;
};
+/*
+ * status
+ *
+ * bit
+ * 0 0: probe 1: remove
+ * 1 0: init 1: quit
+ * 2 0: start 1: stop
+ * 3 0: pcm_new
+ * 4 0: fallback
+ */
+#define __rsnd_mod_shift_probe 0
+#define __rsnd_mod_shift_remove 0
+#define __rsnd_mod_shift_init 1
+#define __rsnd_mod_shift_quit 1
+#define __rsnd_mod_shift_start 2
+#define __rsnd_mod_shift_stop 2
+#define __rsnd_mod_shift_pcm_new 3
+#define __rsnd_mod_shift_fallback 4
+
+#define __rsnd_mod_call_probe 0
+#define __rsnd_mod_call_remove 1
+#define __rsnd_mod_call_init 0
+#define __rsnd_mod_call_quit 1
+#define __rsnd_mod_call_start 0
+#define __rsnd_mod_call_stop 1
+#define __rsnd_mod_call_pcm_new 0
+#define __rsnd_mod_call_fallback 0
#define rsnd_mod_to_priv(mod) ((mod)->priv)
#define rsnd_mod_to_dma(mod) (&(mod)->dma)
@@ -267,7 +303,8 @@ struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id);
int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io);
int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai);
#define rsnd_dai_get_platform_info(rdai) ((rdai)->info)
-#define rsnd_io_to_runtime(io) ((io)->substream->runtime)
+#define rsnd_io_to_runtime(io) ((io)->substream ? \
+ (io)->substream->runtime : NULL)
void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
@@ -382,6 +419,51 @@ struct rsnd_priv {
})
/*
+ * rsnd_kctrl
+ */
+struct rsnd_kctrl_cfg {
+ unsigned int max;
+ unsigned int size;
+ u32 *val;
+ const char * const *texts;
+ void (*update)(struct rsnd_mod *mod);
+};
+
+#define RSND_DVC_CHANNELS 2
+struct rsnd_kctrl_cfg_m {
+ struct rsnd_kctrl_cfg cfg;
+ u32 val[RSND_DVC_CHANNELS];
+};
+
+struct rsnd_kctrl_cfg_s {
+ struct rsnd_kctrl_cfg cfg;
+ u32 val;
+};
+
+int rsnd_kctrl_new_m(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct snd_soc_pcm_runtime *rtd,
+ const unsigned char *name,
+ void (*update)(struct rsnd_mod *mod),
+ struct rsnd_kctrl_cfg_m *_cfg,
+ u32 max);
+int rsnd_kctrl_new_s(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct snd_soc_pcm_runtime *rtd,
+ const unsigned char *name,
+ void (*update)(struct rsnd_mod *mod),
+ struct rsnd_kctrl_cfg_s *_cfg,
+ u32 max);
+int rsnd_kctrl_new_e(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct snd_soc_pcm_runtime *rtd,
+ const unsigned char *name,
+ struct rsnd_kctrl_cfg_s *_cfg,
+ void (*update)(struct rsnd_mod *mod),
+ const char * const *texts,
+ u32 max);
+
+/*
* R-Car SRC
*/
int rsnd_src_probe(struct platform_device *pdev,
@@ -395,10 +477,11 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
struct rsnd_dai *rdai,
int use_busif);
int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
- struct rsnd_dai *rdai,
- int use_busif);
-int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
+ struct rsnd_dai *rdai);
+int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod,
struct rsnd_dai *rdai);
+int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod,
+ struct rsnd_dai *rdai);
#define rsnd_src_nr(priv) ((priv)->src_nr)
@@ -410,6 +493,7 @@ int rsnd_ssi_probe(struct platform_device *pdev,
struct rsnd_priv *priv);
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
/*
* R-Car DVC
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 9183e0145503..eede3ac6eed2 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -175,30 +175,47 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
}
int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
- struct rsnd_dai *rdai,
- int use_busif)
+ struct rsnd_dai *rdai)
{
/*
* DMA settings for SSIU
*/
- if (use_busif)
- rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
+ rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
return 0;
}
-int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
+int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod,
struct rsnd_dai *rdai)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
- /* enable PIO interrupt if Gen2 */
- if (rsnd_is_gen2(priv))
+ if (rsnd_is_gen1(priv))
+ return 0;
+
+ /* enable SSI interrupt if Gen2 */
+ if (rsnd_ssi_is_dma_mode(ssi_mod))
+ rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0e000000);
+ else
rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000);
return 0;
}
+int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod,
+ struct rsnd_dai *rdai)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+
+ if (rsnd_is_gen1(priv))
+ return 0;
+
+ /* disable SSI interrupt if Gen2 */
+ rsnd_mod_write(ssi_mod, INT_ENABLE, 0x00000000);
+
+ return 0;
+}
+
unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
struct rsnd_dai_stream *io,
struct snd_pcm_runtime *runtime)
@@ -239,12 +256,6 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod,
rsnd_mod_write(mod, SRC_SWRSR, 0);
rsnd_mod_write(mod, SRC_SWRSR, 1);
- /*
- * Initialize the operation of the SRC internal circuits
- * see rsnd_src_start()
- */
- rsnd_mod_write(mod, SRC_SRCIR, 1);
-
/* Set channel number and output bit length */
rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod));
@@ -269,6 +280,12 @@ static int rsnd_src_init(struct rsnd_mod *mod,
clk_prepare_enable(src->clk);
+ /*
+ * Initialize the operation of the SRC internal circuits
+ * see rsnd_src_start()
+ */
+ rsnd_mod_write(mod, SRC_SRCIR, 1);
+
return 0;
}
@@ -282,32 +299,20 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
return 0;
}
-static int rsnd_src_start(struct rsnd_mod *mod,
- struct rsnd_dai *rdai)
+static int rsnd_src_start(struct rsnd_mod *mod)
{
- struct rsnd_src *src = rsnd_mod_to_src(mod);
-
/*
* Cancel the initialization and operate the SRC function
- * see rsnd_src_set_convert_rate()
+ * see rsnd_src_init()
*/
rsnd_mod_write(mod, SRC_SRCIR, 0);
- if (rsnd_src_convert_rate(src))
- rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
-
return 0;
}
-
-static int rsnd_src_stop(struct rsnd_mod *mod,
- struct rsnd_dai *rdai)
+static int rsnd_src_stop(struct rsnd_mod *mod)
{
- struct rsnd_src *src = rsnd_mod_to_src(mod);
-
- if (rsnd_src_convert_rate(src))
- rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0);
-
+ /* nothing to do */
return 0;
}
@@ -414,6 +419,7 @@ static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod,
static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
struct rsnd_dai *rdai)
{
+ struct rsnd_src *src = rsnd_mod_to_src(mod);
int ret;
ret = rsnd_src_set_convert_rate(mod, rdai);
@@ -427,6 +433,10 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
rsnd_mod_write(mod, SRC_MNFSR,
rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98);
+ /* Gen1/Gen2 are not compatible */
+ if (rsnd_src_convert_rate(src))
+ rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
+
/* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */
return 0;
@@ -438,7 +448,8 @@ static int rsnd_src_probe_gen1(struct rsnd_mod *mod,
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
- dev_dbg(dev, "%s (Gen1) is probed\n", rsnd_mod_name(mod));
+ dev_dbg(dev, "%s[%d] (Gen1) is probed\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
return 0;
}
@@ -474,7 +485,7 @@ static int rsnd_src_start_gen1(struct rsnd_mod *mod,
rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id));
- return rsnd_src_start(mod, rdai);
+ return rsnd_src_start(mod);
}
static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
@@ -484,7 +495,7 @@ static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0);
- return rsnd_src_stop(mod, rdai);
+ return rsnd_src_stop(mod);
}
static struct rsnd_mod_ops rsnd_src_gen1_ops = {
@@ -507,16 +518,17 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
+ u32 convert_rate = rsnd_src_convert_rate(src);
uint ratio;
int ret;
/* 6 - 1/6 are very enough ratio for SRC_BSDSR */
- if (!rsnd_src_convert_rate(src))
+ if (!convert_rate)
ratio = 0;
- else if (rsnd_src_convert_rate(src) > runtime->rate)
- ratio = 100 * rsnd_src_convert_rate(src) / runtime->rate;
+ else if (convert_rate > runtime->rate)
+ ratio = 100 * convert_rate / runtime->rate;
else
- ratio = 100 * runtime->rate / rsnd_src_convert_rate(src);
+ ratio = 100 * runtime->rate / convert_rate;
if (ratio > 600) {
dev_err(dev, "FSO/FSI ratio error\n");
@@ -529,6 +541,11 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
rsnd_mod_write(mod, SRC_SRCCR, 0x00011110);
+ if (convert_rate) {
+ /* Gen1/Gen2 are not compatible */
+ rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
+ }
+
switch (rsnd_mod_id(mod)) {
case 5:
case 6:
@@ -578,9 +595,11 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
rsnd_info_is_playback(priv, src),
src->info->dma_id);
if (ret < 0)
- dev_err(dev, "SRC DMA failed\n");
-
- dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
+ dev_err(dev, "%s[%d] (Gen2) failed\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
+ else
+ dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
return ret;
}
@@ -624,7 +643,7 @@ static int rsnd_src_start_gen2(struct rsnd_mod *mod,
rsnd_mod_write(mod, SRC_CTRL, val);
- return rsnd_src_start(mod, rdai);
+ return rsnd_src_start(mod);
}
static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
@@ -636,7 +655,7 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
rsnd_dma_stop(rsnd_mod_to_dma(&src->mod));
- return rsnd_src_stop(mod, rdai);
+ return rsnd_src_stop(mod);
}
static struct rsnd_mod_ops rsnd_src_gen2_ops = {
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 34e84009162b..3844fbef4664 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -68,7 +68,6 @@ struct rsnd_ssi {
struct rsnd_dai *rdai;
u32 cr_own;
u32 cr_clk;
- u32 cr_etc;
int err;
unsigned int usrcnt;
unsigned int rate;
@@ -83,7 +82,7 @@ struct rsnd_ssi {
#define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
-#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0)
+#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
#define rsnd_ssi_dma_available(ssi) \
rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod))
#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
@@ -96,6 +95,9 @@ static int rsnd_ssi_use_busif(struct rsnd_mod *mod)
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
int use_busif = 0;
+ if (!rsnd_ssi_is_dma_mode(mod))
+ return 0;
+
if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF))
use_busif = 1;
if (rsnd_io_to_mod_src(io))
@@ -159,7 +161,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
ssi->cr_clk = FORCE | SWL_32 |
SCKD | SWSD | CKDV(j);
- dev_dbg(dev, "ssi%d outputs %u Hz\n",
+ dev_dbg(dev, "%s[%d] outputs %u Hz\n",
+ rsnd_mod_name(&ssi->mod),
rsnd_mod_id(&ssi->mod), rate);
return 0;
@@ -184,6 +187,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
{
struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
struct device *dev = rsnd_priv_to_dev(priv);
+ u32 cr_mode;
u32 cr;
if (0 == ssi->usrcnt) {
@@ -197,16 +201,29 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
}
}
+ cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ?
+ DMEN : /* DMA : enable DMA */
+ DIEN; /* PIO : enable Data interrupt */
+
+
cr = ssi->cr_own |
ssi->cr_clk |
- ssi->cr_etc |
- EN;
+ cr_mode |
+ UIEN | OIEN | EN;
rsnd_mod_write(&ssi->mod, SSICR, cr);
+ /* enable WS continue */
+ if (rsnd_dai_is_clk_master(rdai))
+ rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+
+ /* clear error status */
+ rsnd_mod_write(&ssi->mod, SSISR, 0);
+
ssi->usrcnt++;
- dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod));
+ dev_dbg(dev, "%s[%d] hw started\n",
+ rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
}
static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
@@ -249,7 +266,8 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
clk_disable_unprepare(ssi->clk);
}
- dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod));
+ dev_dbg(dev, "%s[%d] hw stopped\n",
+ rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
}
/*
@@ -334,25 +352,54 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
}
}
-/*
- * SSI PIO
- */
-static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
+static int rsnd_ssi_start(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+
+ rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
+
+ rsnd_ssi_hw_start(ssi, rdai, io);
+
+ rsnd_src_ssi_irq_enable(mod, rdai);
+
+ return 0;
+}
+
+static int rsnd_ssi_stop(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+ rsnd_src_ssi_irq_disable(mod, rdai);
+
+ rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
+
+ rsnd_ssi_hw_stop(ssi, rdai);
+
+ rsnd_src_ssiu_stop(mod, rdai);
+
+ return 0;
+}
+
+static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
{
struct rsnd_ssi *ssi = data;
+ struct rsnd_dai *rdai = ssi->rdai;
struct rsnd_mod *mod = &ssi->mod;
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
u32 status = rsnd_mod_read(mod, SSISR);
- irqreturn_t ret = IRQ_NONE;
- if (io && (status & DIRQ)) {
- struct rsnd_dai *rdai = ssi->rdai;
+ if (!io)
+ return IRQ_NONE;
+
+ /* PIO only */
+ if (status & DIRQ) {
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
u32 *buf = (u32 *)(runtime->dma_area +
rsnd_dai_pointer_offset(io, 0));
- rsnd_ssi_record_error(ssi, status);
-
/*
* 8/16/32 data can be assesse to TDR/RDR register
* directly as 32bit data
@@ -364,73 +411,60 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
*buf = rsnd_mod_read(mod, SSIRDR);
rsnd_dai_pointer_update(io, sizeof(*buf));
+ }
+
+ /* PIO / DMA */
+ if (status & (UIRQ | OIRQ)) {
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ /*
+ * restart SSI
+ */
+ rsnd_ssi_stop(mod, rdai);
+ rsnd_ssi_start(mod, rdai);
- ret = IRQ_HANDLED;
+ dev_dbg(dev, "%s[%d] restart\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
}
- return ret;
+ rsnd_ssi_record_error(ssi, status);
+
+ return IRQ_HANDLED;
}
+/*
+ * SSI PIO
+ */
static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
struct rsnd_dai *rdai)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- int irq = ssi->info->pio_irq;
int ret;
- ret = devm_request_irq(dev, irq,
- rsnd_ssi_pio_interrupt,
+ ret = devm_request_irq(dev, ssi->info->irq,
+ rsnd_ssi_interrupt,
IRQF_SHARED,
dev_name(dev), ssi);
if (ret)
- dev_err(dev, "SSI request interrupt failed\n");
-
- dev_dbg(dev, "%s (PIO) is probed\n", rsnd_mod_name(mod));
+ dev_err(dev, "%s[%d] (PIO) request interrupt failed\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
+ else
+ dev_dbg(dev, "%s[%d] (PIO) is probed\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
return ret;
}
-static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
- struct rsnd_dai *rdai)
-{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
-
- /* enable PIO IRQ */
- ssi->cr_etc = UIEN | OIEN | DIEN;
-
- rsnd_src_ssiu_start(mod, rdai, 0);
-
- rsnd_src_enable_ssi_irq(mod, rdai);
-
- rsnd_ssi_hw_start(ssi, rdai, io);
-
- return 0;
-}
-
-static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
- struct rsnd_dai *rdai)
-{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-
- ssi->cr_etc = 0;
-
- rsnd_ssi_hw_stop(ssi, rdai);
-
- rsnd_src_ssiu_stop(mod, rdai, 0);
-
- return 0;
-}
-
static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
.name = SSI_NAME,
.probe = rsnd_ssi_pio_probe,
.init = rsnd_ssi_init,
.quit = rsnd_ssi_quit,
- .start = rsnd_ssi_pio_start,
- .stop = rsnd_ssi_pio_stop,
+ .start = rsnd_ssi_start,
+ .stop = rsnd_ssi_stop,
};
static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
@@ -442,15 +476,28 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
int dma_id = ssi->info->dma_id;
int ret;
+ ret = devm_request_irq(dev, ssi->info->irq,
+ rsnd_ssi_interrupt,
+ IRQF_SHARED,
+ dev_name(dev), ssi);
+ if (ret)
+ goto rsnd_ssi_dma_probe_fail;
+
ret = rsnd_dma_init(
priv, rsnd_mod_to_dma(mod),
rsnd_info_is_playback(priv, ssi),
dma_id);
+ if (ret)
+ goto rsnd_ssi_dma_probe_fail;
- if (ret < 0)
- dev_err(dev, "SSI DMA failed\n");
+ dev_dbg(dev, "%s[%d] (DMA) is probed\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+ return ret;
- dev_dbg(dev, "%s (DMA) is probed\n", rsnd_mod_name(mod));
+rsnd_ssi_dma_probe_fail:
+ dev_err(dev, "%s[%d] (DMA) is failed\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
return ret;
}
@@ -458,30 +505,48 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
struct rsnd_dai *rdai)
{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int irq = ssi->info->irq;
+
rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod));
+ /* PIO will request IRQ again */
+ devm_free_irq(dev, irq, ssi);
+
return 0;
}
-static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
- struct rsnd_dai *rdai)
+static int rsnd_ssi_fallback(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai)
{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
- /* enable DMA transfer */
- ssi->cr_etc = DMEN;
+ /*
+ * fallback to PIO
+ *
+ * SSI .probe might be called again.
+ * see
+ * rsnd_rdai_continuance_probe()
+ */
+ mod->ops = &rsnd_ssi_pio_ops;
- rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
+ dev_info(dev, "%s[%d] fallback to PIO mode\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
- rsnd_dma_start(dma);
+ return 0;
+}
- rsnd_ssi_hw_start(ssi, ssi->rdai, io);
+static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai)
+{
+ struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
- /* enable WS continue */
- if (rsnd_dai_is_clk_master(rdai))
- rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+ rsnd_ssi_start(mod, rdai);
+
+ rsnd_dma_start(dma);
return 0;
}
@@ -489,18 +554,11 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
struct rsnd_dai *rdai)
{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
-
- ssi->cr_etc = 0;
-
- rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
-
- rsnd_ssi_hw_stop(ssi, rdai);
+ struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
rsnd_dma_stop(dma);
- rsnd_src_ssiu_stop(mod, rdai, 1);
+ rsnd_ssi_stop(mod, rdai);
return 0;
}
@@ -519,8 +577,15 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
.quit = rsnd_ssi_quit,
.start = rsnd_ssi_dma_start,
.stop = rsnd_ssi_dma_stop,
+ .fallback = rsnd_ssi_fallback,
};
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
+{
+ return mod->ops == &rsnd_ssi_dma_ops;
+}
+
+
/*
* Non SSI
*/
@@ -614,7 +679,7 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
/*
* irq
*/
- ssi_info->pio_irq = irq_of_parse_and_map(np, 0);
+ ssi_info->irq = irq_of_parse_and_map(np, 0);
/*
* DMA