diff options
author | Laura Lawrence <Laura.Lawrence@freescale.com> | 2008-01-22 15:30:09 -0600 |
---|---|---|
committer | Daniel Schaeffer <daniel.schaeffer@timesys.com> | 2008-08-25 15:20:34 -0400 |
commit | 5124f25fa48ef919a9c04d4e172f2584305825ba (patch) | |
tree | 734631a82591a9b93ffbf8a2f0838926fabc3686 /sound | |
parent | 3cdf325fbd2ba08781116943cf89eff68cb4ecd4 (diff) |
ENGR00060847 ASoC version update for imx37 audio
ASoC version update, Wolfson AudioPlus driver
http://opensource.wolfsonmicro.com/node/8
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/soc-core.c | 1412 | ||||
-rw-r--r-- | sound/soc/soc-dapm.c | 281 |
2 files changed, 1060 insertions, 633 deletions
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index e6a67b58f296..319737e44a4c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -31,7 +31,7 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/bitops.h> -#include <linux/platform_device.h> +#include <linux/device.h> #include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> @@ -50,14 +50,21 @@ static DEFINE_MUTEX(pcm_mutex); static DEFINE_MUTEX(io_mutex); +static DEFINE_MUTEX(list_mutex); static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); +static LIST_HEAD(soc_codec_list); +static LIST_HEAD(soc_codec_dai_list); +static LIST_HEAD(soc_cpu_dai_list); +static LIST_HEAD(soc_platform_list); +static LIST_HEAD(soc_pcm_link_list); /* * This is a timeout to do a DAPM powerdown after a stream is closed(). * It can be used to eliminate pops between different playback streams, e.g. * between two audio tracks. */ -static int pmdown_time = 5000; + +static int pmdown_time = 1500; module_param(pmdown_time, int, 0); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); @@ -84,8 +91,10 @@ static int run_delayed_work(struct delayed_work *dwork) /* unregister ac97 codec */ static int soc_ac97_dev_unregister(struct snd_soc_codec *codec) { - if (codec->ac97->dev.bus) - device_unregister(&codec->ac97->dev); + struct snd_ac97 *ac97 = codec->ac97; + + if (ac97->dev.bus) + device_unregister(&ac97->dev); return 0; } @@ -93,40 +102,27 @@ static int soc_ac97_dev_unregister(struct snd_soc_codec *codec) static void soc_ac97_device_release(struct device *dev){} /* register ac97 codec to bus */ -static int soc_ac97_dev_register(struct snd_soc_codec *codec) +static int soc_ac97_dev_register(struct snd_soc_codec *codec, char *name) { + struct snd_ac97 *ac97 = codec->ac97; int err; - codec->ac97->dev.bus = &ac97_bus_type; - codec->ac97->dev.parent = NULL; - codec->ac97->dev.release = soc_ac97_device_release; + ac97->dev.bus = &ac97_bus_type; + ac97->dev.parent = NULL; + ac97->dev.release = soc_ac97_device_release; - snprintf(codec->ac97->dev.bus_id, BUS_ID_SIZE, "%d-%d:%s", - codec->card->number, 0, codec->name); - err = device_register(&codec->ac97->dev); + snprintf(ac97->dev.bus_id, BUS_ID_SIZE, "%d-%d:%s", + 0, 0, name); + err = device_register(&ac97->dev); if (err < 0) { snd_printk(KERN_ERR "Can't register ac97 bus\n"); - codec->ac97->dev.bus = NULL; + ac97->dev.bus = NULL; return err; } return 0; } #endif -static inline const char* get_dai_name(int type) -{ - switch(type) { - case SND_SOC_DAI_AC97_BUS: - case SND_SOC_DAI_AC97: - return "AC97"; - case SND_SOC_DAI_I2S: - return "I2S"; - case SND_SOC_DAI_PCM: - return "PCM"; - } - return NULL; -} - /* * Called by ALSA when a PCM substream is opened, the runtime->hw record is * then initialized and any private data can be allocated. This also calls @@ -134,20 +130,19 @@ static inline const char* get_dai_name(int type) */ static int soc_pcm_open(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_pcm_link *pcm_link = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_dai_link *machine = rtd->dai; - struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; - struct snd_soc_codec_dai *codec_dai = machine->codec_dai; + struct snd_soc_platform *platform = pcm_link->platform; + struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai; + struct snd_soc_dai *codec_dai = pcm_link->codec_dai; + struct snd_soc_codec *codec = pcm_link->codec; int ret = 0; mutex_lock(&pcm_mutex); /* startup the audio subsystem */ - if (cpu_dai->ops.startup) { - ret = cpu_dai->ops.startup(substream); + if (cpu_dai->audio_ops && cpu_dai->audio_ops->startup) { + ret = cpu_dai->audio_ops->startup(substream); if (ret < 0) { printk(KERN_ERR "asoc: can't open interface %s\n", cpu_dai->name); @@ -163,8 +158,8 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } - if (codec_dai->ops.startup) { - ret = codec_dai->ops.startup(substream); + if (codec_dai->audio_ops && codec_dai->audio_ops->startup) { + ret = codec_dai->audio_ops->startup(substream); if (ret < 0) { printk(KERN_ERR "asoc: can't open codec %s\n", codec_dai->name); @@ -172,62 +167,68 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } - if (machine->ops && machine->ops->startup) { - ret = machine->ops->startup(substream); + if (pcm_link->audio_ops && pcm_link->audio_ops->startup) { + ret = pcm_link->audio_ops->startup(substream); if (ret < 0) { - printk(KERN_ERR "asoc: %s startup failed\n", machine->name); - goto machine_err; + printk(KERN_ERR "asoc: %s startup failed\n", pcm_link->name); + goto pcm_link_err; } } /* Check that the codec and cpu DAI's are compatible */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { runtime->hw.rate_min = - max(codec_dai->playback.rate_min, cpu_dai->playback.rate_min); + max(codec_dai->playback->rate_min, + cpu_dai->playback->rate_min); runtime->hw.rate_max = - min(codec_dai->playback.rate_max, cpu_dai->playback.rate_max); + min(codec_dai->playback->rate_max, + cpu_dai->playback->rate_max); runtime->hw.channels_min = - max(codec_dai->playback.channels_min, - cpu_dai->playback.channels_min); + max(codec_dai->playback->channels_min, + cpu_dai->playback->channels_min); runtime->hw.channels_max = - min(codec_dai->playback.channels_max, - cpu_dai->playback.channels_max); + min(codec_dai->playback->channels_max, + cpu_dai->playback->channels_max); runtime->hw.formats = - codec_dai->playback.formats & cpu_dai->playback.formats; + codec_dai->playback->formats & + cpu_dai->playback->formats; runtime->hw.rates = - codec_dai->playback.rates & cpu_dai->playback.rates; + codec_dai->playback->rates & cpu_dai->playback->rates; } else { runtime->hw.rate_min = - max(codec_dai->capture.rate_min, cpu_dai->capture.rate_min); + max(codec_dai->capture->rate_min, + cpu_dai->capture->rate_min); runtime->hw.rate_max = - min(codec_dai->capture.rate_max, cpu_dai->capture.rate_max); + min(codec_dai->capture->rate_max, + cpu_dai->capture->rate_max); runtime->hw.channels_min = - max(codec_dai->capture.channels_min, - cpu_dai->capture.channels_min); + max(codec_dai->capture->channels_min, + cpu_dai->capture->channels_min); runtime->hw.channels_max = - min(codec_dai->capture.channels_max, - cpu_dai->capture.channels_max); + min(codec_dai->capture->channels_max, + cpu_dai->capture->channels_max); runtime->hw.formats = - codec_dai->capture.formats & cpu_dai->capture.formats; + codec_dai->capture->formats & cpu_dai->capture->formats; runtime->hw.rates = - codec_dai->capture.rates & cpu_dai->capture.rates; + codec_dai->capture->rates & cpu_dai->capture->rates; } + ret = -EINVAL; snd_pcm_limit_hw_rates(runtime); if (!runtime->hw.rates) { printk(KERN_ERR "asoc: %s <-> %s No matching rates\n", codec_dai->name, cpu_dai->name); - goto machine_err; + goto pcm_link_err; } if (!runtime->hw.formats) { printk(KERN_ERR "asoc: %s <-> %s No matching formats\n", codec_dai->name, cpu_dai->name); - goto machine_err; + goto pcm_link_err; } if (!runtime->hw.channels_min || !runtime->hw.channels_max) { printk(KERN_ERR "asoc: %s <-> %s No matching channels\n", codec_dai->name, cpu_dai->name); - goto machine_err; + goto pcm_link_err; } dbg("asoc: %s <-> %s info:\n",codec_dai->name, cpu_dai->name); @@ -238,26 +239,26 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) runtime->hw.rate_max); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_dai->playback.active = codec_dai->playback.active = 1; + cpu_dai->playback_active = codec_dai->playback_active = 1; else - cpu_dai->capture.active = codec_dai->capture.active = 1; + cpu_dai->capture_active = codec_dai->capture_active = 1; cpu_dai->active = codec_dai->active = 1; cpu_dai->runtime = runtime; - socdev->codec->active++; + codec->active++; mutex_unlock(&pcm_mutex); return 0; -machine_err: - if (machine->ops && machine->ops->shutdown) - machine->ops->shutdown(substream); +pcm_link_err: + if (pcm_link->audio_ops && pcm_link->audio_ops->shutdown) + pcm_link->audio_ops->shutdown(substream); codec_dai_err: if (platform->pcm_ops->close) platform->pcm_ops->close(substream); platform_err: - if (cpu_dai->ops.shutdown) - cpu_dai->ops.shutdown(substream); + if (cpu_dai->audio_ops && cpu_dai->audio_ops->shutdown) + cpu_dai->audio_ops->shutdown(substream); out: mutex_unlock(&pcm_mutex); return ret; @@ -270,35 +271,41 @@ out: */ static void close_delayed_work(struct work_struct *work) { - struct snd_soc_device *socdev = - container_of(work, struct snd_soc_device, delayed_work.work); - struct snd_soc_codec *codec = socdev->codec; - struct snd_soc_codec_dai *codec_dai; - int i; + struct snd_soc_pcm_link *pcm_link = + container_of(work, struct snd_soc_pcm_link, + delayed_work.work); + struct snd_soc_dai *codec_dai = pcm_link->codec_dai; + struct snd_soc_machine *machine = pcm_link->machine; mutex_lock(&pcm_mutex); - for(i = 0; i < codec->num_dai; i++) { - codec_dai = &codec->dai[i]; - dbg("pop wq checking: %s status: %s waiting: %s\n", - codec_dai->playback.stream_name, - codec_dai->playback.active ? "active" : "inactive", - codec_dai->pop_wait ? "yes" : "no"); + dbg("pop wq checking: %s status: %s waiting: %s\n", + codec_dai->playback->stream_name, + codec_dai->playback_active ? "active" : "inactive", + codec_dai->pop_wait ? "yes" : "no"); - /* are we waiting on this codec DAI stream */ - if (codec_dai->pop_wait == 1) { + /* are we waiting on this codec DAI stream */ + 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_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); - } + /* power down the codec to D1 if no longer active */ + if (codec_dai->playback_active == 0) { + dbg("pop wq D1 %s %s\n", pcm_link->name, + codec_dai->playback->stream_name); + snd_soc_dapm_device_event(pcm_link, + SNDRV_CTL_POWER_D1); + } + + codec_dai->pop_wait = 0; + snd_soc_dapm_stream_event(machine, + codec_dai->playback->stream_name, + SND_SOC_DAPM_STREAM_STOP); + + /* power down the codec power domain if no longer active */ + if (codec_dai->playback_active == 0) { + dbg("pop wq D3 %s %s\n", pcm_link->name, + codec_dai->playback->stream_name); + snd_soc_dapm_device_event(pcm_link, + SNDRV_CTL_POWER_D3hot); } } mutex_unlock(&pcm_mutex); @@ -311,35 +318,34 @@ static void close_delayed_work(struct work_struct *work) */ static int soc_codec_close(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_device *socdev = rtd->socdev; - struct snd_soc_dai_link *machine = rtd->dai; - struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; - struct snd_soc_codec_dai *codec_dai = machine->codec_dai; - struct snd_soc_codec *codec = socdev->codec; + struct snd_soc_pcm_link *pcm_link = substream->private_data; + struct snd_soc_platform *platform = pcm_link->platform; + struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai; + struct snd_soc_dai *codec_dai = pcm_link->codec_dai; + struct snd_soc_codec *codec = pcm_link->codec; + struct snd_soc_machine *machine = pcm_link->machine; mutex_lock(&pcm_mutex); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_dai->playback.active = codec_dai->playback.active = 0; + cpu_dai->playback_active = codec_dai->playback_active = 0; else - cpu_dai->capture.active = codec_dai->capture.active = 0; + cpu_dai->capture_active = codec_dai->capture_active = 0; - if (codec_dai->playback.active == 0 && - codec_dai->capture.active == 0) { + if (codec_dai->playback_active == 0 && + codec_dai->capture_active == 0) { cpu_dai->active = codec_dai->active = 0; } codec->active--; - if (cpu_dai->ops.shutdown) - cpu_dai->ops.shutdown(substream); + if (cpu_dai->audio_ops && cpu_dai->audio_ops->shutdown) + cpu_dai->audio_ops->shutdown(substream); - if (codec_dai->ops.shutdown) - codec_dai->ops.shutdown(substream); + if (codec_dai->audio_ops && codec_dai->audio_ops->shutdown) + codec_dai->audio_ops->shutdown(substream); - if (machine->ops && machine->ops->shutdown) - machine->ops->shutdown(substream); + if (pcm_link->audio_ops && pcm_link->audio_ops->shutdown) + pcm_link->audio_ops->shutdown(substream); if (platform->pcm_ops->close) platform->pcm_ops->close(substream); @@ -348,17 +354,17 @@ static int soc_codec_close(struct snd_pcm_substream *substream) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* start delayed pop wq here for playback streams */ codec_dai->pop_wait = 1; - schedule_delayed_work(&socdev->delayed_work, + schedule_delayed_work(&pcm_link->delayed_work, msecs_to_jiffies(pmdown_time)); } else { /* capture streams can be powered down now */ - snd_soc_dapm_stream_event(codec, - codec_dai->capture.stream_name, SND_SOC_DAPM_STREAM_STOP); + snd_soc_dapm_stream_event(machine, + 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(pcm_link, + SNDRV_CTL_POWER_D3hot); } mutex_unlock(&pcm_mutex); @@ -372,19 +378,18 @@ static int soc_codec_close(struct snd_pcm_substream *substream) */ static int soc_pcm_prepare(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_device *socdev = rtd->socdev; - struct snd_soc_dai_link *machine = rtd->dai; - struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; - struct snd_soc_codec_dai *codec_dai = machine->codec_dai; - struct snd_soc_codec *codec = socdev->codec; + struct snd_soc_pcm_link *pcm_link = substream->private_data; + struct snd_soc_platform *platform = pcm_link->platform; + struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai; + struct snd_soc_dai *codec_dai = pcm_link->codec_dai; + struct snd_soc_codec *codec = pcm_link->codec; + struct snd_soc_machine *machine = pcm_link->machine; int ret = 0; mutex_lock(&pcm_mutex); - if (machine->ops && machine->ops->prepare) { - ret = machine->ops->prepare(substream); + if (pcm_link->audio_ops && pcm_link->audio_ops->prepare) { + ret = pcm_link->audio_ops->prepare(substream); if (ret < 0) { printk(KERN_ERR "asoc: machine prepare error\n"); goto out; @@ -399,16 +404,16 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } - if (codec_dai->ops.prepare) { - ret = codec_dai->ops.prepare(substream); + if (codec_dai->audio_ops && codec_dai->audio_ops->prepare) { + ret = codec_dai->audio_ops->prepare(substream); if (ret < 0) { printk(KERN_ERR "asoc: codec DAI prepare error\n"); goto out; } } - if (cpu_dai->ops.prepare) { - ret = cpu_dai->ops.prepare(substream); + if (cpu_dai->audio_ops && cpu_dai->audio_ops->prepare) { + ret = cpu_dai->audio_ops->prepare(substream); if (ret < 0) { printk(KERN_ERR "asoc: cpu DAI prepare error\n"); goto out; @@ -420,48 +425,46 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) if (codec_dai->pop_wait) { /* we are waiting for the delayed work to start */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - snd_soc_dapm_stream_event(socdev->codec, - codec_dai->capture.stream_name, + snd_soc_dapm_stream_event(machine, + codec_dai->capture->stream_name, SND_SOC_DAPM_STREAM_START); else { codec_dai->pop_wait = 0; - cancel_delayed_work(&socdev->delayed_work); - if (codec_dai->dai_ops.digital_mute) - codec_dai->dai_ops.digital_mute(codec_dai, 0); + cancel_delayed_work(&pcm_link->delayed_work); + if (codec_dai->ops && codec_dai->ops->digital_mute) + codec_dai->ops->digital_mute(codec_dai, 0); } } else { /* 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(pcm_link, SNDRV_CTL_POWER_D1); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dapm_stream_event(codec, - codec_dai->playback.stream_name, + snd_soc_dapm_stream_event(machine, + codec_dai->playback->stream_name, SND_SOC_DAPM_STREAM_START); else - snd_soc_dapm_stream_event(codec, - codec_dai->capture.stream_name, + snd_soc_dapm_stream_event(machine, + codec_dai->capture->stream_name, SND_SOC_DAPM_STREAM_START); - if (codec->dapm_event) - codec->dapm_event(codec, SNDRV_CTL_POWER_D0); - if (codec_dai->dai_ops.digital_mute) - codec_dai->dai_ops.digital_mute(codec_dai, 0); + snd_soc_dapm_device_event(pcm_link, SNDRV_CTL_POWER_D0); + if (codec_dai->ops && codec_dai->ops->digital_mute) + codec_dai->ops->digital_mute(codec_dai, 0); } else { /* codec already powered - power on widgets */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dapm_stream_event(codec, - codec_dai->playback.stream_name, + snd_soc_dapm_stream_event(machine, + codec_dai->playback->stream_name, SND_SOC_DAPM_STREAM_START); else - snd_soc_dapm_stream_event(codec, - codec_dai->capture.stream_name, + snd_soc_dapm_stream_event(machine, + codec_dai->capture->stream_name, SND_SOC_DAPM_STREAM_START); - if (codec_dai->dai_ops.digital_mute) - codec_dai->dai_ops.digital_mute(codec_dai, 0); + if (codec_dai->ops && codec_dai->ops->digital_mute) + codec_dai->ops->digital_mute(codec_dai, 0); } } @@ -478,26 +481,24 @@ out: static int soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_device *socdev = rtd->socdev; - struct snd_soc_dai_link *machine = rtd->dai; - struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; - struct snd_soc_codec_dai *codec_dai = machine->codec_dai; + struct snd_soc_pcm_link *pcm_link = substream->private_data; + struct snd_soc_platform *platform = pcm_link->platform; + struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai; + struct snd_soc_dai *codec_dai = pcm_link->codec_dai; int ret = 0; mutex_lock(&pcm_mutex); - if (machine->ops && machine->ops->hw_params) { - ret = machine->ops->hw_params(substream, params); + if (pcm_link->audio_ops && pcm_link->audio_ops->hw_params) { + ret = pcm_link->audio_ops->hw_params(substream, params); if (ret < 0) { printk(KERN_ERR "asoc: machine hw_params failed\n"); goto out; } } - if (codec_dai->ops.hw_params) { - ret = codec_dai->ops.hw_params(substream, params); + if (codec_dai->audio_ops && codec_dai->audio_ops->hw_params) { + ret = codec_dai->audio_ops->hw_params(substream, params); if (ret < 0) { printk(KERN_ERR "asoc: can't set codec %s hw params\n", codec_dai->name); @@ -505,8 +506,8 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, } } - if (cpu_dai->ops.hw_params) { - ret = cpu_dai->ops.hw_params(substream, params); + if (cpu_dai->audio_ops && cpu_dai->audio_ops->hw_params) { + ret = cpu_dai->audio_ops->hw_params(substream, params); if (ret < 0) { printk(KERN_ERR "asoc: can't set interface %s hw params\n", cpu_dai->name); @@ -528,16 +529,16 @@ out: return ret; platform_err: - if (cpu_dai->ops.hw_free) - cpu_dai->ops.hw_free(substream); + if (cpu_dai->audio_ops && cpu_dai->audio_ops->hw_free) + cpu_dai->audio_ops->hw_free(substream); interface_err: - if (codec_dai->ops.hw_free) - codec_dai->ops.hw_free(substream); + if (codec_dai->audio_ops && codec_dai->audio_ops->hw_free) + codec_dai->audio_ops->hw_free(substream); codec_err: - if(machine->ops && machine->ops->hw_free) - machine->ops->hw_free(substream); + if(pcm_link->audio_ops && pcm_link->audio_ops->hw_free) + pcm_link->audio_ops->hw_free(substream); mutex_unlock(&pcm_mutex); return ret; @@ -548,34 +549,31 @@ codec_err: */ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_device *socdev = rtd->socdev; - struct snd_soc_dai_link *machine = rtd->dai; - struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; - struct snd_soc_codec_dai *codec_dai = machine->codec_dai; - struct snd_soc_codec *codec = socdev->codec; + struct snd_soc_pcm_link *pcm_link = substream->private_data; + struct snd_soc_platform *platform = pcm_link->platform; + struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai; + struct snd_soc_dai *codec_dai = pcm_link->codec_dai; mutex_lock(&pcm_mutex); /* apply codec digital mute */ - if (!codec->active && codec_dai->dai_ops.digital_mute) - codec_dai->dai_ops.digital_mute(codec_dai, 1); + if (codec_dai->ops && codec_dai->ops->digital_mute) + codec_dai->ops->digital_mute(codec_dai, 1); /* free any machine hw params */ - if (machine->ops && machine->ops->hw_free) - machine->ops->hw_free(substream); + if (pcm_link->audio_ops && pcm_link->audio_ops->hw_free) + pcm_link->audio_ops->hw_free(substream); /* free any DMA resources */ if (platform->pcm_ops->hw_free) platform->pcm_ops->hw_free(substream); /* now free hw params for the DAI's */ - if (codec_dai->ops.hw_free) - codec_dai->ops.hw_free(substream); + if (codec_dai->audio_ops && codec_dai->audio_ops->hw_free) + codec_dai->audio_ops->hw_free(substream); - if (cpu_dai->ops.hw_free) - cpu_dai->ops.hw_free(substream); + if (cpu_dai->audio_ops && cpu_dai->audio_ops->hw_free) + cpu_dai->audio_ops->hw_free(substream); mutex_unlock(&pcm_mutex); return 0; @@ -583,16 +581,14 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_device *socdev = rtd->socdev; - struct snd_soc_dai_link *machine = rtd->dai; - struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; - struct snd_soc_codec_dai *codec_dai = machine->codec_dai; + struct snd_soc_pcm_link *pcm_link = substream->private_data; + struct snd_soc_platform *platform = pcm_link->platform; + struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai; + struct snd_soc_dai *codec_dai = pcm_link->codec_dai; int ret; - if (codec_dai->ops.trigger) { - ret = codec_dai->ops.trigger(substream, cmd); + if (codec_dai->audio_ops && codec_dai->audio_ops->trigger) { + ret = codec_dai->audio_ops->trigger(substream, cmd); if (ret < 0) return ret; } @@ -603,8 +599,8 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } - if (cpu_dai->ops.trigger) { - ret = cpu_dai->ops.trigger(substream, cmd); + if (cpu_dai->audio_ops && cpu_dai->audio_ops->trigger) { + ret = cpu_dai->audio_ops->trigger(substream, cmd); if (ret < 0) return ret; } @@ -623,264 +619,350 @@ static struct snd_pcm_ops soc_pcm_ops = { #ifdef CONFIG_PM /* powers down audio subsystem for suspend */ -static int soc_suspend(struct platform_device *pdev, pm_message_t state) +int snd_soc_suspend(struct snd_soc_machine *machine, pm_message_t state) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_machine *machine = socdev->machine; - struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_codec_device *codec_dev = socdev->codec_dev; - struct snd_soc_codec *codec = socdev->codec; - int i; - + struct snd_soc_platform *platform; + struct snd_soc_dai *cpu_dai; + struct snd_soc_dai *codec_dai; + struct snd_soc_codec *codec; + struct snd_soc_pcm_link *pcm_link; + char *stream; + + if (machine->ops && machine->ops->suspend_pre) + machine->ops->suspend_pre(machine, state); + /* mute any active DAC's */ - for(i = 0; i < machine->num_links; i++) { - struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai; - if (dai->dai_ops.digital_mute && dai->playback.active) - dai->dai_ops.digital_mute(dai, 1); + list_for_each_entry(pcm_link, &machine->active_list, active_list) { + codec_dai = pcm_link->codec_dai; + if (codec_dai->ops && + codec_dai->ops->digital_mute && + codec_dai->playback_active) + codec_dai->ops->digital_mute(codec_dai, 1); } - if (machine->suspend_pre) - machine->suspend_pre(pdev, state); - - for(i = 0; i < machine->num_links; i++) { - struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; - if (cpu_dai->suspend && cpu_dai->type != SND_SOC_DAI_AC97) - cpu_dai->suspend(pdev, cpu_dai); - if (platform->suspend) - platform->suspend(pdev, cpu_dai); + snd_power_change_state(machine->card, SNDRV_CTL_POWER_D3cold); + + /* suspend all pcm's */ + list_for_each_entry(pcm_link, &machine->active_list, active_list) + snd_pcm_suspend_all(pcm_link->pcm); + + list_for_each_entry(pcm_link, &machine->active_list, active_list) { + cpu_dai = pcm_link->cpu_dai; + platform = pcm_link->platform; + if (cpu_dai->dev.driver->suspend && + cpu_dai->type != SND_SOC_DAI_AC97) + cpu_dai->dev.driver->suspend(&cpu_dai->dev, state); + if (platform->dev.driver->suspend) + platform->dev.driver->suspend(&platform->dev, state); } /* close any waiting streams and save state */ - run_delayed_work(&socdev->delayed_work); - codec->suspend_dapm_state = codec->dapm_state; + list_for_each_entry(pcm_link, &machine->active_list, active_list) { + codec = pcm_link->codec; + run_delayed_work(&pcm_link->delayed_work); + codec->suspend_dapm_state = codec->dapm_state; + } - for(i = 0; i < codec->num_dai; i++) { - char *stream = codec->dai[i].playback.stream_name; + list_for_each_entry(pcm_link, &machine->active_list, active_list) { + codec_dai = pcm_link->codec_dai; + stream = codec_dai->playback->stream_name; if (stream != NULL) - snd_soc_dapm_stream_event(codec, stream, + snd_soc_dapm_stream_event(machine, stream, SND_SOC_DAPM_STREAM_SUSPEND); - stream = codec->dai[i].capture.stream_name; + stream = codec_dai->capture->stream_name; if (stream != NULL) - snd_soc_dapm_stream_event(codec, stream, + snd_soc_dapm_stream_event(machine, stream, SND_SOC_DAPM_STREAM_SUSPEND); } - if (codec_dev->suspend) - codec_dev->suspend(pdev, state); + list_for_each_entry(pcm_link, &machine->active_list, active_list) { + codec = pcm_link->codec; + if (codec->dev.driver->suspend) + codec->dev.driver->suspend(&codec->dev, state); + } - for(i = 0; i < machine->num_links; i++) { - struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; - if (cpu_dai->suspend && cpu_dai->type == SND_SOC_DAI_AC97) - cpu_dai->suspend(pdev, cpu_dai); + list_for_each_entry(pcm_link, &machine->active_list, active_list) { + cpu_dai = pcm_link->cpu_dai; + if (cpu_dai->dev.driver->suspend && + cpu_dai->type == SND_SOC_DAI_AC97) + cpu_dai->dev.driver->suspend(&cpu_dai->dev, state); } - if (machine->suspend_post) - machine->suspend_post(pdev, state); + if (machine->ops && machine->ops->suspend_post) + machine->ops->suspend_post(machine, state); return 0; } /* powers up audio subsystem after a suspend */ -static int soc_resume(struct platform_device *pdev) +int snd_soc_resume(struct snd_soc_machine *machine) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_machine *machine = socdev->machine; - struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_codec_device *codec_dev = socdev->codec_dev; - struct snd_soc_codec *codec = socdev->codec; - int i; - - if (machine->resume_pre) - machine->resume_pre(pdev); - - for(i = 0; i < machine->num_links; i++) { - struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; - if (cpu_dai->resume && cpu_dai->type == SND_SOC_DAI_AC97) - cpu_dai->resume(pdev, cpu_dai); + struct snd_soc_platform *platform; + struct snd_soc_dai *cpu_dai; + struct snd_soc_dai *codec_dai; + struct snd_soc_codec *codec; + struct snd_soc_pcm_link *pcm_link; + char *stream; + + if (machine->ops && machine->ops->resume_pre) + machine->ops->resume_pre(machine); + + list_for_each_entry(pcm_link, &soc_pcm_link_list, active_list) { + cpu_dai = pcm_link->cpu_dai; + codec = pcm_link->codec; + + if (cpu_dai->dev.driver->resume && + cpu_dai->type == SND_SOC_DAI_AC97) + cpu_dai->dev.driver->resume(&cpu_dai->dev); + if (codec->dev.driver->resume) + codec->dev.driver->resume(&codec->dev); } - if (codec_dev->resume) - codec_dev->resume(pdev); - - for(i = 0; i < codec->num_dai; i++) { - char* stream = codec->dai[i].playback.stream_name; + list_for_each_entry(pcm_link, &machine->active_list, active_list) { + codec_dai = pcm_link->codec_dai; + stream = codec_dai->playback->stream_name; if (stream != NULL) - snd_soc_dapm_stream_event(codec, stream, + snd_soc_dapm_stream_event(machine, stream, SND_SOC_DAPM_STREAM_RESUME); - stream = codec->dai[i].capture.stream_name; + stream = codec_dai->capture->stream_name; if (stream != NULL) - snd_soc_dapm_stream_event(codec, stream, + snd_soc_dapm_stream_event(machine, stream, SND_SOC_DAPM_STREAM_RESUME); } /* unmute any active DAC's */ - for(i = 0; i < machine->num_links; i++) { - struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai; - if (dai->dai_ops.digital_mute && dai->playback.active) - dai->dai_ops.digital_mute(dai, 0); + list_for_each_entry(pcm_link, &machine->active_list, active_list) { + codec_dai = pcm_link->codec_dai; + if (codec_dai->ops && + codec_dai->ops->digital_mute && + codec_dai->playback_active) + codec_dai->ops->digital_mute(codec_dai, 0); } - for(i = 0; i < machine->num_links; i++) { - struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; - if (cpu_dai->resume && cpu_dai->type != SND_SOC_DAI_AC97) - cpu_dai->resume(pdev, cpu_dai); - if (platform->resume) - platform->resume(pdev, cpu_dai); + list_for_each_entry(pcm_link, &machine->active_list, active_list) { + cpu_dai = pcm_link->cpu_dai; + platform = pcm_link->platform; + if (cpu_dai->dev.driver->resume && + cpu_dai->type != SND_SOC_DAI_AC97) + cpu_dai->dev.driver->resume(&cpu_dai->dev); + if (platform->dev.driver->resume) + platform->dev.driver->resume(&platform->dev); } - if (machine->resume_post) - machine->resume_post(pdev); + if (machine->ops && machine->ops->resume_post) + machine->ops->resume_post(machine); + snd_power_change_state(machine->card, SNDRV_CTL_POWER_D3hot); return 0; } #else -#define soc_suspend NULL -#define soc_resume NULL -#endif +int snd_soc_suspend(struct snd_soc_machine *machine, pm_message_t state) +{ + return 0; +} -/* probes a new socdev */ -static int soc_probe(struct platform_device *pdev) +int snd_soc_resume(struct snd_soc_machine *machine) { - int ret = 0, i; - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_machine *machine = socdev->machine; - struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_codec_device *codec_dev = socdev->codec_dev; - - if (machine->probe) { - ret = machine->probe(pdev); - if(ret < 0) - return ret; - } + return 0; +} +#endif +EXPORT_SYMBOL_GPL(snd_soc_suspend); +EXPORT_SYMBOL_GPL(snd_soc_resume); - for (i = 0; i < machine->num_links; i++) { - struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; - if (cpu_dai->probe) { - ret = cpu_dai->probe(pdev); - if(ret < 0) - goto cpu_dai_err; +static void soc_match_components(void) +{ + struct snd_soc_codec *codec; + struct snd_soc_dai *codec_dai, *cpu_dai; + struct snd_soc_platform *platform; + struct snd_soc_pcm_link *pcm_link, *tmp; + struct snd_soc_machine *machine = NULL; + int probe = 0; + + mutex_lock(&list_mutex); + list_for_each_entry_safe(pcm_link, tmp, &soc_pcm_link_list, all_list) { + + if (pcm_link->probed) + continue; + machine = pcm_link->machine; + probe = 0; + + if (!pcm_link->codec) { + list_for_each_entry(codec, &soc_codec_list, list) { + if (!strcmp(codec->name, pcm_link->codec_id)) { + pcm_link->codec = codec; + break; + } + } } - } - - if (codec_dev->probe) { - ret = codec_dev->probe(pdev); - if(ret < 0) - goto cpu_dai_err; - } - if (platform->probe) { - ret = platform->probe(pdev); - if(ret < 0) - goto platform_err; - } + if (!pcm_link->codec_dai) { + list_for_each_entry(codec_dai, &soc_codec_dai_list, list) { + if (!strcmp(codec_dai->name, pcm_link->codec_dai_id)) { + pcm_link->codec_dai = codec_dai; + break; + } + } + } - /* DAPM stream work */ - INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work); - return 0; + if (!pcm_link->cpu_dai) { + list_for_each_entry(cpu_dai, &soc_cpu_dai_list, list) { + if (!strcmp(cpu_dai->name, pcm_link->cpu_dai_id)) { + pcm_link->cpu_dai = cpu_dai; + break; + } + } + } -platform_err: - if (codec_dev->remove) - codec_dev->remove(pdev); + if (!pcm_link->platform) { + list_for_each_entry(platform, &soc_platform_list, list) { + if (!strcmp(platform->name, pcm_link->platform_id)) { + pcm_link->platform = platform; + break; + } + } + } -cpu_dai_err: - for (i--; i >= 0; i--) { - struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; - if (cpu_dai->remove) - cpu_dai->remove(pdev); + if (pcm_link->platform && pcm_link->cpu_dai && + pcm_link->codec_dai && pcm_link->codec) { + pcm_link->link_ops->new(pcm_link); + probe = pcm_link->probed = 1; + list_add(&pcm_link->codec->dai_list, + &pcm_link->codec_dai->codec_list); + list_add(&pcm_link->active_list, &machine->active_list); + pcm_link->codec_dai->codec = pcm_link->codec; + } } - if (machine->remove) - machine->remove(pdev); - - return ret; + /* are all pcm_links now created ? */ + if (probe && machine && machine->pcm_links == machine->pcm_links_total) + machine->ops->mach_probe(machine); + mutex_unlock(&list_mutex); } -/* removes a socdev */ -static int soc_remove(struct platform_device *pdev) +static void soc_pcm_link_remove(struct snd_soc_pcm_link *pcm_link) { - int i; - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_machine *machine = socdev->machine; - struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_codec_device *codec_dev = socdev->codec_dev; - - run_delayed_work(&socdev->delayed_work); - - if (platform->remove) - platform->remove(pdev); - - if (codec_dev->remove) - codec_dev->remove(pdev); + struct snd_soc_machine *machine = pcm_link->machine; + + if (pcm_link->probed && pcm_link->link_ops->free) { + if (machine && !machine->disconnect) { + snd_card_disconnect(machine->card); + machine->disconnect = 1; + machine->pcm_links--; + } + run_delayed_work(&pcm_link->delayed_work); + pcm_link->link_ops->free(pcm_link); + pcm_link->probed = 0; + list_del(&pcm_link->active_list); + } + if (machine && machine->pcm_links == 0) { + if (machine->ops->mach_remove) + machine->ops->mach_remove(machine); + } +} - for (i = 0; i < machine->num_links; i++) { - struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; - if (cpu_dai->remove) - cpu_dai->remove(pdev); +static void soc_remove_components(const char *name) +{ + struct snd_soc_codec *codec; + struct snd_soc_dai *codec_dai, *cpu_dai; + struct snd_soc_platform *platform; + struct snd_soc_pcm_link *pcm_link, *tmp; + + mutex_lock(&list_mutex); + list_for_each_entry_safe(pcm_link, tmp, &soc_pcm_link_list, all_list) { + + codec = pcm_link->codec; + codec_dai = pcm_link->codec_dai; + cpu_dai = pcm_link->cpu_dai; + platform = pcm_link->platform; + + if (codec && !strcmp(name, codec->name)) { + soc_pcm_link_remove(pcm_link); + pcm_link->codec = NULL; + goto free; + } else if (codec_dai && !strcmp(name, codec_dai->name)) { + soc_pcm_link_remove(pcm_link); + pcm_link->codec_dai = NULL; + goto free; + } else if (cpu_dai && !strcmp(name, cpu_dai->name)) { + soc_pcm_link_remove(pcm_link); + pcm_link->cpu_dai = NULL; + goto free; + } else if (platform && !strcmp(name, platform->name)) { + soc_pcm_link_remove(pcm_link); + pcm_link->platform = NULL; + goto free; + } +free: + if (!pcm_link->codec && !pcm_link->platform && + !pcm_link->codec_dai && !pcm_link->cpu_dai) { + list_del(&pcm_link->all_list); + kfree(pcm_link); + } } + mutex_unlock(&list_mutex); +} - if (machine->remove) - machine->remove(pdev); +static void soc_dai_dev_release(struct device *dev) +{ + struct snd_soc_dai *dai = to_snd_soc_dai(dev); + + list_del(&dai->list); + soc_remove_components(dai->name); + kfree(dai); +} - return 0; +static void soc_codec_dev_release(struct device *dev) +{ + struct snd_soc_codec *codec = to_snd_soc_codec(dev); + + list_del(&codec->list); + soc_remove_components(codec->name); + kfree(codec); } -/* ASoC platform driver */ -static struct platform_driver soc_driver = { - .driver = { - .name = "soc-audio", - }, - .probe = soc_probe, - .remove = soc_remove, - .suspend = soc_suspend, - .resume = soc_resume, -}; +static void soc_platform_dev_release(struct device *dev) +{ + struct snd_soc_platform *platform = to_snd_soc_platform(dev); + list_del(&platform->list); + soc_remove_components(platform->name); + kfree(platform); +} /* create a new pcm */ -static int soc_new_pcm(struct snd_soc_device *socdev, - struct snd_soc_dai_link *dai_link, int num) +int snd_soc_pcm_new(struct snd_soc_pcm_link *pcm_link, int playback, + int capture) { - struct snd_soc_codec *codec = socdev->codec; - struct snd_soc_codec_dai *codec_dai = dai_link->codec_dai; - struct snd_soc_cpu_dai *cpu_dai = dai_link->cpu_dai; - struct snd_soc_pcm_runtime *rtd; + struct snd_soc_codec *codec = pcm_link->codec; + struct snd_soc_dai *codec_dai = pcm_link->codec_dai; + struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai; + struct snd_soc_platform *platform = pcm_link->platform; + struct snd_soc_machine *machine = pcm_link->machine; struct snd_pcm *pcm; - char new_name[64]; - int ret = 0, playback = 0, capture = 0; - - rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL); - if (rtd == NULL) - return -ENOMEM; - - rtd->dai = dai_link; - rtd->socdev = socdev; - codec_dai->codec = socdev->codec; - - /* check client and interface hw capabilities */ - sprintf(new_name, "%s %s-%s-%d",dai_link->stream_name, codec_dai->name, - get_dai_name(cpu_dai->type), num); + int ret = 0; - if (codec_dai->playback.channels_min) - playback = 1; - if (codec_dai->capture.channels_min) - capture = 1; + snd_assert(codec != NULL, return -EINVAL); + snd_assert(codec_dai != NULL, return -EINVAL); + snd_assert(cpu_dai != NULL, return -EINVAL); + snd_assert(platform != NULL, return -EINVAL); + snd_assert(machine != NULL, return -EINVAL); - ret = snd_pcm_new(codec->card, new_name, codec->pcm_devs++, playback, - capture, &pcm); + ret = snd_pcm_new(machine->card, (char*)pcm_link->name, + machine->pcm_links++, playback, capture, &pcm); if (ret < 0) { printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name); - kfree(rtd); return ret; } - pcm->private_data = rtd; - soc_pcm_ops.mmap = socdev->platform->pcm_ops->mmap; - soc_pcm_ops.pointer = socdev->platform->pcm_ops->pointer; - soc_pcm_ops.ioctl = socdev->platform->pcm_ops->ioctl; - soc_pcm_ops.copy = socdev->platform->pcm_ops->copy; - soc_pcm_ops.silence = socdev->platform->pcm_ops->silence; - soc_pcm_ops.ack = socdev->platform->pcm_ops->ack; - soc_pcm_ops.page = socdev->platform->pcm_ops->page; + pcm_link->pcm = pcm; + pcm->private_data = pcm_link; + soc_pcm_ops.mmap = platform->pcm_ops->mmap; + soc_pcm_ops.pointer = platform->pcm_ops->pointer; + soc_pcm_ops.ioctl = platform->pcm_ops->ioctl; + soc_pcm_ops.copy = platform->pcm_ops->copy; + soc_pcm_ops.silence = platform->pcm_ops->silence; + soc_pcm_ops.ack = platform->pcm_ops->ack; + soc_pcm_ops.page = platform->pcm_ops->page; if (playback) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops); @@ -888,25 +970,25 @@ static int soc_new_pcm(struct snd_soc_device *socdev, if (capture) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops); - ret = socdev->platform->pcm_new(codec->card, codec_dai, pcm); + ret = platform->platform_ops->pcm_new(platform, machine->card, + playback, capture, pcm); if (ret < 0) { printk(KERN_ERR "asoc: platform pcm constructor failed\n"); - kfree(rtd); return ret; } - pcm->private_free = socdev->platform->pcm_free; + pcm->private_free = platform->platform_ops->pcm_free; printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name, cpu_dai->name); return ret; } +EXPORT_SYMBOL_GPL(snd_soc_pcm_new); /* codec register dump */ static ssize_t codec_reg_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct snd_soc_device *devdata = dev_get_drvdata(dev); - struct snd_soc_codec *codec = devdata->codec; + struct snd_soc_codec *codec = to_snd_soc_codec(dev); int i, step = 1, count = 0; if (!codec->reg_cache_size) @@ -917,7 +999,7 @@ static ssize_t codec_reg_show(struct device *dev, count += sprintf(buf, "%s registers\n", codec->name); for(i = 0; i < codec->reg_cache_size; i += step) - count += sprintf(buf + count, "%2x: %4x\n", i, codec->read(codec, i)); + count += sprintf(buf + count, "%2x: %4x\n", i, codec->ops->read(codec, i)); return count; } @@ -931,28 +1013,40 @@ static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL); * * Initialises AC97 codec resources for use by ad-hoc devices only. */ -int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, +int snd_soc_new_ac97_codec(struct snd_soc_pcm_link *pcm_link, struct snd_ac97_bus_ops *ops, int num) { - mutex_lock(&codec->mutex); - - codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL); - if (codec->ac97 == NULL) { - mutex_unlock(&codec->mutex); + struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai; + struct snd_soc_machine *machine = pcm_link->machine; + struct snd_ac97 *ac97; + + snd_assert(machine != NULL, return -EINVAL); + snd_assert(cpu_dai != NULL, return -EINVAL); + snd_assert(ops != NULL, return -EINVAL); + + mutex_lock(&machine->mutex); + + ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL); + if (ac97 == NULL) { + mutex_unlock(&machine->mutex); return -ENOMEM; } - codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL); - if (codec->ac97->bus == NULL) { - kfree(codec->ac97); - codec->ac97 = NULL; - mutex_unlock(&codec->mutex); + ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL); + if (ac97->bus == NULL) { + kfree(ac97); + mutex_unlock(&machine->mutex); return -ENOMEM; } - codec->ac97->bus->ops = ops; - codec->ac97->num = num; - mutex_unlock(&codec->mutex); + ac97->bus->ops = ops; + ac97->num = num; + ac97->bus->card = machine->card; + ac97->bus->clock = 48000; + ac97->bus->num = cpu_dai->id; + spin_lock_init(&ac97->bus->bus_lock); + pcm_link->codec->ac97 = ac97; + mutex_unlock(&machine->mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); @@ -963,13 +1057,16 @@ EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); * * Frees AC97 codec device resources. */ -void snd_soc_free_ac97_codec(struct snd_soc_codec *codec) +void snd_soc_free_ac97_codec(struct snd_soc_pcm_link *pcm_link) { - mutex_lock(&codec->mutex); - kfree(codec->ac97->bus); - kfree(codec->ac97); - codec->ac97 = NULL; - mutex_unlock(&codec->mutex); + struct snd_soc_machine *machine = pcm_link->machine; + struct snd_ac97 *ac97 = pcm_link->codec->ac97; + + mutex_lock(&machine->mutex); + kfree(ac97->bus); + kfree(ac97); + pcm_link->codec->ac97 = NULL; + mutex_unlock(&machine->mutex); } EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec); @@ -1031,49 +1128,47 @@ int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, EXPORT_SYMBOL_GPL(snd_soc_test_bits); /** - * snd_soc_new_pcms - create new sound card and pcms + * snd_soc_new_card - create new sound card * @socdev: the SoC audio device * - * Create a new sound card based upon the codec and interface pcms. + * Create a new sound card based upon the machine. * * Returns 0 for success, else error. */ -int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid) +int snd_soc_new_card(struct snd_soc_machine *machine, int num_pcm_links, + int idx, const char *xid) { - struct snd_soc_codec *codec = socdev->codec; - struct snd_soc_machine *machine = socdev->machine; - int ret = 0, i; + int ret = 0; - mutex_lock(&codec->mutex); + snd_assert(machine->name != NULL, return -EINVAL); + snd_assert(machine->longname != NULL, return -EINVAL); + snd_assert(machine->ops != NULL, return -EINVAL); + snd_assert(machine->ops->mach_probe != NULL, return -EINVAL); + snd_assert(num_pcm_links != 0, return -EINVAL); + mutex_init(&machine->mutex); + mutex_lock(&machine->mutex); + INIT_LIST_HEAD(&machine->dapm_widgets); + INIT_LIST_HEAD(&machine->dapm_paths); + INIT_LIST_HEAD(&machine->active_list); /* register a sound card */ - codec->card = snd_card_new(idx, xid, codec->owner, 0); - if (!codec->card) { - printk(KERN_ERR "asoc: can't create sound card for codec %s\n", - codec->name); - mutex_unlock(&codec->mutex); + machine->card = snd_card_new(idx, xid, machine->owner, 0); + if (!machine->card) { + printk(KERN_ERR "asoc: can't create sound card for machine %s\n", + machine->name); + mutex_unlock(&machine->mutex); return -ENODEV; } - codec->card->dev = socdev->dev; - codec->card->private_data = codec; - strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver)); - - /* create the pcms */ - for(i = 0; i < machine->num_links; i++) { - ret = soc_new_pcm(socdev, &machine->dai_link[i], i); - if (ret < 0) { - printk(KERN_ERR "asoc: can't create pcm %s\n", - machine->dai_link[i].stream_name); - mutex_unlock(&codec->mutex); - return ret; - } - } - - mutex_unlock(&codec->mutex); + machine->card->dev = &machine->pdev->dev; + machine->card->private_data = machine; + machine->pcm_links_total = num_pcm_links; + strncpy(machine->card->driver, machine->name, + sizeof(machine->card->driver)); + mutex_unlock(&machine->mutex); return ret; } -EXPORT_SYMBOL_GPL(snd_soc_new_pcms); +EXPORT_SYMBOL_GPL(snd_soc_new_card); /** * snd_soc_register_card - register sound card @@ -1084,95 +1179,106 @@ EXPORT_SYMBOL_GPL(snd_soc_new_pcms); * * Returns 0 for success, else error. */ -int snd_soc_register_card(struct snd_soc_device *socdev) +int snd_soc_register_card(struct snd_soc_machine *machine) { - struct snd_soc_codec *codec = socdev->codec; - 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); - if (err < 0) { - printk(KERN_ERR "asoc: failed to init %s\n", - socdev->machine->dai_link[i].stream_name); - continue; + struct snd_soc_pcm_link *pcm_link; + int ret = 0, err = 0; + + snd_assert(machine->card != NULL, return -EINVAL); + snd_assert(machine->pdev != NULL, return -EINVAL); + + mutex_lock(&machine->mutex); + list_for_each_entry(pcm_link, &machine->active_list, active_list) { +#ifdef CONFIG_SND_SOC_AC97_BUS + if (pcm_link->codec_dai->type == SND_SOC_DAI_AC97_BUS) { + ret = soc_ac97_dev_register(pcm_link->codec, + (char*)pcm_link->name); + if (ret < 0) { + printk(KERN_ERR "asoc: AC97 device register failed\n"); + snd_card_free(machine->card); + goto out; } } - if (socdev->machine->dai_link[i].codec_dai->type == - SND_SOC_DAI_AC97_BUS) - ac97 = 1; +#endif } - snprintf(codec->card->shortname, sizeof(codec->card->shortname), + snprintf(machine->card->shortname, sizeof(machine->card->shortname), "%s", machine->name); - snprintf(codec->card->longname, sizeof(codec->card->longname), - "%s (%s)", machine->name, codec->name); + snprintf(machine->card->longname, sizeof(machine->card->longname), + "%s (%s)", machine->name, machine->longname); - ret = snd_card_register(codec->card); + ret = snd_card_register(machine->card); if (ret < 0) { printk(KERN_ERR "asoc: failed to register soundcard for codec %s\n", - codec->name); + machine->name); goto out; } -#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); - goto out; - } - } -#endif - - err = snd_soc_dapm_sys_add(socdev->dev); + err = snd_soc_dapm_sys_add(&machine->pdev->dev); if (err < 0) printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n"); - err = device_create_file(socdev->dev, &dev_attr_codec_reg); - if (err < 0) - printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n"); + list_for_each_entry(pcm_link, &machine->active_list, active_list) { + struct snd_soc_codec *codec = pcm_link->codec; + err = device_create_file(&codec->dev, &dev_attr_codec_reg); + if (err < 0) + printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n"); + } out: - mutex_unlock(&codec->mutex); + mutex_unlock(&machine->mutex); return ret; } EXPORT_SYMBOL_GPL(snd_soc_register_card); /** - * snd_soc_free_pcms - free sound card and pcms - * @socdev: the SoC audio device + * snd_soc_machine_free - free sound card and pcms + * @machine: the SoC audio machine * - * Frees sound card and pcms associated with the socdev. + * Frees sound card and pcms associated with the machine. * Also unregister the codec if it is an AC97 device. */ -void snd_soc_free_pcms(struct snd_soc_device *socdev) +void snd_soc_machine_free(struct snd_soc_machine *machine) { - struct snd_soc_codec *codec = socdev->codec; -#ifdef CONFIG_SND_SOC_AC97_BUS - struct snd_soc_codec_dai *codec_dai; - int i; + struct snd_soc_codec* codec, *codec_tmp; + struct snd_soc_dai *dai, *dai_tmp; + struct snd_soc_platform *platform, *platform_tmp; +#ifdef CONFIG_SND_SOC_AC97_BUS + struct snd_soc_pcm_link *pcm_link, *pcm_link_tmp; #endif - mutex_lock(&codec->mutex); + mutex_lock(&machine->mutex); #ifdef CONFIG_SND_SOC_AC97_BUS - for(i = 0; i < codec->num_dai; i++) { - codec_dai = &codec->dai[i]; - if (codec_dai->type == SND_SOC_DAI_AC97_BUS && codec->ac97) { - soc_ac97_dev_unregister(codec); - goto free_card; - } + list_for_each_entry_safe(pcm_link, pcm_link_tmp, + &machine->active_list, active_list) { + if (pcm_link->codec->ac97) + soc_ac97_dev_unregister(pcm_link->codec); } -free_card: #endif - - if (codec->card) - snd_card_free(codec->card); - device_remove_file(socdev->dev, &dev_attr_codec_reg); - mutex_unlock(&codec->mutex); + if (machine->card) + snd_card_free(machine->card); + + mutex_unlock(&machine->mutex); + + list_for_each_entry_safe(codec, codec_tmp, &soc_codec_list, list) { + if (device_is_registered(&codec->dev)) { + device_remove_file(&codec->dev, &dev_attr_codec_reg); + device_unregister(&codec->dev); + } + } + list_for_each_entry_safe(dai, dai_tmp, &soc_codec_dai_list, list) { + if (device_is_registered(&dai->dev)) + device_unregister(&dai->dev); + } + list_for_each_entry_safe(dai, dai_tmp, &soc_cpu_dai_list, list) { + if (device_is_registered(&dai->dev)) + device_unregister(&dai->dev); + } + list_for_each_entry_safe(platform, platform_tmp, + &soc_platform_list, list) { + if (device_is_registered(&platform->dev)) + device_unregister(&platform->dev); + } } -EXPORT_SYMBOL_GPL(snd_soc_free_pcms); +EXPORT_SYMBOL_GPL(snd_soc_machine_free); /** * snd_soc_set_runtime_hwparams - set the runtime hardware parameters @@ -1215,7 +1321,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); @@ -1350,13 +1455,13 @@ 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; uinfo->type = - mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + max == 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); @@ -1373,15 +1478,15 @@ 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; + max == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : 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); @@ -1402,7 +1507,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] = @@ -1412,10 +1518,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; @@ -1438,25 +1544,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); @@ -1473,13 +1578,13 @@ 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; uinfo->type = - mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + max == 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); @@ -1500,7 +1605,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] = @@ -1509,9 +1615,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; @@ -1534,7 +1640,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; @@ -1544,8 +1651,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; @@ -1559,19 +1666,276 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r); -static int __devinit snd_soc_init(void) +int snd_soc_register_codec_dai(struct snd_soc_dai *dai) +{ + mutex_lock(&list_mutex); + list_add(&dai->list, &soc_codec_dai_list); + mutex_unlock(&list_mutex); + soc_match_components(); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_register_codec_dai); + +int snd_soc_register_cpu_dai(struct snd_soc_dai *dai) +{ + mutex_lock(&list_mutex); + list_add(&dai->list, &soc_cpu_dai_list); + mutex_unlock(&list_mutex); + soc_match_components(); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_register_cpu_dai); + +int snd_soc_register_codec(struct snd_soc_codec *codec) +{ + mutex_lock(&list_mutex); + list_add(&codec->list, &soc_codec_list); + mutex_unlock(&list_mutex); + soc_match_components(); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_register_codec); + +int snd_soc_register_platform(struct snd_soc_platform *platform) +{ + mutex_lock(&list_mutex); + list_add(&platform->list, &soc_platform_list); + mutex_unlock(&list_mutex); + soc_match_components(); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_register_platform); + +struct snd_soc_platform *snd_soc_new_platform (struct device *parent, + const char *platform_id) { + struct snd_soc_platform *platform = NULL, *lplatform; + int err = 0, new_platform = 1; + + snd_assert(platform_id != NULL, return -EINVAL); + + mutex_lock(&list_mutex); + list_for_each_entry(lplatform, &soc_platform_list, list) { + if (!strcmp(lplatform->name, platform_id)) { + platform = lplatform; + new_platform = 0; + break; + } + } + mutex_unlock(&list_mutex); + if (new_platform) { + platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL); + if (platform == NULL) + return NULL; + + INIT_LIST_HEAD(&platform->list); + strcpy(platform->dev.bus_id, platform_id); + strcpy(platform->name, platform_id); + platform->dev.bus = &asoc_bus_type; + platform->dev.parent = parent; + platform->dev.release = soc_platform_dev_release; + err = device_register(&platform->dev); + if (err < 0) + goto dev_err; + } + soc_match_components(); + return platform; +dev_err: + kfree(platform); + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_new_platform); + +struct snd_soc_codec *snd_soc_new_codec (struct device *parent, + const char *codec_id) +{ + struct snd_soc_codec *codec = NULL, *lcodec; + int err = 0, new_codec = 1; + + snd_assert(codec_id != NULL, return -EINVAL); + + mutex_lock(&list_mutex); + list_for_each_entry(lcodec, &soc_codec_list, list) { + if (!strcmp(lcodec->name, codec_id)) { + codec = lcodec; + new_codec = 0; + break; + } + } + mutex_unlock(&list_mutex); + if (new_codec) { + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return NULL; + INIT_LIST_HEAD(&codec->list); + INIT_LIST_HEAD(&codec->dai_list); + mutex_init(&codec->mutex); + strcpy(codec->dev.bus_id, codec_id); + strcpy(codec->name, codec_id); + codec->dev.bus = &asoc_bus_type; + codec->dev.parent = parent; + codec->dev.release = soc_codec_dev_release; + err = device_register(&codec->dev); + if (err < 0) + goto dev_err; + } + soc_match_components(); + return codec; +dev_err: + kfree(codec); + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_new_codec); + +static struct snd_soc_dai *snd_soc_new_dai (struct device *parent, + const char *dai_id) +{ + struct snd_soc_dai *dai = NULL, *ldai; + int err = 0, new_dai = 1; + + snd_assert(dai_id != NULL, return -EINVAL); + + mutex_lock(&list_mutex); + list_for_each_entry(ldai, &soc_cpu_dai_list, list) { + if (!strcmp(ldai->name, dai_id)) { + dai = ldai; + new_dai = 0; + goto out; + } + } + list_for_each_entry(ldai, &soc_codec_dai_list, list) { + if (!strcmp(ldai->name, dai_id)) { + dai = ldai; + new_dai = 0; + goto out; + } + } +out: + mutex_unlock(&list_mutex); + if (new_dai) { + dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); + if (dai == NULL) + return NULL; + INIT_LIST_HEAD(&dai->list); + INIT_LIST_HEAD(&dai->codec_list); + strcpy(dai->dev.bus_id, dai_id); + strcpy(dai->name, dai_id); + dai->dev.bus = &asoc_bus_type; + dai->dev.parent = parent; + dai->dev.release = soc_dai_dev_release; + err = device_register(&dai->dev); + if (err < 0) + goto dev_err; + } + soc_match_components(); + return dai; +dev_err: + kfree(dai); + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_new_dai); + +struct snd_soc_pcm_link *snd_soc_pcm_link_new( + struct snd_soc_machine *machine, const char *name, + const struct snd_soc_pcm_link_ops *link_ops, + const char *platform_id, const char *codec_id, + const char *codec_dai_id, const char *cpu_dai_id) +{ + struct snd_soc_pcm_link *pcm_link; + + snd_assert(name != NULL, return -EINVAL); + snd_assert(codec_dai_id != NULL, return -EINVAL); + snd_assert(cpu_dai_id != NULL, return -EINVAL); + snd_assert(codec_id != NULL, return -EINVAL); + snd_assert(platform_id != NULL, return -EINVAL); + + pcm_link = kzalloc(sizeof(struct snd_soc_pcm_link), GFP_KERNEL); + if (pcm_link == NULL) + return NULL; + + INIT_LIST_HEAD(&pcm_link->active_list); + INIT_LIST_HEAD(&pcm_link->all_list); + INIT_DELAYED_WORK(&pcm_link->delayed_work, close_delayed_work); + pcm_link->machine = machine; + pcm_link->link_ops = link_ops; + pcm_link->codec_id = codec_id; + pcm_link->cpu_dai_id = cpu_dai_id; + pcm_link->codec_dai_id = codec_dai_id; + pcm_link->platform_id = platform_id; + strcpy(pcm_link->name, name); + mutex_lock(&list_mutex); + list_add(&pcm_link->all_list, &soc_pcm_link_list); + mutex_unlock(&list_mutex); + soc_match_components(); + + return pcm_link; +} +EXPORT_SYMBOL_GPL(snd_soc_pcm_link_new); + +int snd_soc_pcm_link_attach(struct snd_soc_pcm_link *pcm_link) +{ + struct device *parent; + struct snd_soc_codec *codec; + struct snd_soc_dai *codec_dai, *cpu_dai; + struct snd_soc_platform *platform; + + snd_assert(pcm_link->machine->pdev != NULL, return -EINVAL); + parent = &pcm_link->machine->pdev->dev; + + platform = snd_soc_new_platform(parent, pcm_link->platform_id); + if (platform == NULL) + goto platform_new_err; + codec_dai = snd_soc_new_dai(parent, pcm_link->codec_dai_id); + if (codec_dai == NULL) + goto codec_dai_new_err; + cpu_dai = snd_soc_new_dai(parent, pcm_link->cpu_dai_id); + if (cpu_dai == NULL) + goto cpu_dai_new_err; + codec = snd_soc_new_codec(parent, pcm_link->codec_id); + if (codec == NULL) + goto codec_new_err; + + soc_match_components(); + return 0; + +codec_new_err: + device_unregister(&cpu_dai->dev); +cpu_dai_new_err: + device_unregister(&codec_dai->dev); +codec_dai_new_err: + device_unregister(&platform->dev); +platform_new_err: + printk(KERN_ERR "asoc: failed to create pcm link\n"); + return -ENODEV; +} +EXPORT_SYMBOL_GPL(snd_soc_pcm_link_attach); + +static int asoc_bus_match(struct device *dev, struct device_driver *drv) +{ + if (strstr(dev->bus_id, drv->name)) //lg also check id + return 1; + return 0; +} + +struct bus_type asoc_bus_type = { + .name = "asoc", + .match = asoc_bus_match, +}; +EXPORT_SYMBOL(asoc_bus_type); + +static int __init asoc_bus_init(void) +{ printk(KERN_INFO "ASoC version %s\n", SND_SOC_VERSION); - return platform_driver_register(&soc_driver); + return bus_register(&asoc_bus_type); } +subsys_initcall(asoc_bus_init); -static void snd_soc_exit(void) +static void __exit asoc_bus_exit(void) { - platform_driver_unregister(&soc_driver); + bus_unregister(&asoc_bus_type); } -module_init(snd_soc_init); -module_exit(snd_soc_exit); +module_exit(asoc_bus_exit); /* Module information */ MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 29a546fecacf..ce0554692f8b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -30,9 +30,6 @@ * Todo: * o DAPM power change sequencing - allow for configurable per * codec sequences. - * o Support for analogue bias optimisation. - * o Support for reduced codec oversampling rates. - * o Support for reduced codec bias currents. */ #include <linux/module.h> @@ -62,7 +59,7 @@ #define POP_DEBUG 0 #if POP_DEBUG -#define POP_TIME 500 /* 500 msecs - change if pop debug is too fast */ +#define POP_TIME 200 /* 500 msecs - change if pop debug is too fast */ #define pop_wait(time) schedule_timeout_uninterruptible(msecs_to_jiffies(time)) #define pop_dbg(format, arg...) printk(format, ## arg); pop_wait(POP_TIME) #else @@ -154,7 +151,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, } /* connect mux widget to it's interconnecting audio paths */ -static int dapm_connect_mux(struct snd_soc_codec *codec, +static int dapm_connect_mux(struct snd_soc_machine *machine, struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, struct snd_soc_dapm_path *path, const char *control_name, const struct snd_kcontrol_new *kcontrol) @@ -164,7 +161,7 @@ static int dapm_connect_mux(struct snd_soc_codec *codec, for (i = 0; i < e->mask; i++) { if (!(strcmp(control_name, e->texts[i]))) { - list_add(&path->list, &codec->dapm_paths); + list_add(&path->list, &machine->dapm_paths); list_add(&path->list_sink, &dest->sources); list_add(&path->list_source, &src->sinks); path->name = (char*)e->texts[i]; @@ -177,7 +174,7 @@ static int dapm_connect_mux(struct snd_soc_codec *codec, } /* connect mixer widget to it's interconnecting audio paths */ -static int dapm_connect_mixer(struct snd_soc_codec *codec, +static int dapm_connect_mixer(struct snd_soc_machine *machine, struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, struct snd_soc_dapm_path *path, const char *control_name) { @@ -186,7 +183,7 @@ static int dapm_connect_mixer(struct snd_soc_codec *codec, /* search for mixer kcontrol */ for (i = 0; i < dest->num_kcontrols; i++) { if (!strcmp(control_name, dest->kcontrols[i].name)) { - list_add(&path->list, &codec->dapm_paths); + list_add(&path->list, &machine->dapm_paths); list_add(&path->list_sink, &dest->sources); list_add(&path->list_source, &src->sinks); path->name = dest->kcontrols[i].name; @@ -227,7 +224,7 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget) snd_soc_write(codec, widget->reg, new); pop_wait(POP_TIME); } - dbg("reg old %x new %x change %d\n", old, new, change); + dbg("reg %d old %x new %x change %d\n", widget->reg, old, new, change); return change; } @@ -239,6 +236,7 @@ static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power) if (widget->muted && !power) return 0; + if (!widget->muted && power) return 0; @@ -277,7 +275,7 @@ static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power) } /* create new dapm mixer control */ -static int dapm_new_mixer(struct snd_soc_codec *codec, +static int dapm_new_mixer(struct snd_soc_machine *machine, struct snd_soc_dapm_widget *w) { int i, ret = 0; @@ -302,7 +300,7 @@ static int dapm_new_mixer(struct snd_soc_codec *codec, path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w, path->long_name); - ret = snd_ctl_add(codec->card, path->kcontrol); + ret = snd_ctl_add(machine->card, path->kcontrol); if (ret < 0) { printk(KERN_ERR "asoc: failed to add dapm kcontrol %s\n", path->long_name); @@ -316,7 +314,7 @@ static int dapm_new_mixer(struct snd_soc_codec *codec, } /* create new dapm mux control */ -static int dapm_new_mux(struct snd_soc_codec *codec, +static int dapm_new_mux(struct snd_soc_machine *machine, struct snd_soc_dapm_widget *w) { struct snd_soc_dapm_path *path = NULL; @@ -329,7 +327,7 @@ static int dapm_new_mux(struct snd_soc_codec *codec, } kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name); - ret = snd_ctl_add(codec->card, kcontrol); + ret = snd_ctl_add(machine->card, kcontrol); if (ret < 0) goto err; @@ -344,7 +342,7 @@ err: } /* create new dapm volume control */ -static int dapm_new_pga(struct snd_soc_codec *codec, +static int dapm_new_pga(struct snd_soc_machine *machine, struct snd_soc_dapm_widget *w) { struct snd_kcontrol *kcontrol; @@ -354,7 +352,7 @@ static int dapm_new_pga(struct snd_soc_codec *codec, return -EINVAL; kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name); - ret = snd_ctl_add(codec->card, kcontrol); + ret = snd_ctl_add(machine->card, kcontrol); if (ret < 0) { printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name); return ret; @@ -364,11 +362,11 @@ static int dapm_new_pga(struct snd_soc_codec *codec, } /* reset 'walked' bit for each dapm path */ -static inline void dapm_clear_walk(struct snd_soc_codec *codec) +static inline void dapm_clear_walk(struct snd_soc_machine *machine) { struct snd_soc_dapm_path *p; - list_for_each_entry(p, &codec->dapm_paths, list) + list_for_each_entry(p, &machine->dapm_paths, list) p->walked = 0; } @@ -381,7 +379,8 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) struct snd_soc_dapm_path *path; int con = 0; - if (widget->id == snd_soc_dapm_adc && widget->active) + if (widget->id == snd_soc_dapm_adc && (widget->active || + widget->machine->dapm_policy == SND_SOC_DAPM_POLICY_PATH)) return 1; if (widget->connected) { @@ -390,7 +389,8 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) return 1; /* connected jack or spk ? */ - if (widget->id == snd_soc_dapm_hp || widget->id == snd_soc_dapm_spk || + if (widget->id == snd_soc_dapm_hp || + widget->id == snd_soc_dapm_spk || widget->id == snd_soc_dapm_line) return 1; } @@ -417,8 +417,9 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) struct snd_soc_dapm_path *path; int con = 0; - /* active stream ? */ - if (widget->id == snd_soc_dapm_dac && widget->active) + /* active stream / path ? */ + if (widget->id == snd_soc_dapm_dac && (widget->active || + widget->machine->dapm_policy == SND_SOC_DAPM_POLICY_PATH)) return 1; if (widget->connected) { @@ -431,7 +432,8 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) return 1; /* connected jack ? */ - if (widget->id == snd_soc_dapm_mic || widget->id == snd_soc_dapm_line) + if (widget->id == snd_soc_dapm_mic || + widget->id == snd_soc_dapm_line) return 1; } @@ -457,11 +459,15 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) * o Input pin to Output pin (bypass, sidetone) * o DAC to ADC (loopback). */ -static int dapm_power_widgets(struct snd_soc_codec *codec, int event) +static int dapm_power_widgets(struct snd_soc_machine *machine, int event) { struct snd_soc_dapm_widget *w; int in, out, i, c = 1, *seq = NULL, ret = 0, power_change, power; + /* manual dapm_policy ? */ + if (machine->dapm_policy == SND_SOC_DAPM_POLICY_MANUAL) + return 0; + /* do we have a sequenced stream event */ if (event == SND_SOC_DAPM_STREAM_START) { c = ARRAY_SIZE(dapm_up_seq); @@ -472,7 +478,7 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) } for(i = 0; i < c; i++) { - list_for_each_entry(w, &codec->dapm_widgets, list) { + list_for_each_entry(w, &machine->dapm_widgets, list) { /* is widget in stream order */ if (seq && seq[i] && w->id != seq[i]) @@ -482,10 +488,17 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) if (w->id == snd_soc_dapm_vmid) continue; + /* all on dapm_policy */ + if (machine->dapm_policy == SND_SOC_DAPM_POLICY_ALL_ON) { + w->power = 1; + dapm_update_bits(w); + continue; + } + /* active ADC */ if (w->id == snd_soc_dapm_adc && w->active) { in = is_connected_input_ep(w); - dapm_clear_walk(w->codec); + dapm_clear_walk(w->machine); w->power = (in != 0) ? 1 : 0; dapm_update_bits(w); continue; @@ -494,7 +507,7 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) /* active DAC */ if (w->id == snd_soc_dapm_dac && w->active) { out = is_connected_output_ep(w); - dapm_clear_walk(w->codec); + dapm_clear_walk(w->machine); w->power = (out != 0) ? 1 : 0; dapm_update_bits(w); continue; @@ -502,19 +515,22 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) /* programmable gain/attenuation */ if (w->id == snd_soc_dapm_pga) { - int on; in = is_connected_input_ep(w); - dapm_clear_walk(w->codec); + dapm_clear_walk(w->machine); out = is_connected_output_ep(w); - dapm_clear_walk(w->codec); - w->power = on = (out != 0 && in != 0) ? 1 : 0; + dapm_clear_walk(w->machine); + power = (out != 0 && in != 0) ? 1 : 0; + power_change = (w->power == power) ? 0: 1; + w->power = power; - if (!on) - dapm_set_pga(w, on); /* lower volume to reduce pops */ + if (!power) + dapm_set_pga(w, power); /* lower volume to reduce pops */ dapm_update_bits(w); - if (on) - dapm_set_pga(w, on); /* restore volume from zero */ + if (power) + dapm_set_pga(w, power); /* restore volume from zero */ + if (w->event) + goto event; continue; } @@ -552,13 +568,14 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) /* all other widgets */ in = is_connected_input_ep(w); - dapm_clear_walk(w->codec); + dapm_clear_walk(w->machine); out = is_connected_output_ep(w); - dapm_clear_walk(w->codec); + dapm_clear_walk(w->machine); power = (out != 0 && in != 0) ? 1 : 0; power_change = (w->power == power) ? 0: 1; w->power = power; +event: /* call any power change event handlers */ if (power_change) { if (w->event) { @@ -602,15 +619,15 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) } #if DAPM_DEBUG -static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action) +static void dbg_dump_dapm(struct snd_soc_machine *machine, const char *action) { struct snd_soc_dapm_widget *w; struct snd_soc_dapm_path *p = NULL; int in, out; - printk("DAPM %s %s\n", codec->name, action); + printk("DAPM %s %s\n", machine->name, action); - list_for_each_entry(w, &codec->dapm_widgets, list) { + list_for_each_entry(w, &machine->dapm_widgets, list) { /* only display widgets that effect routing */ switch (w->id) { @@ -633,9 +650,9 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action) case snd_soc_dapm_mixer: if (w->name) { in = is_connected_input_ep(w); - dapm_clear_walk(w->codec); + dapm_clear_walk(w->machine); out = is_connected_output_ep(w); - dapm_clear_walk(w->codec); + dapm_clear_walk(w->machine); printk("%s: %s in %d out %d\n", w->name, w->power ? "On":"Off",in, out); @@ -671,7 +688,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, return 0; /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->codec->dapm_paths, list) { + list_for_each_entry(path, &widget->machine->dapm_paths, list) { if (path->kcontrol != kcontrol) continue; @@ -687,12 +704,12 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, } if (found) - dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP); + dapm_power_widgets(widget->machine, SND_SOC_DAPM_STREAM_NOP); 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,14 +717,14 @@ 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)) return 0; /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->codec->dapm_paths, list) { + list_for_each_entry(path, &widget->machine->dapm_paths, list) { if (path->kcontrol != kcontrol) continue; @@ -723,7 +740,7 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, } if (found) - dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP); + dapm_power_widgets(widget->machine, SND_SOC_DAPM_STREAM_NOP); return 0; } @@ -732,13 +749,14 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, static ssize_t dapm_widget_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct snd_soc_device *devdata = dev_get_drvdata(dev); - struct snd_soc_codec *codec = devdata->codec; + struct platform_device *pdev = to_platform_device(dev); + struct snd_soc_machine *machine = pdev->dev.driver_data; struct snd_soc_dapm_widget *w; + struct snd_soc_pcm_link *p; int count = 0; char *state = "not set"; - list_for_each_entry(w, &codec->dapm_widgets, list) { + list_for_each_entry(w, &machine->dapm_widgets, list) { /* only display widgets that burnm power */ switch (w->id) { @@ -760,24 +778,29 @@ static ssize_t dapm_widget_show(struct device *dev, } } - switch(codec->dapm_state){ - case SNDRV_CTL_POWER_D0: - state = "D0"; - break; - case SNDRV_CTL_POWER_D1: - state = "D1"; - break; - case SNDRV_CTL_POWER_D2: - state = "D2"; - break; - case SNDRV_CTL_POWER_D3hot: - state = "D3hot"; - break; - case SNDRV_CTL_POWER_D3cold: - state = "D3cold"; - break; + list_for_each_entry(p, &machine->active_list, active_list) { + struct snd_soc_codec *codec = p->codec; + + switch (codec->dapm_state) { + case SNDRV_CTL_POWER_D0: + state = "D0"; + break; + case SNDRV_CTL_POWER_D1: + state = "D1"; + break; + case SNDRV_CTL_POWER_D2: + state = "D2"; + break; + case SNDRV_CTL_POWER_D3hot: + state = "D3hot"; + break; + case SNDRV_CTL_POWER_D3cold: + state = "D3cold"; + break; + } + count += sprintf(buf + count, "%s: PM State: %s\n", + p->name, state); } - count += sprintf(buf + count, "PM State: %s\n", state); return count; } @@ -801,17 +824,17 @@ static void snd_soc_dapm_sys_remove(struct device *dev) } /* free all dapm widgets and resources */ -static void dapm_free_widgets(struct snd_soc_codec *codec) +static void dapm_free_widgets(struct snd_soc_machine *machine) { struct snd_soc_dapm_widget *w, *next_w; struct snd_soc_dapm_path *p, *next_p; - list_for_each_entry_safe(w, next_w, &codec->dapm_widgets, list) { + list_for_each_entry_safe(w, next_w, &machine->dapm_widgets, list) { list_del(&w->list); kfree(w); } - list_for_each_entry_safe(p, next_p, &codec->dapm_paths, list) { + list_for_each_entry_safe(p, next_p, &machine->dapm_paths, list) { list_del(&p->list); kfree(p->long_name); kfree(p); @@ -827,9 +850,9 @@ static void dapm_free_widgets(struct snd_soc_codec *codec) * * Returns 0 for success. */ -int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec) +int snd_soc_dapm_sync_endpoints(struct snd_soc_machine *machine) { - return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP); + return dapm_power_widgets(machine, SND_SOC_DAPM_STREAM_NOP); } EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints); @@ -846,7 +869,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints); * * Returns 0 for success else error. */ -int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink, +int snd_soc_dapm_connect_input(struct snd_soc_machine *machine, const char *sink, const char * control, const char *source) { struct snd_soc_dapm_path *path; @@ -854,7 +877,7 @@ int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink, int ret = 0; /* find src and dest widgets */ - list_for_each_entry(w, &codec->dapm_widgets, list) { + list_for_each_entry(w, &machine->dapm_widgets, list) { if (!wsink && !(strcmp(w->name, sink))) { wsink = w; @@ -896,7 +919,7 @@ int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink, /* connect static paths */ if (control == NULL) { - list_add(&path->list, &codec->dapm_paths); + list_add(&path->list, &machine->dapm_paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 1; @@ -914,20 +937,20 @@ int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink, case snd_soc_dapm_vmid: case snd_soc_dapm_pre: case snd_soc_dapm_post: - list_add(&path->list, &codec->dapm_paths); + list_add(&path->list, &machine->dapm_paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 1; return 0; case snd_soc_dapm_mux: - ret = dapm_connect_mux(codec, wsource, wsink, path, control, + ret = dapm_connect_mux(machine, wsource, wsink, path, control, &wsink->kcontrols[0]); if (ret != 0) goto err; break; case snd_soc_dapm_switch: case snd_soc_dapm_mixer: - ret = dapm_connect_mixer(codec, wsource, wsink, path, control); + ret = dapm_connect_mixer(machine, wsource, wsink, path, control); if (ret != 0) goto err; break; @@ -935,7 +958,7 @@ int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink, case snd_soc_dapm_mic: case snd_soc_dapm_line: case snd_soc_dapm_spk: - list_add(&path->list, &codec->dapm_paths); + list_add(&path->list, &machine->dapm_paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 0; @@ -959,12 +982,12 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input); * * Returns 0 for success. */ -int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) +int snd_soc_dapm_new_widgets(struct snd_soc_machine *machine) { struct snd_soc_dapm_widget *w; - mutex_lock(&codec->mutex); - list_for_each_entry(w, &codec->dapm_widgets, list) + mutex_lock(&machine->mutex); + list_for_each_entry(w, &machine->dapm_widgets, list) { if (w->new) continue; @@ -972,15 +995,15 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) switch(w->id) { case snd_soc_dapm_switch: case snd_soc_dapm_mixer: - dapm_new_mixer(codec, w); + dapm_new_mixer(machine, w); break; case snd_soc_dapm_mux: - dapm_new_mux(codec, w); + dapm_new_mux(machine, w); break; case snd_soc_dapm_adc: case snd_soc_dapm_dac: case snd_soc_dapm_pga: - dapm_new_pga(codec, w); + dapm_new_pga(machine, w); break; case snd_soc_dapm_input: case snd_soc_dapm_output: @@ -997,8 +1020,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) w->new = 1; } - dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP); - mutex_unlock(&codec->mutex); + dapm_power_widgets(machine, SND_SOC_DAPM_STREAM_NOP); + mutex_unlock(&machine->mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); @@ -1019,8 +1042,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 +1059,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 +1085,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 +1094,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; } @@ -1199,8 +1224,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); * * Returns 0 for success else error. */ -int snd_soc_dapm_new_control(struct snd_soc_codec *codec, - const struct snd_soc_dapm_widget *widget) +int snd_soc_dapm_new_control(struct snd_soc_machine *machine, + struct snd_soc_codec *codec, const struct snd_soc_dapm_widget *widget) { struct snd_soc_dapm_widget *w; @@ -1208,10 +1233,11 @@ int snd_soc_dapm_new_control(struct snd_soc_codec *codec, return -ENOMEM; w->codec = codec; + w->machine = machine; INIT_LIST_HEAD(&w->sources); INIT_LIST_HEAD(&w->sinks); INIT_LIST_HEAD(&w->list); - list_add(&w->list, &codec->dapm_widgets); + list_add(&w->list, &machine->dapm_widgets); /* machine layer set ups unconnected pins and insertions */ w->connected = 1; @@ -1230,7 +1256,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control); * * Returns 0 for success else error. */ -int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, +int snd_soc_dapm_stream_event(struct snd_soc_machine *machine, char *stream, int event) { struct snd_soc_dapm_widget *w; @@ -1238,8 +1264,8 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, if (stream == NULL) return 0; - mutex_lock(&codec->mutex); - list_for_each_entry(w, &codec->dapm_widgets, list) + mutex_lock(&machine->mutex); + list_for_each_entry(w, &machine->dapm_widgets, list) { if (!w->sname) continue; @@ -1271,15 +1297,38 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, } } } - mutex_unlock(&codec->mutex); + mutex_unlock(&machine->mutex); - dapm_power_widgets(codec, event); - dump_dapm(codec, __FUNCTION__); + dapm_power_widgets(machine, event); + dump_dapm(machine, __FUNCTION__); return 0; } 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_pcm_link *pcm_link, int event) +{ + struct snd_soc_codec *codec = pcm_link->codec; + struct snd_soc_machine *machine = pcm_link->machine; + + if (machine->ops && machine->ops->dapm_event) + machine->ops->dapm_event(machine, event); + if (codec->ops && codec->ops->dapm_event) + codec->ops->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) @@ -1289,12 +1338,12 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); * * Returns 0 for success else error. */ -int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec, +int snd_soc_dapm_set_endpoint(struct snd_soc_machine *machine, char *endpoint, int status) { struct snd_soc_dapm_widget *w; - list_for_each_entry(w, &codec->dapm_widgets, list) { + list_for_each_entry(w, &machine->dapm_widgets, list) { if (!strcmp(w->name, endpoint)) { w->connected = status; } @@ -1310,15 +1359,29 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint); * * Free all dapm widgets and resources. */ -void snd_soc_dapm_free(struct snd_soc_device *socdev) +void snd_soc_dapm_free(struct snd_soc_machine *machine) { - struct snd_soc_codec *codec = socdev->codec; - - snd_soc_dapm_sys_remove(socdev->dev); - dapm_free_widgets(codec); + snd_soc_dapm_sys_remove(&machine->pdev->dev); + dapm_free_widgets(machine); } EXPORT_SYMBOL_GPL(snd_soc_dapm_free); +/** + * snd_soc_dapm_set_policy - set DAPM policy + * @socdev: SoC device + * + * Policy only applies to the platform, path and stream DAPM power domains. + */ +int snd_soc_dapm_set_policy(struct snd_soc_machine *machine, int dapm_policy) +{ + mutex_lock(&machine->mutex); + machine->dapm_policy = dapm_policy; + mutex_unlock(&machine->mutex); + snd_soc_dapm_sync_endpoints(machine); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_set_policy); + /* Module information */ MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC"); |