[alsa-devel] [PATCH 01/17] Update MAINTAINERS for ALSA SoC
Add myself as a point of contact for the ALSA SoC subsystem and add a reference to the development GIT tree.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com Signed-off-by: Liam Girdwood lg@opensource.wolfsonmicro.com --- MAINTAINERS | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS index b4f611c..10c3af3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3514,6 +3514,9 @@ S: Maintained SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT P: Liam Girdwood M: liam.girdwood@wolfsonmicro.com +P: Mark Brown +M: broonie@opensource.wolfsonmicro.com +T: git opensource.wolfsonmicro.com/linux-2.6-asoc L: alsa-devel@alsa-project.org (subscribers-only) S: Supported
From: Joe Sauer jsauer@vernier.com
Signed-off-by: Joe Sauer jsauer@vernier.com Signed-off-by: Liam Girdwood lg@opensource.wolfsonmicro.com Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm9712.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 986b5d5..111c266 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -102,7 +102,7 @@ SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0), SOC_SINGLE("Speaker Playback Invert Switch", AC97_MASTER, 6, 1, 0), SOC_SINGLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 7, 1, 0), SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_MONO, 7, 1, 0), -SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 0), +SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1),
SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0), SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0), @@ -131,7 +131,7 @@ SOC_SINGLE("Aux Playback Headphone Volume", AC97_CD, 12, 7, 1), SOC_SINGLE("Aux Playback Speaker Volume", AC97_CD, 8, 7, 1), SOC_SINGLE("Aux Playback Phone Volume", AC97_CD, 4, 7, 1),
-SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 0), +SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 1), SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1),
SOC_SINGLE("Capture 20dB Boost Switch", AC97_REC_SEL, 14, 1, 0), @@ -145,8 +145,8 @@ SOC_ENUM("Bass Control", wm9712_enum[5]), SOC_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1), SOC_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1), SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0), -SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 0), -SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 0), +SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 1), +SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 1),
SOC_SINGLE("Capture ADC Switch", AC97_REC_GAIN, 15, 1, 1), SOC_ENUM("Capture Volume Steps", wm9712_enum[6]),
From: Liam Girdwood lg@opensource.wolfsonmicro.com
Added a device level dapm event so that both the machine and codec are informed when dapm events occur.
Signed-off-by: Liam Girdwood lg@opensource.wolfsonmicro.com Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- include/sound/soc-dapm.h | 1 + include/sound/soc.h | 3 +++ sound/soc/soc-core.c | 23 +++++++++++------------ sound/soc/soc-dapm.c | 23 +++++++++++++++++++++++ 4 files changed, 38 insertions(+), 12 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 2b1ae8e..db02159 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -199,6 +199,7 @@ void snd_soc_dapm_free(struct snd_soc_device *socdev); /* dapm events */ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream, int event); +int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event);
/* dapm sys fs - used by the core */ int snd_soc_dapm_sys_add(struct device *dev); diff --git a/include/sound/soc.h b/include/sound/soc.h index f47ef1f..000f3e7 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -426,6 +426,9 @@ struct snd_soc_machine { int (*resume_pre)(struct platform_device *pdev); int (*resume_post)(struct platform_device *pdev);
+ /* callbacks */ + int (*dapm_event)(struct snd_soc_machine *, int event); + /* CPU <--> Codec DAI links */ struct snd_soc_dai_link *dai_link; int num_links; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index e6a67b5..aaf141c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -289,15 +289,16 @@ static void close_delayed_work(struct work_struct *work) if (codec_dai->pop_wait == 1) {
codec_dai->pop_wait = 0; - snd_soc_dapm_stream_event(codec, codec_dai->playback.stream_name, + snd_soc_dapm_stream_event(codec, + codec_dai->playback.stream_name, SND_SOC_DAPM_STREAM_STOP);
/* power down the codec power domain if no longer active */ if (codec->active == 0) { dbg("pop wq D3 %s %s\n", codec->name, codec_dai->playback.stream_name); - if (codec->dapm_event) - codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot); + snd_soc_dapm_device_event(socdev, + SNDRV_CTL_POWER_D3hot); } } } @@ -353,12 +354,12 @@ static int soc_codec_close(struct snd_pcm_substream *substream) } else { /* capture streams can be powered down now */ snd_soc_dapm_stream_event(codec, - codec_dai->capture.stream_name, SND_SOC_DAPM_STREAM_STOP); + codec_dai->capture.stream_name, + SND_SOC_DAPM_STREAM_STOP);
- if (codec->active == 0 && codec_dai->pop_wait == 0){ - if (codec->dapm_event) - codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot); - } + if (codec->active == 0 && codec_dai->pop_wait == 0) + snd_soc_dapm_device_event(socdev, + SNDRV_CTL_POWER_D3hot); }
mutex_unlock(&pcm_mutex); @@ -433,8 +434,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) /* no delayed work - do we need to power up codec */ if (codec->dapm_state != SNDRV_CTL_POWER_D0) {
- if (codec->dapm_event) - codec->dapm_event(codec, SNDRV_CTL_POWER_D1); + snd_soc_dapm_device_event(socdev, SNDRV_CTL_POWER_D1);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) snd_soc_dapm_stream_event(codec, @@ -445,8 +445,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) codec_dai->capture.stream_name, SND_SOC_DAPM_STREAM_START);
- if (codec->dapm_event) - codec->dapm_event(codec, SNDRV_CTL_POWER_D0); + snd_soc_dapm_device_event(socdev, SNDRV_CTL_POWER_D0); if (codec_dai->dai_ops.digital_mute) codec_dai->dai_ops.digital_mute(codec_dai, 0);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 29a546f..0a9d192 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1280,6 +1280,29 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
/** + * snd_soc_dapm_device_event - send a device event to the dapm core + * @socdev: audio device + * @event: device event + * + * Sends a device event to the dapm core. The core then makes any + * necessary machine or codec power changes.. + * + * Returns 0 for success else error. + */ +int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event) +{ + struct snd_soc_codec *codec = socdev->codec; + struct snd_soc_machine *machine = socdev->machine; + + if (machine->dapm_event) + machine->dapm_event(machine, event); + if (codec->dapm_event) + codec->dapm_event(codec, event); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_device_event); + +/** * snd_soc_dapm_set_endpoint - set audio endpoint status * @codec: audio codec * @endpoint: audio signal endpoint (or start point)
From: Philipp Zabel philipp.zabel@gmail.com
Signed-off-by: Philipp Zabel philipp.zabel@gmail.com Signed-off-by: Liam Girdwood lg@opensource.wolfsonmicro.com --- include/sound/soc-dapm.h | 24 ++++++++++++++--- include/sound/soc.h | 58 +++++++++++++++++++++++++++++++++------- sound/soc/soc-core.c | 66 ++++++++++++++++++++++++++------------------- sound/soc/soc-dapm.c | 14 +++++---- 4 files changed, 114 insertions(+), 48 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index db02159..335d73c 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -131,18 +131,34 @@ .shift = wshift, .invert = winvert}
/* dapm kcontrol types */ -#define SOC_DAPM_SINGLE(xname, reg, shift, mask, invert) \ +#define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, mask, invert) } -#define SOC_DAPM_DOUBLE(xname, reg, shift_left, shift_right, mask, invert, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } +#define SOC_DAPM_DOUBLE(xname, reg, shift_left, shift_right, max, invert, \ power) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ .info = snd_soc_info_volsw, \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ .private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) |\ - ((mask) << 16) | ((invert) << 24) } + ((max) << 16) | ((invert) << 24) } +#define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } +#define SOC_DAPM_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, \ + power, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ + .private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) |\ + ((max) << 16) | ((invert) << 24) } #define SOC_DAPM_ENUM(xname, xenum) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_enum_double, \ diff --git a/include/sound/soc.h b/include/sound/soc.h index 000f3e7..c69b58c 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -27,27 +27,53 @@ /* * Convenience kcontrol builders */ -#define SOC_SINGLE_VALUE(reg,shift,mask,invert) ((reg) | ((shift) << 8) |\ - ((shift) << 12) | ((mask) << 16) | ((invert) << 24)) -#define SOC_SINGLE_VALUE_EXT(reg,mask,invert) ((reg) | ((mask) << 16) |\ +#define SOC_SINGLE_VALUE(reg, shift, max, invert) ((reg) | ((shift) << 8) |\ + ((shift) << 12) | ((max) << 16) | ((invert) << 24)) +#define SOC_SINGLE_VALUE_EXT(reg, max, invert) ((reg) | ((max) << 16) |\ ((invert) << 31)) -#define SOC_SINGLE(xname, reg, shift, mask, invert) \ +#define SOC_SINGLE(xname, reg, shift, max, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ .put = snd_soc_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, mask, invert) } -#define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } +#define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ + .put = snd_soc_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } +#define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ .put = snd_soc_put_volsw, \ .private_value = (reg) | ((shift_left) << 8) | \ - ((shift_right) << 12) | ((mask) << 16) | ((invert) << 24) } -#define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, mask, invert) \ + ((shift_right) << 12) | ((max) << 16) | ((invert) << 24) } +#define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, max, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ .info = snd_soc_info_volsw_2r, \ .get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \ .private_value = (reg_left) | ((shift) << 8) | \ - ((mask) << 12) | ((invert) << 20) | ((reg_right) << 24) } + ((max) << 12) | ((invert) << 20) | ((reg_right) << 24) } +#define SOC_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ + .put = snd_soc_put_volsw, \ + .private_value = (reg) | ((shift_left) << 8) | \ + ((shift_right) << 12) | ((max) << 16) | ((invert) << 24) } +#define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, shift, max, invert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw_2r, \ + .get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \ + .private_value = (reg_left) | ((shift) << 8) | \ + ((max) << 12) | ((invert) << 20) | ((reg_right) << 24) } #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \ { .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ .mask = xmask, .texts = xtexts } @@ -105,9 +131,21 @@ #define SND_SOC_DAIFMT_GATED (1 << 4) /* clock is gated when not Tx/Rx */
/* + * DAI Sync + * Synchronous LR (Left Right) clocks and Frame signals. + */ +#define SND_SOC_DAIFMT_SYNC (0 << 5) /* Tx FRM = Rx FRM */ +#define SND_SOC_DAIFMT_ASYNC (1 << 5) /* Tx FRM ~ Rx FRM */ + +/* + * TDM + */ +#define SND_SOC_DAIFMT_TDM (1 << 6) + +/* * DAI hardware signal inversions */ -#define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bit clock + frame */ +#define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bclk + frm */ #define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal bclk + inv frm */ #define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert bclk + nor frm */ #define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert bclk + frm */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index aaf141c..0e361d5 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1214,7 +1214,6 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, memcpy(&template, _template, sizeof(template)); if (long_name) template.name = long_name; - template.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; template.index = 0;
return snd_ctl_new1(&template, data); @@ -1349,13 +1348,16 @@ EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext); int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - int mask = kcontrol->private_value; + int max = kcontrol->private_value; + + if (max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->type = - mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = mask; + uinfo->value.integer.max = max; return 0; } EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext); @@ -1372,15 +1374,18 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext); int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - int mask = (kcontrol->private_value >> 16) & 0xff; + int max = (kcontrol->private_value >> 16) & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; int rshift = (kcontrol->private_value >> 12) & 0x0f;
- uinfo->type = - mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + if (max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = shift == rshift ? 1 : 2; uinfo->value.integer.min = 0; - uinfo->value.integer.max = mask; + uinfo->value.integer.max = max; return 0; } EXPORT_SYMBOL_GPL(snd_soc_info_volsw); @@ -1401,7 +1406,8 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; int rshift = (kcontrol->private_value >> 12) & 0x0f; - int mask = (kcontrol->private_value >> 16) & 0xff; + int max = (kcontrol->private_value >> 16) & 0xff; + int mask = (1 << fls(max)) - 1; int invert = (kcontrol->private_value >> 24) & 0x01;
ucontrol->value.integer.value[0] = @@ -1411,10 +1417,10 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, (snd_soc_read(codec, reg) >> rshift) & mask; if (invert) { ucontrol->value.integer.value[0] = - mask - ucontrol->value.integer.value[0]; + max - ucontrol->value.integer.value[0]; if (shift != rshift) ucontrol->value.integer.value[1] = - mask - ucontrol->value.integer.value[1]; + max - ucontrol->value.integer.value[1]; }
return 0; @@ -1437,25 +1443,24 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; int rshift = (kcontrol->private_value >> 12) & 0x0f; - int mask = (kcontrol->private_value >> 16) & 0xff; + int max = (kcontrol->private_value >> 16) & 0xff; + int mask = (1 << fls(max)) - 1; int invert = (kcontrol->private_value >> 24) & 0x01; - int err; unsigned short val, val2, val_mask;
val = (ucontrol->value.integer.value[0] & mask); if (invert) - val = mask - val; + val = max - val; val_mask = mask << shift; val = val << shift; if (shift != rshift) { val2 = (ucontrol->value.integer.value[1] & mask); if (invert) - val2 = mask - val2; + val2 = max - val2; val_mask |= mask << rshift; val |= val2 << rshift; } - err = snd_soc_update_bits(codec, reg, val_mask, val); - return err; + return snd_soc_update_bits(codec, reg, val_mask, val); } EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
@@ -1472,13 +1477,16 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw); int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - int mask = (kcontrol->private_value >> 12) & 0xff; + int max = (kcontrol->private_value >> 12) & 0xff; + + if (max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->type = - mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; - uinfo->value.integer.max = mask; + uinfo->value.integer.max = max; return 0; } EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r); @@ -1499,7 +1507,8 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol, int reg = kcontrol->private_value & 0xff; int reg2 = (kcontrol->private_value >> 24) & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; - int mask = (kcontrol->private_value >> 12) & 0xff; + int max = (kcontrol->private_value >> 12) & 0xff; + int mask = (1<<fls(max))-1; int invert = (kcontrol->private_value >> 20) & 0x01;
ucontrol->value.integer.value[0] = @@ -1508,9 +1517,9 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol, (snd_soc_read(codec, reg2) >> shift) & mask; if (invert) { ucontrol->value.integer.value[0] = - mask - ucontrol->value.integer.value[0]; + max - ucontrol->value.integer.value[0]; ucontrol->value.integer.value[1] = - mask - ucontrol->value.integer.value[1]; + max - ucontrol->value.integer.value[1]; }
return 0; @@ -1533,7 +1542,8 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, int reg = kcontrol->private_value & 0xff; int reg2 = (kcontrol->private_value >> 24) & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; - int mask = (kcontrol->private_value >> 12) & 0xff; + int max = (kcontrol->private_value >> 12) & 0xff; + int mask = (1 << fls(max)) - 1; int invert = (kcontrol->private_value >> 20) & 0x01; int err; unsigned short val, val2, val_mask; @@ -1543,8 +1553,8 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, val2 = (ucontrol->value.integer.value[1] & mask);
if (invert) { - val = mask - val; - val2 = mask - val2; + val = max - val; + val2 = max - val2; }
val = val << shift; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 0a9d192..d5d3c17 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1019,8 +1019,9 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; int rshift = (kcontrol->private_value >> 12) & 0x0f; - int mask = (kcontrol->private_value >> 16) & 0xff; + int max = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0x01; + int mask = (1 << fls(max)) - 1;
/* return the saved value if we are powered down */ if (widget->id == snd_soc_dapm_pga && !widget->power) { @@ -1035,10 +1036,10 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, (snd_soc_read(widget->codec, reg) >> rshift) & mask; if (invert) { ucontrol->value.integer.value[0] = - mask - ucontrol->value.integer.value[0]; + max - ucontrol->value.integer.value[0]; if (shift != rshift) ucontrol->value.integer.value[1] = - mask - ucontrol->value.integer.value[1]; + max - ucontrol->value.integer.value[1]; }
return 0; @@ -1061,7 +1062,8 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; int rshift = (kcontrol->private_value >> 12) & 0x0f; - int mask = (kcontrol->private_value >> 16) & 0xff; + int max = (kcontrol->private_value >> 16) & 0xff; + int mask = (1 << fls(max)) - 1; int invert = (kcontrol->private_value >> 24) & 0x01; unsigned short val, val2, val_mask; int ret; @@ -1069,13 +1071,13 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, val = (ucontrol->value.integer.value[0] & mask);
if (invert) - val = mask - val; + val = max - val; val_mask = mask << shift; val = val << shift; if (shift != rshift) { val2 = (ucontrol->value.integer.value[1] & mask); if (invert) - val2 = mask - val2; + val2 = max - val2; val_mask |= mask << rshift; val |= val2 << rshift; }
From: Liam Girdwood lg@opensource.wolfsonmicro.com
Signed-off-by: Liam Girdwood lg@opensource.wolfsonmicro.com --- sound/soc/soc-core.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0e361d5..b159734 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -288,6 +288,14 @@ static void close_delayed_work(struct work_struct *work) /* are we waiting on this codec DAI stream */ if (codec_dai->pop_wait == 1) {
+ /* power down the codec to D1 if no longer active */ + if (codec->active == 0) { + dbg("pop wq D1 %s %s\n", codec->name, + codec_dai->playback.stream_name); + snd_soc_dapm_device_event(socdev, + SNDRV_CTL_POWER_D1); + } + codec_dai->pop_wait = 0; snd_soc_dapm_stream_event(codec, codec_dai->playback.stream_name,
From: Liam Girdwood lg@opensource.wolfsonmicro.com
This fixes a bug whereby PCMs were not being suspended when the rest of the audio subsystem was suspended.
Signed-off-by: Liam Girdwood lg@opensource.wolfsonmicro.com --- include/sound/soc.h | 3 +++ sound/soc/soc-core.c | 5 +++++ 2 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/include/sound/soc.h b/include/sound/soc.h index c69b58c..e32fa2f 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -448,6 +448,9 @@ struct snd_soc_dai_link {
/* codec/machine specific init - e.g. add machine controls */ int (*init)(struct snd_soc_codec *codec); + + /* DAI pcm */ + struct snd_pcm *pcm; };
/* SoC machine */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b159734..680dea9 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -646,6 +646,10 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) dai->dai_ops.digital_mute(dai, 1); }
+ /* suspend all pcms */ + for (i = 0; i < machine->num_links; i++) + snd_pcm_suspend_all(machine->dai_link[i].pcm); + if (machine->suspend_pre) machine->suspend_pre(pdev, state);
@@ -880,6 +884,7 @@ static int soc_new_pcm(struct snd_soc_device *socdev, return ret; }
+ dai_link->pcm = pcm; pcm->private_data = rtd; soc_pcm_ops.mmap = socdev->platform->pcm_ops->mmap; soc_pcm_ops.pointer = socdev->platform->pcm_ops->pointer;
From: Milan plzik milan.plzik@gmail.com
Signed-off-by: Milan plzik milan.plzik@gmail.com Signed-off-by: Liam Girdwood lg@opensource.wolfsonmicro.com --- sound/soc/soc-dapm.c | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d5d3c17..e8d7fae 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -692,7 +692,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, return 0; }
-/* test and update the power status of a mixer widget */ +/* test and update the power status of a mixer or switch widget */ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kcontrol, int reg, int val_mask, int val, int invert) @@ -700,7 +700,8 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, struct snd_soc_dapm_path *path; int found = 0;
- if (widget->id != snd_soc_dapm_mixer) + if (widget->id != snd_soc_dapm_mixer && + widget->id != snd_soc_dapm_switch) return -ENODEV;
if (!snd_soc_test_bits(widget->codec, reg, val_mask, val))
From: Liam Girdwood lg@opensource.wolfsonmicro.com
Signed-off-by: Liam Girdwood lg@opensource.wolfsonmicro.com --- sound/soc/soc-dapm.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index e8d7fae..4a5bd85 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1298,9 +1298,9 @@ int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event) struct snd_soc_machine *machine = socdev->machine;
if (machine->dapm_event) - machine->dapm_event(machine, event); + machine->dapm_event(machine, event); if (codec->dapm_event) - codec->dapm_event(codec, event); + codec->dapm_event(codec, event); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_device_event);
From: Liam Girdwood lg@opensource.wolfsonmicro.com
Signed-off-by: Laim Girdwood lg@opensource.wolfsonmicro.com --- include/sound/soc-dapm.h | 2 +- sound/soc/soc-dapm.c | 40 +++++++++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 14 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 335d73c..0df89bb 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -289,7 +289,7 @@ struct snd_soc_dapm_widget {
/* external events */ unsigned short event_flags; /* flags to specify event types */ - int (*event)(struct snd_soc_dapm_widget*, int); + int (*event)(struct snd_soc_dapm_widget*, struct snd_kcontrol *, int);
/* kcontrols that relate to this widget */ int num_kcontrols; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 4a5bd85..a4ce882 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -524,11 +524,13 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) continue;
if (event == SND_SOC_DAPM_STREAM_START) { - ret = w->event(w, SND_SOC_DAPM_PRE_PMU); + ret = w->event(w, + NULL, SND_SOC_DAPM_PRE_PMU); if (ret < 0) return ret; } else if (event == SND_SOC_DAPM_STREAM_STOP) { - ret = w->event(w, SND_SOC_DAPM_PRE_PMD); + ret = w->event(w, + NULL, SND_SOC_DAPM_PRE_PMD); if (ret < 0) return ret; } @@ -539,11 +541,13 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) continue;
if (event == SND_SOC_DAPM_STREAM_START) { - ret = w->event(w, SND_SOC_DAPM_POST_PMU); + ret = w->event(w, + NULL, SND_SOC_DAPM_POST_PMU); if (ret < 0) return ret; } else if (event == SND_SOC_DAPM_STREAM_STOP) { - ret = w->event(w, SND_SOC_DAPM_POST_PMD); + ret = w->event(w, + NULL, SND_SOC_DAPM_POST_PMD); if (ret < 0) return ret; } @@ -567,26 +571,30 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) if (power) { /* power up event */ if (w->event_flags & SND_SOC_DAPM_PRE_PMU) { - ret = w->event(w, SND_SOC_DAPM_PRE_PMU); + ret = w->event(w, + NULL, SND_SOC_DAPM_PRE_PMU); if (ret < 0) return ret; } dapm_update_bits(w); if (w->event_flags & SND_SOC_DAPM_POST_PMU){ - ret = w->event(w, SND_SOC_DAPM_POST_PMU); + ret = w->event(w, + NULL, SND_SOC_DAPM_POST_PMU); if (ret < 0) return ret; } } else { /* power down event */ if (w->event_flags & SND_SOC_DAPM_PRE_PMD) { - ret = w->event(w, SND_SOC_DAPM_PRE_PMD); + ret = w->event(w, + NULL, SND_SOC_DAPM_PRE_PMD); if (ret < 0) return ret; } dapm_update_bits(w); if (w->event_flags & SND_SOC_DAPM_POST_PMD) { - ret = w->event(w, SND_SOC_DAPM_POST_PMD); + ret = w->event(w, + NULL, SND_SOC_DAPM_POST_PMD); if (ret < 0) return ret; } @@ -1096,13 +1104,17 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert); if (widget->event) { if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { - ret = widget->event(widget, SND_SOC_DAPM_PRE_REG); - if (ret < 0) + ret = widget->event(widget, kcontrol, + SND_SOC_DAPM_PRE_REG); + if (ret < 0) { + ret = 1; goto out; + } } ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); if (widget->event_flags & SND_SOC_DAPM_POST_REG) - ret = widget->event(widget, SND_SOC_DAPM_POST_REG); + ret = widget->event(widget, kcontrol, + SND_SOC_DAPM_POST_REG); } else ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
@@ -1177,13 +1189,15 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, dapm_mux_update_power(widget, kcontrol, mask, mux, e); if (widget->event) { if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { - ret = widget->event(widget, SND_SOC_DAPM_PRE_REG); + ret = widget->event(widget, + kcontrol, SND_SOC_DAPM_PRE_REG); if (ret < 0) goto out; } ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); if (widget->event_flags & SND_SOC_DAPM_POST_REG) - ret = widget->event(widget, SND_SOC_DAPM_POST_REG); + ret = widget->event(widget, + kcontrol, SND_SOC_DAPM_POST_REG); } else ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
snd_soc_dapm_new_widgets() takes the codec lock when adding new widgets, causing lockdep warnings when applications later call down through ALSA to adjust controls. Since widgets are only added during probe this lock should be unneeded so don't take it.
Thanks to Dmitry Baryshkov dbaryshkov@gmail.com for reporting this issue.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com Cc: Dmitry Baryshkov dbaryshkov@gmail.com --- sound/soc/soc-dapm.c | 2 -- 1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index a4ce882..f99cac1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -972,7 +972,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) { struct snd_soc_dapm_widget *w;
- mutex_lock(&codec->mutex); list_for_each_entry(w, &codec->dapm_widgets, list) { if (w->new) @@ -1007,7 +1006,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) }
dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP); - mutex_unlock(&codec->mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- include/sound/soc.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/include/sound/soc.h b/include/sound/soc.h index e32fa2f..1c22931 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -22,7 +22,7 @@ #include <sound/control.h> #include <sound/ac97_codec.h>
-#define SND_SOC_VERSION "0.13.1" +#define SND_SOC_VERSION "0.13.2"
/* * Convenience kcontrol builders
From: Liam Girdwood lg@opensource.wolfsonmicro.com
Signed-off-by: Liam Girdwood lg@opensource.wolfsonmicro.com Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm8753.c | 7 +++++-- 1 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index efced93..ba16375 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -48,6 +48,7 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/initval.h> +#include <sound/tlv.h> #include <asm/div64.h>
#include "wm8753.h" @@ -258,6 +259,8 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol, return 1; }
+static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600); + static const struct snd_kcontrol_new wm8753_snd_controls[] = { SOC_DOUBLE_R("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0),
@@ -287,8 +290,8 @@ SOC_SINGLE("Bass Volume", WM8753_BASS, 0, 15, 1), SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 15, 1), SOC_ENUM("Treble Cut-off", wm8753_enum[2]),
-SOC_DOUBLE("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1), -SOC_SINGLE("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1), +SOC_DOUBLE_TLV("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1, rec_mix_tlv), +SOC_SINGLE_TLV("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1, rec_mix_tlv),
SOC_DOUBLE_R("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0), SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0),
From: Graeme Gregory graeme@openmoko.com
This one changes the DMA initialisation as it turns out the DMA driver in s3c24xx doesnt store registers between suspend/resume so you have to re-initialise the channels on every resume.
Signed-off-by: Graeme Gregory graeme@openmoko.com Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/s3c24xx/s3c24xx-pcm.c | 48 ++++++++++++++++++++------------------ 1 files changed, 25 insertions(+), 23 deletions(-)
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c index 4107a87..e9f3b60 100644 --- a/sound/soc/s3c24xx/s3c24xx-pcm.c +++ b/sound/soc/s3c24xx/s3c24xx-pcm.c @@ -49,7 +49,9 @@ static const struct snd_pcm_hardware s3c24xx_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID, + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U8 | @@ -176,28 +178,6 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream, } }
- /* channel needs configuring for mem=>device, increment memory addr, - * sync to pclk, half-word transfers to the IIS-FIFO. */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - s3c2410_dma_devconfig(prtd->params->channel, - S3C2410_DMASRC_MEM, S3C2410_DISRCC_INC | - S3C2410_DISRCC_APB, prtd->params->dma_addr); - - s3c2410_dma_config(prtd->params->channel, - prtd->params->dma_size, - S3C2410_DCON_SYNC_PCLK | - S3C2410_DCON_HANDSHAKE); - } else { - s3c2410_dma_config(prtd->params->channel, - prtd->params->dma_size, - S3C2410_DCON_HANDSHAKE | - S3C2410_DCON_SYNC_PCLK); - - s3c2410_dma_devconfig(prtd->params->channel, - S3C2410_DMASRC_HW, 0x3, - prtd->params->dma_addr); - } - s3c2410_dma_set_buffdone_fn(prtd->params->channel, s3c24xx_audio_buffdone);
@@ -246,6 +226,28 @@ static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream) if (!prtd->params) return 0;
+ /* channel needs configuring for mem=>device, increment memory addr, + * sync to pclk, half-word transfers to the IIS-FIFO. */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + s3c2410_dma_devconfig(prtd->params->channel, + S3C2410_DMASRC_MEM, S3C2410_DISRCC_INC | + S3C2410_DISRCC_APB, prtd->params->dma_addr); + + s3c2410_dma_config(prtd->params->channel, + prtd->params->dma_size, + S3C2410_DCON_SYNC_PCLK | + S3C2410_DCON_HANDSHAKE); + } else { + s3c2410_dma_config(prtd->params->channel, + prtd->params->dma_size, + S3C2410_DCON_HANDSHAKE | + S3C2410_DCON_SYNC_PCLK); + + s3c2410_dma_devconfig(prtd->params->channel, + S3C2410_DMASRC_HW, 0x3, + prtd->params->dma_addr); + } + /* flush the DMA channel */ s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH); prtd->dma_loaded = 0;
From: Graeme Gregory graeme@openmoko.com
Signed-off-by: Graeme Gregory graeme@openmoko.com Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/s3c24xx/s3c24xx-i2s.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 files changed, 38 insertions(+), 0 deletions(-)
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c index cd89c41..fee7925 100644 --- a/sound/soc/s3c24xx/s3c24xx-i2s.c +++ b/sound/soc/s3c24xx/s3c24xx-i2s.c @@ -75,6 +75,10 @@ static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_in = { struct s3c24xx_i2s_info { void __iomem *regs; struct clk *iis_clk; + u32 iiscon; + u32 iismod; + u32 iisfcon; + u32 iispsr; }; static struct s3c24xx_i2s_info s3c24xx_i2s;
@@ -405,6 +409,38 @@ static int s3c24xx_i2s_probe(struct platform_device *pdev) return 0; }
+#ifdef CONFIG_PM +int s3c24xx_i2s_suspend(struct platform_device *pdev, + struct snd_soc_cpu_dai *cpu_dai) +{ + s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); + s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); + s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); + s3c24xx_i2s.iispsr = readl(s3c24xx_i2s.regs + S3C2410_IISPSR); + + clk_disable(s3c24xx_i2s.iis_clk); + + return 0; +} + +int s3c24xx_i2s_resume(struct platform_device *pdev, + struct snd_soc_cpu_dai *cpu_dai) +{ + clk_enable(s3c24xx_i2s.iis_clk); + + writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON); + writel(s3c24xx_i2s.iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); + writel(s3c24xx_i2s.iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON); + writel(s3c24xx_i2s.iispsr, s3c24xx_i2s.regs + S3C2410_IISPSR); + + return 0; +} +#else +#define s3c24xx_i2s_suspend NULL +#define s3c24xx_i2s_resume NULL +#endif + + #define S3C24XX_I2S_RATES \ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ @@ -415,6 +451,8 @@ struct snd_soc_cpu_dai s3c24xx_i2s_dai = { .id = 0, .type = SND_SOC_DAI_I2S, .probe = s3c24xx_i2s_probe, + .suspend = s3c24xx_i2s_suspend, + .resume = s3c24xx_i2s_resume, .playback = { .channels_min = 2, .channels_max = 2,
From: Ben Dooks ben@fluff.org.uk
S3C2412 SoC IIS support for ALSA/ASoC
Signed-off-by: Ben Dooks ben-linux@fluff.org Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/s3c24xx/Kconfig | 3 + sound/soc/s3c24xx/Makefile | 2 + sound/soc/s3c24xx/s3c2412-i2s.c | 682 +++++++++++++++++++++++++++++++++++++++ sound/soc/s3c24xx/s3c2412-i2s.h | 38 +++ 4 files changed, 725 insertions(+), 0 deletions(-) create mode 100644 sound/soc/s3c24xx/s3c2412-i2s.c create mode 100644 sound/soc/s3c24xx/s3c2412-i2s.h
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig index 5632a2e..3fdedbb 100644 --- a/sound/soc/s3c24xx/Kconfig +++ b/sound/soc/s3c24xx/Kconfig @@ -10,6 +10,9 @@ config SND_S3C24XX_SOC config SND_S3C24XX_SOC_I2S tristate
+config SND_S3C2412_SOC_I2S + tristate + config SND_S3C2443_SOC_AC97 tristate select AC97_BUS diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile index 13c92f0..063e642 100644 --- a/sound/soc/s3c24xx/Makefile +++ b/sound/soc/s3c24xx/Makefile @@ -1,11 +1,13 @@ # S3c24XX Platform Support snd-soc-s3c24xx-objs := s3c24xx-pcm.o snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o +snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o
obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o +obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
# S3C24XX Machine Support snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c new file mode 100644 index 0000000..11fee8b --- /dev/null +++ b/sound/soc/s3c24xx/s3c2412-i2s.c @@ -0,0 +1,682 @@ +/* sound/soc/s3c24xx/s3c2412-i2s.c + * + * ALSA Soc Audio Layer - S3C2412 I2S driver + * + * Copyright (c) 2006 Wolfson Microelectronics PLC. + * Graeme Gregory graeme.gregory@wolfsonmicro.com + * linux@wolfsonmicro.com + * + * Copyright (c) 2007, 2004-2005 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks ben@simtec.co.uk + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. +*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/kernel.h> + +#include <sound/driver.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> +#include <asm/hardware.h> + +#include <linux/io.h> +#include <asm/dma.h> + +#include <asm/plat-s3c24xx/regs-s3c2412-iis.h> + +#include <asm/arch/regs-gpio.h> +#include <asm/arch/audio.h> +#include <asm/arch/dma.h> + +#include "s3c24xx-pcm.h" +#include "s3c2412-i2s.h" + +#define S3C2412_I2S_DEBUG 0 +#define S3C2412_I2S_DEBUG_CON 0 + +#if S3C2412_I2S_DEBUG +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) do { } while (0) +#endif + +static struct s3c2410_dma_client s3c2412_dma_client_out = { + .name = "I2S PCM Stereo out" +}; + +static struct s3c2410_dma_client s3c2412_dma_client_in = { + .name = "I2S PCM Stereo in" +}; + +static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_out = { + .client = &s3c2412_dma_client_out, + .channel = DMACH_I2S_OUT, + .dma_addr = S3C2410_PA_IIS + S3C2412_IISTXD, + .dma_size = 4, +}; + +static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_in = { + .client = &s3c2412_dma_client_in, + .channel = DMACH_I2S_IN, + .dma_addr = S3C2410_PA_IIS + S3C2412_IISRXD, + .dma_size = 4, +}; + +struct s3c2412_i2s_info { + struct device *dev; + void __iomem *regs; + struct clk *iis_clk; + struct clk *iis_pclk; + struct clk *iis_cclk; +}; + +static struct s3c2412_i2s_info s3c2412_i2s; + +#define bit_set(v, b) (((v) & (b)) ? 1 : 0) + +#if S3C2412_I2S_DEBUG_CON +static void dbg_showcon(const char *fn, u32 con) +{ + printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn, + bit_set(con, S3C2412_IISCON_LRINDEX), + bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY), + bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY), + bit_set(con, S3C2412_IISCON_TXFIFO_FULL), + bit_set(con, S3C2412_IISCON_RXFIFO_FULL)); + + printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n", + fn, + bit_set(con, S3C2412_IISCON_TXDMA_PAUSE), + bit_set(con, S3C2412_IISCON_RXDMA_PAUSE), + bit_set(con, S3C2412_IISCON_TXCH_PAUSE), + bit_set(con, S3C2412_IISCON_RXCH_PAUSE)); + printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn, + bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE), + bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE), + bit_set(con, S3C2412_IISCON_IIS_ACTIVE)); +} +#else +static inline void dbg_showcon(const char *fn, u32 con) +{ +} +#endif + +/* Turn on or off the transmission path. */ +static void s3c2412_snd_txctrl(int on) +{ + struct s3c2412_i2s_info *i2s = &s3c2412_i2s; + void __iomem *regs = i2s->regs; + u32 fic, con, mod; + + DBG("%s(%d)\n", __func__, on); + + fic = readl(regs + S3C2412_IISFIC); + con = readl(regs + S3C2412_IISCON); + mod = readl(regs + S3C2412_IISMOD); + + DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); + + if (on) { + con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; + con &= ~S3C2412_IISCON_TXDMA_PAUSE; + con &= ~S3C2412_IISCON_TXCH_PAUSE; + + switch (mod & S3C2412_IISMOD_MODE_MASK) { + case S3C2412_IISMOD_MODE_TXONLY: + case S3C2412_IISMOD_MODE_TXRX: + /* do nothing, we are in the right mode */ + break; + + case S3C2412_IISMOD_MODE_RXONLY: + mod &= ~S3C2412_IISMOD_MODE_MASK; + mod |= S3C2412_IISMOD_MODE_TXRX; + break; + + default: + dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n"); + } + + writel(con, regs + S3C2412_IISCON); + writel(mod, regs + S3C2412_IISMOD); + } else { + /* Note, we do not have any indication that the FIFO problems + * tha the S3C2410/2440 had apply here, so we should be able + * to disable the DMA and TX without resetting the FIFOS. + */ + + con |= S3C2412_IISCON_TXDMA_PAUSE; + con |= S3C2412_IISCON_TXCH_PAUSE; + con &= ~S3C2412_IISCON_TXDMA_ACTIVE; + + switch (mod & S3C2412_IISMOD_MODE_MASK) { + case S3C2412_IISMOD_MODE_TXRX: + mod &= ~S3C2412_IISMOD_MODE_MASK; + mod |= S3C2412_IISMOD_MODE_RXONLY; + break; + + case S3C2412_IISMOD_MODE_TXONLY: + mod &= ~S3C2412_IISMOD_MODE_MASK; + con &= ~S3C2412_IISCON_IIS_ACTIVE; + break; + + default: + dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n"); + } + + writel(mod, regs + S3C2412_IISMOD); + writel(con, regs + S3C2412_IISCON); + } + + fic = readl(regs + S3C2412_IISFIC); + dbg_showcon(__func__, con); + DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); +} + +static void s3c2412_snd_rxctrl(int on) +{ + struct s3c2412_i2s_info *i2s = &s3c2412_i2s; + void __iomem *regs = i2s->regs; + u32 fic, con, mod; + + DBG("%s(%d)\n", __func__, on); + + fic = readl(regs + S3C2412_IISFIC); + con = readl(regs + S3C2412_IISCON); + mod = readl(regs + S3C2412_IISMOD); + + DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); + + if (on) { + con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; + con &= ~S3C2412_IISCON_RXDMA_PAUSE; + con &= ~S3C2412_IISCON_RXCH_PAUSE; + + switch (mod & S3C2412_IISMOD_MODE_MASK) { + case S3C2412_IISMOD_MODE_TXRX: + case S3C2412_IISMOD_MODE_RXONLY: + /* do nothing, we are in the right mode */ + break; + + case S3C2412_IISMOD_MODE_TXONLY: + mod &= ~S3C2412_IISMOD_MODE_MASK; + mod |= S3C2412_IISMOD_MODE_TXRX; + break; + + default: + dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); + } + + writel(mod, regs + S3C2412_IISMOD); + writel(con, regs + S3C2412_IISCON); + } else { + /* See txctrl notes on FIFOs. */ + + con &= ~S3C2412_IISCON_RXDMA_ACTIVE; + con |= S3C2412_IISCON_RXDMA_PAUSE; + con |= S3C2412_IISCON_RXCH_PAUSE; + + switch (mod & S3C2412_IISMOD_MODE_MASK) { + case S3C2412_IISMOD_MODE_RXONLY: + con &= ~S3C2412_IISCON_IIS_ACTIVE; + mod &= ~S3C2412_IISMOD_MODE_MASK; + break; + + case S3C2412_IISMOD_MODE_TXRX: + mod &= ~S3C2412_IISMOD_MODE_MASK; + mod |= S3C2412_IISMOD_MODE_TXONLY; + break; + + default: + dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); + } + + writel(con, regs + S3C2412_IISCON); + writel(mod, regs + S3C2412_IISMOD); + } + + fic = readl(regs + S3C2412_IISFIC); + DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); +} + + +/* + * Wait for the LR signal to allow synchronisation to the L/R clock + * from the codec. May only be needed for slave mode. + */ +static int s3c2412_snd_lrsync(void) +{ + u32 iiscon; + unsigned long timeout = jiffies + msecs_to_jiffies(5); + + DBG("Entered %s\n", __func__); + + while (1) { + iiscon = readl(s3c2412_i2s.regs + S3C2412_IISCON); + if (iiscon & S3C2412_IISCON_LRINDEX) + break; + + if (timeout < jiffies) { + printk(KERN_ERR "%s: timeout\n", __func__); + return -ETIMEDOUT; + } + } + + return 0; +} + +/* + * Check whether CPU is the master or slave + */ +static inline int s3c2412_snd_is_clkmaster(void) +{ + u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); + + DBG("Entered %s\n", __func__); + + iismod &= S3C2412_IISMOD_MASTER_MASK; + return !(iismod == S3C2412_IISMOD_SLAVE); +} + +/* + * Set S3C2412 I2S DAI format + */ +static int s3c2412_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai, + unsigned int fmt) +{ + u32 iismod; + + + DBG("Entered %s\n", __func__); + + iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); + DBG("hw_params r: IISMOD: %x \n", iismod); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iismod &= ~S3C2412_IISMOD_MASTER_MASK; + iismod |= S3C2412_IISMOD_SLAVE; + break; + case SND_SOC_DAIFMT_CBS_CFS: + iismod &= ~S3C2412_IISMOD_MASTER_MASK; + iismod |= S3C2412_IISMOD_MASTER_INTERNAL; + break; + default: + DBG("unknwon master/slave format\n"); + return -EINVAL; + } + + iismod &= ~S3C2412_IISMOD_SDF_MASK; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + iismod |= S3C2412_IISMOD_SDF_MSB; + break; + case SND_SOC_DAIFMT_LEFT_J: + iismod |= S3C2412_IISMOD_SDF_LSB; + break; + case SND_SOC_DAIFMT_I2S: + iismod |= S3C2412_IISMOD_SDF_IIS; + break; + default: + DBG("Unknown data format\n"); + return -EINVAL; + } + + writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD); + DBG("hw_params w: IISMOD: %x \n", iismod); + return 0; +} + +static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + u32 iismod; + + DBG("Entered %s\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_out; + else + rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_in; + + /* Working copies of register */ + iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); + DBG("%s: r: IISMOD: %x\n", __func__, iismod); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + iismod |= S3C2412_IISMOD_8BIT; + break; + case SNDRV_PCM_FORMAT_S16_LE: + iismod &= ~S3C2412_IISMOD_8BIT; + break; + } + + writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD); + DBG("%s: w: IISMOD: %x\n", __func__, iismod); + return 0; +} + +static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); + unsigned long irqs; + int ret = 0; + + DBG("Entered %s\n", __func__); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* On start, ensure that the FIFOs are cleared and reset. */ + + writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH, + s3c2412_i2s.regs + S3C2412_IISFIC); + + /* clear again, just in case */ + writel(0x0, s3c2412_i2s.regs + S3C2412_IISFIC); + + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!s3c2412_snd_is_clkmaster()) { + ret = s3c2412_snd_lrsync(); + if (ret) + goto exit_err; + } + + local_irq_save(irqs); + + if (capture) + s3c2412_snd_rxctrl(1); + else + s3c2412_snd_txctrl(1); + + local_irq_restore(irqs); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + local_irq_save(irqs); + + if (capture) + s3c2412_snd_rxctrl(0); + else + s3c2412_snd_txctrl(0); + + local_irq_restore(irqs); + break; + default: + ret = -EINVAL; + break; + } + +exit_err: + return ret; +} + +/* default table of all avaialable root fs divisors */ +static unsigned int s3c2412_iis_fs[] = { 256, 512, 384, 768, 0 }; + +int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info, + unsigned int *fstab, + unsigned int rate, struct clk *clk) +{ + unsigned long clkrate = clk_get_rate(clk); + unsigned int div; + unsigned int fsclk; + unsigned int actual; + unsigned int fs; + unsigned int fsdiv; + signed int deviation = 0; + unsigned int best_fs = 0; + unsigned int best_div = 0; + unsigned int best_rate = 0; + unsigned int best_deviation = INT_MAX; + + + if (fstab == NULL) + fstab = s3c2412_iis_fs; + + for (fs = 0;; fs++) { + fsdiv = s3c2412_iis_fs[fs]; + + if (fsdiv == 0) + break; + + fsclk = clkrate / fsdiv; + div = fsclk / rate; + + if ((fsclk % rate) > (rate / 2)) + div++; + + if (div <= 1) + continue; + + actual = clkrate / (fsdiv * div); + deviation = actual - rate; + + printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n", + fsdiv, div, actual, deviation); + + deviation = abs(deviation); + + if (deviation < best_deviation) { + best_fs = fsdiv; + best_div = div; + best_rate = actual; + best_deviation = deviation; + } + + if (deviation == 0) + break; + } + + printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n", + best_fs, best_div, best_rate); + + info->fs_div = best_fs; + info->clk_div = best_div; + + return 0; +} +EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate); + +/* + * Set S3C2412 Clock source + */ +static int s3c2412_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); + + DBG("%s(%p, %d, %u, %d)\n", __func__, cpu_dai, clk_id, + freq, dir); + + switch (clk_id) { + case S3C2412_CLKSRC_PCLK: + iismod &= ~S3C2412_IISMOD_MASTER_MASK; + iismod |= S3C2412_IISMOD_MASTER_INTERNAL; + break; + case S3C2412_CLKSRC_I2SCLK: + iismod &= ~S3C2412_IISMOD_MASTER_MASK; + iismod |= S3C2412_IISMOD_MASTER_EXTERNAL; + break; + default: + return -EINVAL; + } + + writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD); + return 0; +} + +/* + * Set S3C2412 Clock dividers + */ +static int s3c2412_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai, + int div_id, int div) +{ + struct s3c2412_i2s_info *i2s = &s3c2412_i2s; + u32 reg; + + DBG("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div); + + switch (div_id) { + case S3C2412_DIV_BCLK: + reg = readl(i2s->regs + S3C2412_IISMOD); + reg &= ~S3C2412_IISMOD_BCLK_MASK; + writel(reg | div, i2s->regs + S3C2412_IISMOD); + + DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); + break; + + case S3C2412_DIV_RCLK: + if (div > 3) { + /* convert value to bit field */ + + switch (div) { + case 256: + div = S3C2412_IISMOD_RCLK_256FS; + break; + + case 384: + div = S3C2412_IISMOD_RCLK_384FS; + break; + + case 512: + div = S3C2412_IISMOD_RCLK_512FS; + break; + + case 768: + div = S3C2412_IISMOD_RCLK_768FS; + break; + + default: + return -EINVAL; + } + } + + reg = readl(s3c2412_i2s.regs + S3C2412_IISMOD); + reg &= ~S3C2412_IISMOD_RCLK_MASK; + writel(reg | div, i2s->regs + S3C2412_IISMOD); + DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); + break; + + case S3C2412_DIV_PRESCALER: + if (div >= 0) { + writel((div << 8) | S3C2412_IISPSR_PSREN, + i2s->regs + S3C2412_IISPSR); + } else { + writel(0x0, i2s->regs + S3C2412_IISPSR); + } + DBG("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR)); + break; + + default: + return -EINVAL; + } + + return 0; +} + +struct clk *s3c2412_get_iisclk(void) +{ + return s3c2412_i2s.iis_clk; +} +EXPORT_SYMBOL_GPL(s3c2412_get_iisclk); + + +static int s3c2412_i2s_probe(struct platform_device *pdev) +{ + DBG("Entered %s\n", __func__); + + s3c2412_i2s.dev = &pdev->dev; + + s3c2412_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100); + if (s3c2412_i2s.regs == NULL) + return -ENXIO; + + s3c2412_i2s.iis_pclk = clk_get(&pdev->dev, "iis"); + if (s3c2412_i2s.iis_pclk == NULL) { + DBG("failed to get iis_clock\n"); + iounmap(s3c2412_i2s.regs); + return -ENODEV; + } + + s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk"); + if (s3c2412_i2s.iis_cclk == NULL) { + DBG("failed to get i2sclk clock\n"); + iounmap(s3c2412_i2s.regs); + return -ENODEV; + } + + clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll")); + + clk_enable(s3c2412_i2s.iis_pclk); + clk_enable(s3c2412_i2s.iis_cclk); + + s3c2412_i2s.iis_clk = s3c2412_i2s.iis_pclk; + + /* Configure the I2S pins in correct mode */ + s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK); + s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK); + s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK); + s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI); + s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO); + + s3c2412_snd_txctrl(0); + s3c2412_snd_rxctrl(0); + + return 0; +} + +#define S3C2412_I2S_RATES \ + (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +struct snd_soc_cpu_dai s3c2412_i2s_dai = { + .name = "s3c2412-i2s", + .id = 0, + .type = SND_SOC_DAI_I2S, + .probe = s3c2412_i2s_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = S3C2412_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = S3C2412_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = { + .trigger = s3c2412_i2s_trigger, + .hw_params = s3c2412_i2s_hw_params, + }, + .dai_ops = { + .set_fmt = s3c2412_i2s_set_fmt, + .set_clkdiv = s3c2412_i2s_set_clkdiv, + .set_sysclk = s3c2412_i2s_set_sysclk, + }, +}; +EXPORT_SYMBOL_GPL(s3c2412_i2s_dai); + +/* Module information */ +MODULE_AUTHOR("Ben Dooks, ben@simtec.co.uk"); +MODULE_DESCRIPTION("S3C2412 I2S SoC Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/s3c24xx/s3c2412-i2s.h new file mode 100644 index 0000000..27f48e1 --- /dev/null +++ b/sound/soc/s3c24xx/s3c2412-i2s.h @@ -0,0 +1,38 @@ +/* sound/soc/s3c24xx/s3c2412-i2s.c + * + * ALSA Soc Audio Layer - S3C2412 I2S driver + * + * Copyright (c) 2007 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks ben@simtec.co.uk + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. +*/ + +#ifndef __SND_SOC_S3C24XX_S3C2412_I2S_H +#define __SND_SOC_S3C24XX_S3C2412_I2S_H __FILE__ + +#define S3C2412_DIV_BCLK (1) +#define S3C2412_DIV_RCLK (2) +#define S3C2412_DIV_PRESCALER (3) + +#define S3C2412_CLKSRC_PCLK (0) +#define S3C2412_CLKSRC_I2SCLK (1) + +extern struct clk *s3c2412_get_iisclk(void); + +extern struct snd_soc_cpu_dai s3c2412_i2s_dai; + +struct s3c2412_rate_calc { + unsigned int clk_div; /* for prescaler */ + unsigned int fs_div; /* for root frame clock */ +}; + +extern int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info, + unsigned int *fstab, + unsigned int rate, struct clk *clk); + +#endif /* __SND_SOC_S3C24XX_S3C2412_I2S_H */
From: Ben Dooks ben-alsa@fluff.org
Support for suspend/resume for the S3C2412 ASoC IIS core driver.
Signed-off-by: Ben Dooks ben-linux@fluff.org Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/s3c24xx/s3c2412-i2s.c | 63 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 63 insertions(+), 0 deletions(-)
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c index 11fee8b..4520385 100644 --- a/sound/soc/s3c24xx/s3c2412-i2s.c +++ b/sound/soc/s3c24xx/s3c2412-i2s.c @@ -80,6 +80,10 @@ struct s3c2412_i2s_info { struct clk *iis_clk; struct clk *iis_pclk; struct clk *iis_cclk; + + u32 suspend_iismod; + u32 suspend_iiscon; + u32 suspend_iispsr; };
static struct s3c2412_i2s_info s3c2412_i2s; @@ -642,6 +646,63 @@ static int s3c2412_i2s_probe(struct platform_device *pdev) return 0; }
+#ifdef CONFIG_PM +static int s3c2412_i2s_suspend(struct platform_device *dev, + struct snd_soc_cpu_dai *dai) +{ + struct s3c2412_i2s_info *i2s = &s3c2412_i2s; + u32 iismod; + + if (dai->active) { + i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD); + i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON); + i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR); + + /* some basic suspend checks */ + + iismod = readl(i2s->regs + S3C2412_IISMOD); + + if (iismod & S3C2412_IISCON_RXDMA_ACTIVE) + dev_warn(&dev->dev, "%s: RXDMA active?\n", __func__); + + if (iismod & S3C2412_IISCON_TXDMA_ACTIVE) + dev_warn(&dev->dev, "%s: TXDMA active?\n", __func__); + + if (iismod & S3C2412_IISCON_IIS_ACTIVE) + dev_warn(&dev->dev, "%s: IIS active\n", __func__); + } + + return 0; +} + +static int s3c2412_i2s_resume(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) +{ + struct s3c2412_i2s_info *i2s = &s3c2412_i2s; + + dev_info(&pdev->dev, "dai_active %d, IISMOD %08x, IISCON %08x\n", + dai->active, i2s->suspend_iismod, i2s->suspend_iiscon); + + if (dai->active) { + writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON); + writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD); + writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR); + + writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH, + i2s->regs + S3C2412_IISFIC); + + ndelay(250); + writel(0x0, i2s->regs + S3C2412_IISFIC); + + } + + return 0; +} +#else +#define s3c2412_i2s_suspend NULL +#define s3c2412_i2s_resume NULL +#endif /* CONFIG_PM */ + #define S3C2412_I2S_RATES \ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ @@ -652,6 +713,8 @@ struct snd_soc_cpu_dai s3c2412_i2s_dai = { .id = 0, .type = SND_SOC_DAI_I2S, .probe = s3c2412_i2s_probe, + .suspend= s3c2412_i2s_suspend, + .resume = s3c2412_i2s_resume, .playback = { .channels_min = 2, .channels_max = 2,
From: Ian Molton spyro@f2s.com
Currently only the AUX channel is used (touchscreen)
Signed-off-by: Ian Molton spyro@f2s.com Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/pxa/Kconfig | 9 ++++ sound/soc/pxa/Makefile | 2 + sound/soc/pxa/e800_wm9712.c | 90 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 0 deletions(-) create mode 100644 sound/soc/pxa/e800_wm9712.c
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index a83e229..484f883 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -53,3 +53,12 @@ config SND_PXA2XX_SOC_TOSA help Say Y if you want to add support for SoC audio on Sharp Zaurus SL-C6000x models (Tosa). + +config SND_PXA2XX_SOC_E800 + tristate "SoC AC97 Audio support for e800" + depends on SND_PXA2XX_SOC && MACH_E800 + select SND_SOC_WM9712 + select SND_PXA2XX_SOC_AC97 + help + Say Y if you want to add support for SoC audio on the + Toshiba e800 PDA diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index 78e0d6b..04e5646 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile @@ -11,10 +11,12 @@ obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o snd-soc-corgi-objs := corgi.o snd-soc-poodle-objs := poodle.o snd-soc-tosa-objs := tosa.o +snd-soc-e800-objs := e800_wm9712.o snd-soc-spitz-objs := spitz.o
obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o +obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c new file mode 100644 index 0000000..1e25722 --- /dev/null +++ b/sound/soc/pxa/e800_wm9712.c @@ -0,0 +1,90 @@ +/* + * e800-wm9712.c -- SoC audio for e800 + * + * Based on tosa.c + * + * Copyright 2007 (c) Ian Molton spyro@f2s.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 ONLY. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> + +#include <sound/driver.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include <asm/mach-types.h> +#include <asm/arch/pxa-regs.h> +#include <asm/arch/hardware.h> +#include <asm/arch/audio.h> + +#include "../codecs/wm9712.h" +#include "pxa2xx-pcm.h" +#include "pxa2xx-ac97.h" + +static struct snd_soc_machine e800; + +static struct snd_soc_dai_link e800_dai[] = { +{ + .name = "AC97 Aux", + .stream_name = "AC97 Aux", + .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], + .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], +}, +}; + +static struct snd_soc_machine e800 = { + .name = "Toshiba e800", + .dai_link = e800_dai, + .num_links = ARRAY_SIZE(e800_dai), +}; + +static struct snd_soc_device e800_snd_devdata = { + .machine = &e800, + .platform = &pxa2xx_soc_platform, + .codec_dev = &soc_codec_dev_wm9712, +}; + +static struct platform_device *e800_snd_device; + +static int __init e800_init(void) +{ + int ret; + + if (!machine_is_e800()) + return -ENODEV; + + e800_snd_device = platform_device_alloc("soc-audio", -1); + if (!e800_snd_device) + return -ENOMEM; + + platform_set_drvdata(e800_snd_device, &e800_snd_devdata); + e800_snd_devdata.dev = &e800_snd_device->dev; + ret = platform_device_add(e800_snd_device); + + if (ret) + platform_device_put(e800_snd_device); + + return ret; +} + +static void __exit e800_exit(void) +{ + platform_device_unregister(e800_snd_device); +} + +module_init(e800_init); +module_exit(e800_exit); + +/* Module information */ +MODULE_AUTHOR("Ian Molton spyro@f2s.com"); +MODULE_DESCRIPTION("ALSA SoC driver for e800"); +MODULE_LICENSE("GPL");
Hi,
I'm sorry, but I tested this patch only now. And I just got another message from lockdep:
======================================================= [ INFO: possible circular locking dependency detected ] 2.6.24-rc7-dirty #79 ------------------------------------------------------- alsactl/953 is trying to acquire lock: (&codec->mutex){--..}, at: [<c01d03f0>] snd_soc_dapm_put_volsw+0xc4/0x218
but task is already holding lock: (&card->controls_rwsem){----}, at: [<c01bcfac>] snd_ctl_elem_write+0x24/0x13c
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #1 (&card->controls_rwsem){----}: [<c0058d84>] lock_acquire+0x6c/0x84 [<c0243eb4>] down_write+0x30/0x40 [<c01bd28c>] snd_ctl_add+0x54/0x1dc [<c01d2b78>] tosa_ac97_init+0x60/0xec [<c01ce5a0>] snd_soc_register_card+0x58/0x210 [<c01d193c>] wm9712_soc_probe+0x1f0/0x280 [<c01cf46c>] soc_probe+0x98/0x188 [<c015e998>] platform_drv_probe+0x20/0x24 [<c015d1dc>] driver_probe_device+0x100/0x1b8 [<c015d2a4>] __device_attach+0x10/0x14 [<c015c3a0>] bus_for_each_drv+0x48/0x84 [<c015d344>] device_attach+0x70/0x9c [<c015c308>] bus_attach_device+0x38/0x88 [<c015b068>] device_add+0x274/0x4bc [<c015ee40>] platform_device_add+0x100/0x158 [<c0016e04>] tosa_init+0x60/0x90 [<c00087e4>] kernel_init+0xc8/0x298 [<c0039cf0>] do_exit+0x0/0x760
-> #0 (&codec->mutex){--..}: [<c0058d84>] lock_acquire+0x6c/0x84 [<c0243b48>] mutex_lock_nested+0xf8/0x2bc [<c01d03f0>] snd_soc_dapm_put_volsw+0xc4/0x218 [<c01bd080>] snd_ctl_elem_write+0xf8/0x13c [<c01bde2c>] snd_ctl_ioctl+0x720/0xbfc [<c0099314>] do_ioctl+0x38/0x98 [<c0099620>] vfs_ioctl+0x2ac/0x2dc [<c0099690>] sys_ioctl+0x40/0x64 [<c001cf60>] ret_fast_syscall+0x0/0x2c
other info that might help us debug this:
2 locks held by alsactl/953: #0: (&card->power_lock){--..}, at: [<c01bde08>] snd_ctl_ioctl+0x6fc/0xbfc #1: (&card->controls_rwsem){----}, at: [<c01bcfac>] snd_ctl_elem_write+0x24/0x13c
stack backtrace: [<c00217d4>] (dump_stack+0x0/0x14) from [<c0056848>] (print_circular_bug_tail+0x78/0x94) [<c00567d0>] (print_circular_bug_tail+0x0/0x94) from [<c0058520>] (__lock_acquire+0x970/0xcf4) r6:c3f02538 r5:00000001 r4:c3f02560 [<c0057bb0>] (__lock_acquire+0x0/0xcf4) from [<c0058d84>] (lock_acquire+0x6c/0x84) [<c0058d18>] (lock_acquire+0x0/0x84) from [<c0243b48>] (mutex_lock_nested+0xf8/0x2bc) r7:c3f02220 r6:60000013 r5:c3d6eda8 r4:c3f9bd04 [<c0243a50>] (mutex_lock_nested+0x0/0x2bc) from [<c01d03f0>] (snd_soc_dapm_put_volsw+0xc4/0x218) [<c01d032c>] (snd_soc_dapm_put_volsw+0x0/0x218) from [<c01bd080>] (snd_ctl_elem_write+0xf8/0x13c) [<c01bcf88>] (snd_ctl_elem_write+0x0/0x13c) from [<c01bde2c>] (snd_ctl_ioctl+0x720/0xbfc) [<c01bd70c>] (snd_ctl_ioctl+0x0/0xbfc) from [<c0099314>] (do_ioctl+0x38/0x98) [<c00992dc>] (do_ioctl+0x0/0x98) from [<c0099620>] (vfs_ioctl+0x2ac/0x2dc) r6:bed526d8 r5:c3ed8288 r4:c3e93140 [<c0099374>] (vfs_ioctl+0x0/0x2dc) from [<c0099690>] (sys_ioctl+0x40/0x64) r9:c3f9a000 r8:c001d108 r6:c2c85513 r5:fffffff7 r4:c3e93140 [<c0099650>] (sys_ioctl+0x0/0x64) from [<c001cf60>] (ret_fast_syscall+0x0/0x2c) r6:00000002 r5:00000000 r4:00000000
2008/1/10, Mark Brown broonie@opensource.wolfsonmicro.com:
snd_soc_dapm_new_widgets() takes the codec lock when adding new widgets, causing lockdep warnings when applications later call down through ALSA to adjust controls. Since widgets are only added during probe this lock should be unneeded so don't take it.
Thanks to Dmitry Baryshkov dbaryshkov@gmail.com for reporting this issue.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com Cc: Dmitry Baryshkov dbaryshkov@gmail.com
sound/soc/soc-dapm.c | 2 -- 1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index a4ce882..f99cac1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -972,7 +972,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) { struct snd_soc_dapm_widget *w;
mutex_lock(&codec->mutex); list_for_each_entry(w, &codec->dapm_widgets, list) { if (w->new)
@@ -1007,7 +1006,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) }
dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
mutex_unlock(&codec->mutex); return 0;
} EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); -- 1.5.3.8
On Wed, 2008-01-16 at 02:40 +0300, Dmitry wrote:
I'm sorry, but I tested this patch only now. And I just got another message from lockdep:
No problem, I managed to reproduce your original case so I was confident of the fix. This one looks at first glance to be a very similar problem due to unneeded locking during initialisation so it shouldn't cause any issues in practise other than the warning from lockdep. I'll have a proper look on Thursday (I won't be able to tomorrow).
On Wed, Jan 16, 2008 at 02:40:55AM +0300, Dmitry wrote:
I'm sorry, but I tested this patch only now. And I just got another message from lockdep:
Could you give this patch a try, please?
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index f5fe7c6..283a0ba 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1131,7 +1131,6 @@ int snd_soc_register_card(struct snd_soc_device *socdev) struct snd_soc_machine *machine = socdev->machine; int ret = 0, i, ac97 = 0, err = 0;
- mutex_lock(&codec->mutex); for(i = 0; i < machine->num_links; i++) { if (socdev->machine->dai_link[i].init) { err = socdev->machine->dai_link[i].init(codec); @@ -1157,12 +1156,14 @@ int snd_soc_register_card(struct snd_soc_device *socdev) goto out; }
+ mutex_lock(&codec->mutex); #ifdef CONFIG_SND_SOC_AC97_BUS if (ac97) { ret = soc_ac97_dev_register(codec); if (ret < 0) { printk(KERN_ERR "asoc: AC97 device register failed\n"); snd_card_free(codec->card); + mutex_unlock(&codec->mutex); goto out; } } @@ -1175,8 +1176,10 @@ int snd_soc_register_card(struct snd_soc_device *socdev) err = device_create_file(socdev->dev, &dev_attr_codec_reg); if (err < 0) printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n"); -out: + mutex_unlock(&codec->mutex); + +out: return ret; } EXPORT_SYMBOL_GPL(snd_soc_register_card);
Hi Takashi,
Sorry for distracting you from the constant struggle with HDA.
I would like to provide complete support for the ESI Juli@ card. Informing the ADC (AK5385) about current sample rate via the dedicated GPIOs is fairly simple, as well as the monitoring features (DIGOUT, DIGIN, ANAIN, MUTE + volume control of the remaining DACs).
However, I do not understand functions of the GPIO_FREQ_XXKHZ and GPIO_MULTI_XX GPIOs. The card's user manual says the card can detect incoming SPDIF rate. AK4114 can either read the rate from the input data in professional format only, or detect it by comparing external clock with the input rate. The AK4114 external clock pin does go to the Xilinx CPLD. The GPIOs lead to the CPLD too. Do they in some way control the CPLD to provide AK4114 with external clock so that AK4114 can provide correct input rate data through its registers?
But if so, where would CPLD get the independent clock when ICE1724 is in slave mode, clocked by AK4114? True, the PMCLK output is fed to the CPLD, but that would be slaved to AK4114 too. I guess the only independent clock during the slave mode would have to come directly from the 24.576MHz crystal of ICE, but I could not trace any connection to the CPLD.
Perhaps the mysterious AV73-1 helps, I could not google-out any information on this IC.
The automated detection of incoming rate is important for setting correct sample rates in codecs when running on external clock.
If resolved, I would add similar functionality to the MI/ODI/O card for Prodigy192 too.
I am sorry for bothering you, but the GPIOs named constants in juli.c suggest you had access to some documentation about the card. My tracing and beeping the card ends with the internal CPLD logic.
Thanks a lot for any information or suggestion.
Best regards,
Pavel Hofman.
-----------------
inSITE, s.r.o.
Rubesova 29, 326 00 Plzen, Czech Republic Tel., fax: +420 - 37 - 74 493 58 GSM: +420 - 603 - 163 973 Email: pavel.hofman@insite.cz
At Fri, 18 Jan 2008 22:41:51 +0100, Pavel Hofman wrote:
Hi Takashi,
Sorry for distracting you from the constant struggle with HDA.
I would like to provide complete support for the ESI Juli@ card. Informing the ADC (AK5385) about current sample rate via the dedicated GPIOs is fairly simple, as well as the monitoring features (DIGOUT, DIGIN, ANAIN, MUTE + volume control of the remaining DACs).
However, I do not understand functions of the GPIO_FREQ_XXKHZ and GPIO_MULTI_XX GPIOs. The card's user manual says the card can detect incoming SPDIF rate. AK4114 can either read the rate from the input data in professional format only, or detect it by comparing external clock with the input rate. The AK4114 external clock pin does go to the Xilinx CPLD. The GPIOs lead to the CPLD too. Do they in some way control the CPLD to provide AK4114 with external clock so that AK4114 can provide correct input rate data through its registers?
Sorry, I have little idea about Juli (and all ESI boards). The datasheet was given only to Jaroslav (cc'ed) at that time.
Takashi
Takashi Iwai wrote:
At Fri, 18 Jan 2008 22:41:51 +0100, Pavel Hofman wrote:
Hi Takashi,
Sorry for distracting you from the constant struggle with HDA.
I would like to provide complete support for the ESI Juli@ card. Informing the ADC (AK5385) about current sample rate via the dedicated GPIOs is fairly simple, as well as the monitoring features (DIGOUT, DIGIN, ANAIN, MUTE + volume control of the remaining DACs).
However, I do not understand functions of the GPIO_FREQ_XXKHZ and GPIO_MULTI_XX GPIOs. The card's user manual says the card can detect incoming SPDIF rate. AK4114 can either read the rate from the input data in professional format only, or detect it by comparing external clock with the input rate. The AK4114 external clock pin does go to the Xilinx CPLD. The GPIOs lead to the CPLD too. Do they in some way control the CPLD to provide AK4114 with external clock so that AK4114 can provide correct input rate data through its registers?
Sorry, I have little idea about Juli (and all ESI boards). The datasheet was given only to Jaroslav (cc'ed) at that time.
Takashi
Takashi,
Thanks a lot for the info and sorry for overlooking the initial changetset author.
Perhaps Jaroslav will find a few minutes to recall the old story, pěkně prosím :)
Regards,
Pavel.
Hi,
2008/1/18, Mark Brown broonie@opensource.wolfsonmicro.com:
On Wed, Jan 16, 2008 at 02:40:55AM +0300, Dmitry wrote:
I'm sorry, but I tested this patch only now. And I just got another message from lockdep:
Could you give this patch a try, please?
Thanks, now I've got a clean boot log :)
At Thu, 10 Jan 2008 13:16:59 +0000, Mark Brown wrote:
Add myself as a point of contact for the ALSA SoC subsystem and add a reference to the development GIT tree.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com Signed-off-by: Liam Girdwood lg@opensource.wolfsonmicro.com
Thanks, all patches have been applied to HG tree now.
Takashi
participants (4)
-
Dmitry
-
Mark Brown
-
Pavel Hofman
-
Takashi Iwai