* soc-core.c -- ALSA SoC Audio Layer
*
* Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
* Author: Liam Girdwood
* liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * with code, comments and ideas from :-
+ * Richard Purdie <richard@openedhand.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; either version 2 of the License, or (at your
* option) any later version.
*
- * Revision history
- * 12th Aug 2005 Initial version.
- * 25th Oct 2005 Working Codec, Interface and Platform registration.
- *
* TODO:
* o Add hw rules to enforce rates, etc.
* o More testing with other codecs/machines.
#include <linux/pm.h>
#include <linux/bitops.h>
#include <linux/platform_device.h>
-#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#else
#define dbg(format, arg...)
#endif
-/* debug DAI capabilities matching */
-#define SOC_DEBUG_DAI 0
-#if SOC_DEBUG_DAI
-#define dbgc(format, arg...) printk(format, ## arg)
-#else
-#define dbgc(format, arg...)
-#endif
static DEFINE_MUTEX(pcm_mutex);
static DEFINE_MUTEX(io_mutex);
-static struct workqueue_struct *soc_workq;
-static struct work_struct soc_stream_work;
static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
-/* supported sample rates */
-/* ATTENTION: these values depend on the definition in pcm.h! */
-static const unsigned int rates[] = {
- 5512, 8000, 11025, 16000, 22050, 32000, 44100,
- 48000, 64000, 88200, 96000, 176400, 192000
-};
-
/*
* 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.
module_param(pmdown_time, int, 0);
MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
+/*
+ * This function forces any delayed work to be queued and run.
+ */
+static int run_delayed_work(struct delayed_work *dwork)
+{
+ int ret;
+
+ /* cancel any work waiting to be queued. */
+ ret = cancel_delayed_work(dwork);
+
+ /* if there was any work waiting then we run it now and
+ * wait for it's completion */
+ if (ret) {
+ schedule_delayed_work(dwork, 0);
+ flush_scheduled_work();
+ }
+ return ret;
+}
+
#ifdef CONFIG_SND_SOC_AC97_BUS
/* unregister ac97 codec */
static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
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 NULL;
}
-/* get rate format from rate */
-static inline int soc_get_rate_format(int rate)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(rates); i++) {
- if (rates[i] == rate)
- return 1 << i;
- }
- return 0;
-}
-
-/* gets the audio system mclk/sysclk for the given parameters */
-static unsigned inline int soc_get_mclk(struct snd_soc_pcm_runtime *rtd,
- struct snd_soc_clock_info *info)
-{
- struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_machine *machine = socdev->machine;
- int i;
-
- /* find the matching machine config and get it's mclk for the given
- * sample rate and hardware format */
- for(i = 0; i < machine->num_links; i++) {
- if (machine->dai_link[i].cpu_dai == rtd->cpu_dai &&
- machine->dai_link[i].config_sysclk)
- return machine->dai_link[i].config_sysclk(rtd, info);
- }
- return 0;
-}
-
-/* changes a bitclk multiplier mask to a divider mask */
-static u16 soc_bfs_mult_to_div(u16 bfs, int rate, unsigned int mclk,
- unsigned int pcmfmt, unsigned int chn)
-{
- int i, j;
- u16 bfs_ = 0;
- int size = snd_pcm_format_physical_width(pcmfmt), min = 0;
-
- if (size <= 0)
- return 0;
-
- /* the minimum bit clock that has enough bandwidth */
- min = size * rate * chn;
- dbgc("mult --> div min bclk %d with mclk %d\n", min, mclk);
-
- for (i = 0; i < 16; i++) {
- if ((bfs >> i) & 0x1) {
- j = rate * SND_SOC_FSB_REAL(1<<i);
-
- if (j >= min) {
- bfs_ |= SND_SOC_FSBD(mclk/j);
- dbgc("mult --> div support mult %d\n",
- SND_SOC_FSB_REAL(1<<i));
- }
- }
- }
-
- return bfs_;
-}
-
-/* changes a bitclk divider mask to a multiplier mask */
-static u16 soc_bfs_div_to_mult(u16 bfs, int rate, unsigned int mclk,
- unsigned int pcmfmt, unsigned int chn)
-{
- int i, j;
- u16 bfs_ = 0;
-
- int size = snd_pcm_format_physical_width(pcmfmt), min = 0;
-
- if (size <= 0)
- return 0;
-
- /* the minimum bit clock that has enough bandwidth */
- min = size * rate * chn;
- dbgc("div to mult min bclk %d with mclk %d\n", min, mclk);
-
- for (i = 0; i < 16; i++) {
- if ((bfs >> i) & 0x1) {
- j = mclk / (SND_SOC_FSBD_REAL(1<<i));
- if (j >= min) {
- bfs_ |= SND_SOC_FSB(j/rate);
- dbgc("div --> mult support div %d\n",
- SND_SOC_FSBD_REAL(1<<i));
- }
- }
- }
-
- return bfs_;
-}
-
-/* Matches codec DAI and SoC CPU DAI hardware parameters */
-static int soc_hw_match_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai_mode *codec_dai_mode = NULL;
- struct snd_soc_dai_mode *cpu_dai_mode = NULL;
- struct snd_soc_clock_info clk_info;
- unsigned int fs, mclk, codec_bfs, cpu_bfs, rate = params_rate(params),
- chn, j, k, cpu_bclk, codec_bclk, pcmrate;
- u16 fmt = 0;
-
- dbg("asoc: match version %s\n", SND_SOC_VERSION);
- clk_info.rate = rate;
- pcmrate = soc_get_rate_format(rate);
-
- /* try and find a match from the codec and cpu DAI capabilities */
- for (j = 0; j < rtd->codec_dai->caps.num_modes; j++) {
- for (k = 0; k < rtd->cpu_dai->caps.num_modes; k++) {
- codec_dai_mode = &rtd->codec_dai->caps.mode[j];
- cpu_dai_mode = &rtd->cpu_dai->caps.mode[k];
-
- if (!(codec_dai_mode->pcmrate & cpu_dai_mode->pcmrate &
- pcmrate)) {
- dbgc("asoc: DAI[%d:%d] failed to match rate\n", j, k);
- continue;
- }
-
- fmt = codec_dai_mode->fmt & cpu_dai_mode->fmt;
- if (!(fmt & SND_SOC_DAIFMT_FORMAT_MASK)) {
- dbgc("asoc: DAI[%d:%d] failed to match format\n", j, k);
- continue;
- }
-
- if (!(fmt & SND_SOC_DAIFMT_CLOCK_MASK)) {
- dbgc("asoc: DAI[%d:%d] failed to match clock masters\n",
- j, k);
- continue;
- }
-
- if (!(fmt & SND_SOC_DAIFMT_INV_MASK)) {
- dbgc("asoc: DAI[%d:%d] failed to match invert\n", j, k);
- continue;
- }
-
- if (!(codec_dai_mode->pcmfmt & cpu_dai_mode->pcmfmt)) {
- dbgc("asoc: DAI[%d:%d] failed to match pcm format\n", j, k);
- continue;
- }
-
- if (!(codec_dai_mode->pcmdir & cpu_dai_mode->pcmdir)) {
- dbgc("asoc: DAI[%d:%d] failed to match direction\n", j, k);
- continue;
- }
-
- /* todo - still need to add tdm selection */
- rtd->cpu_dai->dai_runtime.fmt =
- rtd->codec_dai->dai_runtime.fmt =
- 1 << (ffs(fmt & SND_SOC_DAIFMT_FORMAT_MASK) -1) |
- 1 << (ffs(fmt & SND_SOC_DAIFMT_CLOCK_MASK) - 1) |
- 1 << (ffs(fmt & SND_SOC_DAIFMT_INV_MASK) - 1);
- clk_info.bclk_master =
- rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK;
-
- /* make sure the ratio between rate and master
- * clock is acceptable*/
- fs = (cpu_dai_mode->fs & codec_dai_mode->fs);
- if (fs == 0) {
- dbgc("asoc: DAI[%d:%d] failed to match FS\n", j, k);
- continue;
- }
- clk_info.fs = rtd->cpu_dai->dai_runtime.fs =
- rtd->codec_dai->dai_runtime.fs = fs;
-
- /* calculate audio system clocking using slowest clocks possible*/
- mclk = soc_get_mclk(rtd, &clk_info);
- if (mclk == 0) {
- dbgc("asoc: DAI[%d:%d] configuration not clockable\n", j, k);
- dbgc("asoc: rate %d fs %d master %x\n", rate, fs,
- clk_info.bclk_master);
- continue;
- }
-
- /* calculate word size (per channel) and frame size */
- rtd->codec_dai->dai_runtime.pcmfmt =
- rtd->cpu_dai->dai_runtime.pcmfmt =
- 1 << params_format(params);
-
- chn = params_channels(params);
- /* i2s always has left and right */
- if (params_channels(params) == 1 &&
- rtd->cpu_dai->dai_runtime.fmt & (SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_LEFT_J))
- chn <<= 1;
-
- /* Calculate bfs - the ratio between bitclock and the sample rate
- * We must take into consideration the dividers and multipliers
- * used in the codec and cpu DAI modes. We always choose the
- * lowest possible clocks to reduce power.
- */
- if (codec_dai_mode->flags & cpu_dai_mode->flags &
- SND_SOC_DAI_BFS_DIV) {
- /* cpu & codec bfs dividers */
- rtd->cpu_dai->dai_runtime.bfs =
- rtd->codec_dai->dai_runtime.bfs =
- 1 << (fls(codec_dai_mode->bfs & cpu_dai_mode->bfs) - 1);
- } else if (codec_dai_mode->flags & SND_SOC_DAI_BFS_DIV) {
- /* normalise bfs codec divider & cpu mult */
- codec_bfs = soc_bfs_div_to_mult(codec_dai_mode->bfs, rate,
- mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
- rtd->cpu_dai->dai_runtime.bfs =
- 1 << (ffs(codec_bfs & cpu_dai_mode->bfs) - 1);
- cpu_bfs = soc_bfs_mult_to_div(cpu_dai_mode->bfs, rate, mclk,
- rtd->codec_dai->dai_runtime.pcmfmt, chn);
- rtd->codec_dai->dai_runtime.bfs =
- 1 << (fls(codec_dai_mode->bfs & cpu_bfs) - 1);
- } else if (cpu_dai_mode->flags & SND_SOC_DAI_BFS_DIV) {
- /* normalise bfs codec mult & cpu divider */
- codec_bfs = soc_bfs_mult_to_div(codec_dai_mode->bfs, rate,
- mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
- rtd->cpu_dai->dai_runtime.bfs =
- 1 << (fls(codec_bfs & cpu_dai_mode->bfs) -1);
- cpu_bfs = soc_bfs_div_to_mult(cpu_dai_mode->bfs, rate, mclk,
- rtd->codec_dai->dai_runtime.pcmfmt, chn);
- rtd->codec_dai->dai_runtime.bfs =
- 1 << (ffs(codec_dai_mode->bfs & cpu_bfs) -1);
- } else {
- /* codec & cpu bfs rate multipliers */
- rtd->cpu_dai->dai_runtime.bfs =
- rtd->codec_dai->dai_runtime.bfs =
- 1 << (ffs(codec_dai_mode->bfs & cpu_dai_mode->bfs) -1);
- }
-
- /* make sure the bit clock speed is acceptable */
- if (!rtd->cpu_dai->dai_runtime.bfs ||
- !rtd->codec_dai->dai_runtime.bfs) {
- dbgc("asoc: DAI[%d:%d] failed to match BFS\n", j, k);
- dbgc("asoc: cpu_dai %x codec %x\n",
- rtd->cpu_dai->dai_runtime.bfs,
- rtd->codec_dai->dai_runtime.bfs);
- dbgc("asoc: mclk %d hwfmt %x\n", mclk, fmt);
- continue;
- }
-
- goto found;
- }
- }
- printk(KERN_ERR "asoc: no matching DAI found between codec and CPU\n");
- return -EINVAL;
-
-found:
- /* we have matching DAI's, so complete the runtime info */
- rtd->codec_dai->dai_runtime.pcmrate =
- rtd->cpu_dai->dai_runtime.pcmrate =
- soc_get_rate_format(rate);
-
- rtd->codec_dai->dai_runtime.priv = codec_dai_mode->priv;
- rtd->cpu_dai->dai_runtime.priv = cpu_dai_mode->priv;
- rtd->codec_dai->dai_runtime.flags = codec_dai_mode->flags;
- rtd->cpu_dai->dai_runtime.flags = cpu_dai_mode->flags;
-
- /* for debug atm */
- dbg("asoc: DAI[%d:%d] Match OK\n", j, k);
- if (rtd->codec_dai->dai_runtime.flags == SND_SOC_DAI_BFS_DIV) {
- codec_bclk = (rtd->codec_dai->dai_runtime.fs * params_rate(params)) /
- SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs);
- dbg("asoc: codec fs %d mclk %d bfs div %d bclk %d\n",
- rtd->codec_dai->dai_runtime.fs, mclk,
- SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs), codec_bclk);
- } else {
- codec_bclk = params_rate(params) *
- SND_SOC_FSB_REAL(rtd->codec_dai->dai_runtime.bfs);
- dbg("asoc: codec fs %d mclk %d bfs mult %d bclk %d\n",
- rtd->codec_dai->dai_runtime.fs, mclk,
- SND_SOC_FSB_REAL(rtd->codec_dai->dai_runtime.bfs), codec_bclk);
- }
- if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_DIV) {
- cpu_bclk = (rtd->cpu_dai->dai_runtime.fs * params_rate(params)) /
- SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs);
- dbg("asoc: cpu fs %d mclk %d bfs div %d bclk %d\n",
- rtd->cpu_dai->dai_runtime.fs, mclk,
- SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk);
- } else {
- cpu_bclk = params_rate(params) *
- SND_SOC_FSB_REAL(rtd->cpu_dai->dai_runtime.bfs);
- dbg("asoc: cpu fs %d mclk %d bfs mult %d bclk %d\n",
- rtd->cpu_dai->dai_runtime.fs, mclk,
- SND_SOC_FSB_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk);
- }
-
- /*
- * Check we have matching bitclocks. If we don't then it means the
- * sysclock returned by either the codec or cpu DAI (selected by the
- * machine sysclock function) is wrong compared with the supported DAI
- * modes for the codec or cpu DAI.
- */
- if (cpu_bclk != codec_bclk){
- printk(KERN_ERR
- "asoc: codec and cpu bitclocks differ, audio may be wrong speed\n"
- );
- printk(KERN_ERR "asoc: codec %d != cpu %d\n", codec_bclk, cpu_bclk);
- }
-
- switch(rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- dbg("asoc: DAI codec BCLK master, LRC master\n");
- break;
- case SND_SOC_DAIFMT_CBS_CFM:
- dbg("asoc: DAI codec BCLK slave, LRC master\n");
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- dbg("asoc: DAI codec BCLK master, LRC slave\n");
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- dbg("asoc: DAI codec BCLK slave, LRC slave\n");
- break;
- }
- dbg("asoc: mode %x, invert %x\n",
- rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK,
- rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK);
- dbg("asoc: audio rate %d chn %d fmt %x\n", params_rate(params),
- params_channels(params), params_format(params));
-
- return 0;
-}
-
-static inline u32 get_rates(struct snd_soc_dai_mode *modes, int nmodes)
-{
- int i;
- u32 rates = 0;
-
- for(i = 0; i < nmodes; i++)
- rates |= modes[i].pcmrate;
-
- return rates;
-}
-
-static inline u64 get_formats(struct snd_soc_dai_mode *modes, int nmodes)
-{
- int i;
- u64 formats = 0;
-
- for(i = 0; i < nmodes; i++)
- formats |= modes[i].pcmfmt;
-
- return formats;
-}
-
/*
* 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
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_machine *machine = socdev->machine;
+ struct snd_soc_dai_link *machine = rtd->dai;
struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_codec_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_cpu_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
int ret = 0;
mutex_lock(&pcm_mutex);
/* startup the audio subsystem */
- if (rtd->cpu_dai->ops.startup) {
- ret = rtd->cpu_dai->ops.startup(substream);
+ if (cpu_dai->ops.startup) {
+ ret = cpu_dai->ops.startup(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open interface %s\n",
- rtd->cpu_dai->name);
+ cpu_dai->name);
goto out;
}
}
}
}
- if (machine->ops && machine->ops->startup) {
- ret = machine->ops->startup(substream);
+ if (codec_dai->ops.startup) {
+ ret = codec_dai->ops.startup(substream);
if (ret < 0) {
- printk(KERN_ERR "asoc: %s startup failed\n", machine->name);
- goto machine_err;
+ printk(KERN_ERR "asoc: can't open codec %s\n",
+ codec_dai->name);
+ goto codec_dai_err;
}
}
- if (rtd->codec_dai->ops.startup) {
- ret = rtd->codec_dai->ops.startup(substream);
+ if (machine->ops && machine->ops->startup) {
+ ret = machine->ops->startup(substream);
if (ret < 0) {
- printk(KERN_ERR "asoc: can't open codec %s\n",
- rtd->codec_dai->name);
- goto codec_dai_err;
+ printk(KERN_ERR "asoc: %s startup failed\n", machine->name);
+ goto machine_err;
}
}
- /* create runtime params from DMA, codec and cpu DAI */
- if (runtime->hw.rates)
- runtime->hw.rates &=
- get_rates(codec_dai->caps.mode, codec_dai->caps.num_modes) &
- get_rates(cpu_dai->caps.mode, cpu_dai->caps.num_modes);
- else
- runtime->hw.rates =
- get_rates(codec_dai->caps.mode, codec_dai->caps.num_modes) &
- get_rates(cpu_dai->caps.mode, cpu_dai->caps.num_modes);
- if (runtime->hw.formats)
- runtime->hw.formats &=
- get_formats(codec_dai->caps.mode, codec_dai->caps.num_modes) &
- get_formats(cpu_dai->caps.mode, cpu_dai->caps.num_modes);
- else
- runtime->hw.formats =
- get_formats(codec_dai->caps.mode, codec_dai->caps.num_modes) &
- get_formats(cpu_dai->caps.mode, cpu_dai->caps.num_modes);
-
/* Check that the codec and cpu DAI's are compatible */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
runtime->hw.rate_min =
- max(rtd->codec_dai->playback.rate_min,
- rtd->cpu_dai->playback.rate_min);
+ max(codec_dai->playback.rate_min, cpu_dai->playback.rate_min);
runtime->hw.rate_max =
- min(rtd->codec_dai->playback.rate_max,
- rtd->cpu_dai->playback.rate_max);
+ min(codec_dai->playback.rate_max, cpu_dai->playback.rate_max);
runtime->hw.channels_min =
- max(rtd->codec_dai->playback.channels_min,
- rtd->cpu_dai->playback.channels_min);
+ max(codec_dai->playback.channels_min,
+ cpu_dai->playback.channels_min);
runtime->hw.channels_max =
- min(rtd->codec_dai->playback.channels_max,
- rtd->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;
+ runtime->hw.rates =
+ codec_dai->playback.rates & cpu_dai->playback.rates;
} else {
runtime->hw.rate_min =
- max(rtd->codec_dai->capture.rate_min,
- rtd->cpu_dai->capture.rate_min);
+ max(codec_dai->capture.rate_min, cpu_dai->capture.rate_min);
runtime->hw.rate_max =
- min(rtd->codec_dai->capture.rate_max,
- rtd->cpu_dai->capture.rate_max);
+ min(codec_dai->capture.rate_max, cpu_dai->capture.rate_max);
runtime->hw.channels_min =
- max(rtd->codec_dai->capture.channels_min,
- rtd->cpu_dai->capture.channels_min);
+ max(codec_dai->capture.channels_min,
+ cpu_dai->capture.channels_min);
runtime->hw.channels_max =
- min(rtd->codec_dai->capture.channels_max,
- rtd->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;
+ runtime->hw.rates =
+ codec_dai->capture.rates & cpu_dai->capture.rates;
}
snd_pcm_limit_hw_rates(runtime);
if (!runtime->hw.rates) {
printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
- rtd->codec_dai->name, rtd->cpu_dai->name);
- goto codec_dai_err;
+ codec_dai->name, cpu_dai->name);
+ goto machine_err;
}
if (!runtime->hw.formats) {
printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
- rtd->codec_dai->name, rtd->cpu_dai->name);
- goto codec_dai_err;
+ codec_dai->name, cpu_dai->name);
+ goto machine_err;
}
if (!runtime->hw.channels_min || !runtime->hw.channels_max) {
printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
- rtd->codec_dai->name, rtd->cpu_dai->name);
- goto codec_dai_err;
+ codec_dai->name, cpu_dai->name);
+ goto machine_err;
}
- dbg("asoc: %s <-> %s info:\n", rtd->codec_dai->name, rtd->cpu_dai->name);
+ dbg("asoc: %s <-> %s info:\n",codec_dai->name, cpu_dai->name);
dbg("asoc: rate mask 0x%x\n", runtime->hw.rates);
dbg("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
runtime->hw.channels_max);
dbg("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
runtime->hw.rate_max);
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- rtd->cpu_dai->playback.active = rtd->codec_dai->playback.active = 1;
+ cpu_dai->playback.active = codec_dai->playback.active = 1;
else
- rtd->cpu_dai->capture.active = rtd->codec_dai->capture.active = 1;
- rtd->cpu_dai->active = rtd->codec_dai->active = 1;
- rtd->cpu_dai->runtime = runtime;
+ cpu_dai->capture.active = codec_dai->capture.active = 1;
+ cpu_dai->active = codec_dai->active = 1;
+ cpu_dai->runtime = runtime;
socdev->codec->active++;
mutex_unlock(&pcm_mutex);
return 0;
-codec_dai_err:
+machine_err:
if (machine->ops && machine->ops->shutdown)
machine->ops->shutdown(substream);
-machine_err:
+codec_dai_err:
if (platform->pcm_ops->close)
platform->pcm_ops->close(substream);
platform_err:
- if (rtd->cpu_dai->ops.shutdown)
- rtd->cpu_dai->ops.shutdown(substream);
+ if (cpu_dai->ops.shutdown)
+ cpu_dai->ops.shutdown(substream);
out:
mutex_unlock(&pcm_mutex);
return ret;
}
/*
- * Power down the audio subsytem pmdown_time msecs after close is called.
+ * Power down the audio subsystem pmdown_time msecs after close is called.
* This is to ensure there are no pops or clicks in between any music tracks
* due to DAPM power cycling.
*/
-static void close_delayed_work(void *data)
+static void close_delayed_work(struct work_struct *work)
{
- struct snd_soc_device *socdev = data;
+ 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;
/* are we waiting on this codec DAI stream */
if (codec_dai->pop_wait == 1) {
+ /* Reduce power 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_set_bias_level(socdev,
+ SND_SOC_BIAS_PREPARE);
+ }
+
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 */
+ /* Fall into standby 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_set_bias_level(socdev,
+ SND_SOC_BIAS_STANDBY);
}
}
}
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_machine *machine = socdev->machine;
+ 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;
mutex_lock(&pcm_mutex);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- rtd->cpu_dai->playback.active = rtd->codec_dai->playback.active = 0;
+ cpu_dai->playback.active = codec_dai->playback.active = 0;
else
- rtd->cpu_dai->capture.active = rtd->codec_dai->capture.active = 0;
+ cpu_dai->capture.active = codec_dai->capture.active = 0;
- if (rtd->codec_dai->playback.active == 0 &&
- rtd->codec_dai->capture.active == 0) {
- rtd->cpu_dai->active = rtd->codec_dai->active = 0;
+ if (codec_dai->playback.active == 0 &&
+ codec_dai->capture.active == 0) {
+ cpu_dai->active = codec_dai->active = 0;
}
codec->active--;
- if (rtd->cpu_dai->ops.shutdown)
- rtd->cpu_dai->ops.shutdown(substream);
+ if (cpu_dai->ops.shutdown)
+ cpu_dai->ops.shutdown(substream);
- if (rtd->codec_dai->ops.shutdown)
- rtd->codec_dai->ops.shutdown(substream);
+ if (codec_dai->ops.shutdown)
+ codec_dai->ops.shutdown(substream);
if (machine->ops && machine->ops->shutdown)
machine->ops->shutdown(substream);
if (platform->pcm_ops->close)
platform->pcm_ops->close(substream);
- rtd->cpu_dai->runtime = NULL;
+ cpu_dai->runtime = NULL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* start delayed pop wq here for playback streams */
- rtd->codec_dai->pop_wait = 1;
- queue_delayed_work(soc_workq, &soc_stream_work,
+ codec_dai->pop_wait = 1;
+ schedule_delayed_work(&socdev->delayed_work,
msecs_to_jiffies(pmdown_time));
} else {
/* capture streams can be powered down now */
- snd_soc_dapm_stream_event(codec, rtd->codec_dai->capture.stream_name,
+ snd_soc_dapm_stream_event(codec,
+ codec_dai->capture.stream_name,
SND_SOC_DAPM_STREAM_STOP);
- if (codec->active == 0 && rtd->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_set_bias_level(socdev,
+ SND_SOC_BIAS_STANDBY);
}
mutex_unlock(&pcm_mutex);
{
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;
int ret = 0;
mutex_lock(&pcm_mutex);
+
+ if (machine->ops && machine->ops->prepare) {
+ ret = machine->ops->prepare(substream);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: machine prepare error\n");
+ goto out;
+ }
+ }
+
if (platform->pcm_ops->prepare) {
ret = platform->pcm_ops->prepare(substream);
- if (ret < 0)
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: platform prepare error\n");
goto out;
+ }
}
- if (rtd->codec_dai->ops.prepare) {
- ret = rtd->codec_dai->ops.prepare(substream);
- if (ret < 0)
+ if (codec_dai->ops.prepare) {
+ ret = codec_dai->ops.prepare(substream);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: codec DAI prepare error\n");
goto out;
+ }
}
- if (rtd->cpu_dai->ops.prepare)
- ret = rtd->cpu_dai->ops.prepare(substream);
+ if (cpu_dai->ops.prepare) {
+ ret = cpu_dai->ops.prepare(substream);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: cpu DAI prepare error\n");
+ goto out;
+ }
+ }
/* we only want to start a DAPM playback stream if we are not waiting
* on an existing one stopping */
- if (rtd->codec_dai->pop_wait) {
+ 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(codec,
- rtd->codec_dai->capture.stream_name,
+ snd_soc_dapm_stream_event(socdev->codec,
+ codec_dai->capture.stream_name,
SND_SOC_DAPM_STREAM_START);
else {
- rtd->codec_dai->pop_wait = 0;
- cancel_delayed_work(&soc_stream_work);
- if (rtd->codec_dai->digital_mute)
- rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 0);
+ 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);
}
} else {
/* no delayed work - do we need to power up codec */
- if (codec->dapm_state != SNDRV_CTL_POWER_D0) {
+ if (codec->bias_level != SND_SOC_BIAS_ON) {
- if (codec->dapm_event)
- codec->dapm_event(codec, SNDRV_CTL_POWER_D1);
+ snd_soc_dapm_set_bias_level(socdev,
+ SND_SOC_BIAS_PREPARE);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_dapm_stream_event(codec,
- rtd->codec_dai->playback.stream_name,
+ codec_dai->playback.stream_name,
SND_SOC_DAPM_STREAM_START);
else
snd_soc_dapm_stream_event(codec,
- rtd->codec_dai->capture.stream_name,
+ codec_dai->capture.stream_name,
SND_SOC_DAPM_STREAM_START);
- if (codec->dapm_event)
- codec->dapm_event(codec, SNDRV_CTL_POWER_D0);
- if (rtd->codec_dai->digital_mute)
- rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 0);
+ snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON);
+ if (codec_dai->dai_ops.digital_mute)
+ codec_dai->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,
- rtd->codec_dai->playback.stream_name,
+ codec_dai->playback.stream_name,
SND_SOC_DAPM_STREAM_START);
else
snd_soc_dapm_stream_event(codec,
- rtd->codec_dai->capture.stream_name,
+ codec_dai->capture.stream_name,
SND_SOC_DAPM_STREAM_START);
- if (rtd->codec_dai->digital_mute)
- rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 0);
+ if (codec_dai->dai_ops.digital_mute)
+ codec_dai->dai_ops.digital_mute(codec_dai, 0);
}
}
{
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_machine *machine = socdev->machine;
+ struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
int ret = 0;
mutex_lock(&pcm_mutex);
- /* we don't need to match any AC97 params */
- if (rtd->cpu_dai->type != SND_SOC_DAI_AC97) {
- ret = soc_hw_match_params(substream, params);
- if (ret < 0)
- goto out;
- } else {
- struct snd_soc_clock_info clk_info;
- clk_info.rate = params_rate(params);
- ret = soc_get_mclk(rtd, &clk_info);
- if (ret < 0)
+ if (machine->ops && machine->ops->hw_params) {
+ ret = machine->ops->hw_params(substream, params);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: machine hw_params failed\n");
goto out;
+ }
}
- if (rtd->codec_dai->ops.hw_params) {
- ret = rtd->codec_dai->ops.hw_params(substream, params);
+ if (codec_dai->ops.hw_params) {
+ ret = codec_dai->ops.hw_params(substream, params);
if (ret < 0) {
printk(KERN_ERR "asoc: can't set codec %s hw params\n",
- rtd->codec_dai->name);
- goto out;
+ codec_dai->name);
+ goto codec_err;
}
}
- if (rtd->cpu_dai->ops.hw_params) {
- ret = rtd->cpu_dai->ops.hw_params(substream, params);
+ if (cpu_dai->ops.hw_params) {
+ ret = cpu_dai->ops.hw_params(substream, params);
if (ret < 0) {
printk(KERN_ERR "asoc: can't set interface %s hw params\n",
- rtd->cpu_dai->name);
+ cpu_dai->name);
goto interface_err;
}
}
}
}
- if (machine->ops && machine->ops->hw_params) {
- ret = machine->ops->hw_params(substream, params);
- if (ret < 0) {
- printk(KERN_ERR "asoc: machine hw_params failed\n");
- goto machine_err;
- }
- }
-
out:
mutex_unlock(&pcm_mutex);
return ret;
-machine_err:
- if (platform->pcm_ops->hw_free)
- platform->pcm_ops->hw_free(substream);
-
platform_err:
- if (rtd->cpu_dai->ops.hw_free)
- rtd->cpu_dai->ops.hw_free(substream);
+ if (cpu_dai->ops.hw_free)
+ cpu_dai->ops.hw_free(substream);
interface_err:
- if (rtd->codec_dai->ops.hw_free)
- rtd->codec_dai->ops.hw_free(substream);
+ if (codec_dai->ops.hw_free)
+ codec_dai->ops.hw_free(substream);
+
+codec_err:
+ if(machine->ops && machine->ops->hw_free)
+ machine->ops->hw_free(substream);
mutex_unlock(&pcm_mutex);
return ret;
{
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_machine *machine = socdev->machine;
mutex_lock(&pcm_mutex);
/* apply codec digital mute */
- if (!codec->active && rtd->codec_dai->digital_mute)
- rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 1);
+ if (!codec->active && codec_dai->dai_ops.digital_mute)
+ codec_dai->dai_ops.digital_mute(codec_dai, 1);
/* free any machine hw params */
if (machine->ops && machine->ops->hw_free)
platform->pcm_ops->hw_free(substream);
/* now free hw params for the DAI's */
- if (rtd->codec_dai->ops.hw_free)
- rtd->codec_dai->ops.hw_free(substream);
+ if (codec_dai->ops.hw_free)
+ codec_dai->ops.hw_free(substream);
- if (rtd->cpu_dai->ops.hw_free)
- rtd->cpu_dai->ops.hw_free(substream);
+ if (cpu_dai->ops.hw_free)
+ cpu_dai->ops.hw_free(substream);
mutex_unlock(&pcm_mutex);
return 0;
{
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;
int ret;
- if (rtd->codec_dai->ops.trigger) {
- ret = rtd->codec_dai->ops.trigger(substream, cmd);
+ if (codec_dai->ops.trigger) {
+ ret = codec_dai->ops.trigger(substream, cmd);
if (ret < 0)
return ret;
}
return ret;
}
- if (rtd->cpu_dai->ops.trigger) {
- ret = rtd->cpu_dai->ops.trigger(substream, cmd);
+ if (cpu_dai->ops.trigger) {
+ ret = cpu_dai->ops.trigger(substream, cmd);
if (ret < 0)
return ret;
}
/* 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->digital_mute && dai->playback.active)
- dai->digital_mute(codec, dai, 1);
+ if (dai->dai_ops.digital_mute && dai->playback.active)
+ 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);
}
/* close any waiting streams and save state */
- flush_workqueue(soc_workq);
- codec->suspend_dapm_state = codec->dapm_state;
+ run_delayed_work(&socdev->delayed_work);
+ codec->suspend_bias_level = codec->bias_level;
for(i = 0; i < codec->num_dai; i++) {
char *stream = codec->dai[i].playback.stream_name;
/* 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->digital_mute && dai->playback.active)
- dai->digital_mute(codec, dai, 0);
+ if (dai->dai_ops.digital_mute && dai->playback.active)
+ dai->dai_ops.digital_mute(dai, 0);
}
for(i = 0; i < machine->num_links; i++) {
}
/* DAPM stream work */
- soc_workq = create_workqueue("kdapm");
- if (soc_workq == NULL)
- goto work_err;
- INIT_WORK(&soc_stream_work, close_delayed_work, socdev);
+ INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work);
return 0;
-work_err:
- if (platform->remove)
- platform->remove(pdev);
-
platform_err:
if (codec_dev->remove)
codec_dev->remove(pdev);
cpu_dai_err:
- for (i--; i > 0; i--) {
+ 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);
struct snd_soc_platform *platform = socdev->platform;
struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
- if (soc_workq)
- destroy_workqueue(soc_workq);
+ run_delayed_work(&socdev->delayed_work);
if (platform->remove)
platform->remove(pdev);
static struct platform_driver soc_driver = {
.driver = {
.name = "soc-audio",
+ .owner = THIS_MODULE,
},
.probe = soc_probe,
.remove = soc_remove,
rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL);
if (rtd == NULL)
return -ENOMEM;
- rtd->cpu_dai = cpu_dai;
- rtd->codec_dai = codec_dai;
+
+ 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,
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;
}
EXPORT_SYMBOL_GPL(snd_soc_test_bits);
-/**
- * snd_soc_get_rate - get int sample rate
- * @hwpcmrate: the hardware pcm rate
- *
- * Returns the audio rate integaer value, else 0.
- */
-int snd_soc_get_rate(int hwpcmrate)
-{
- int rate = ffs(hwpcmrate) - 1;
-
- if (rate > ARRAY_SIZE(rates))
- return 0;
- return rates[rate];
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_rate);
-
/**
* snd_soc_new_pcms - create new sound card and pcms
* @socdev: the SoC audio device
*
* 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_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_machine *machine = socdev->machine;
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_machine *machine = socdev->machine;
- int ret = 0, i, ac97 = 0;
+ 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)
- socdev->machine->dai_link[i].init(codec);
- if (socdev->machine->dai_link[i].cpu_dai->type == SND_SOC_DAI_AC97)
+ 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;
+ }
+ }
+ if (socdev->machine->dai_link[i].codec_dai->type ==
+ SND_SOC_DAI_AC97_BUS)
ac97 = 1;
}
snprintf(codec->card->shortname, sizeof(codec->card->shortname),
if (ret < 0) {
printk(KERN_ERR "asoc: failed to register soundcard for codec %s\n",
codec->name);
- mutex_unlock(&codec->mutex);
- return ret;
+ goto out;
}
+ mutex_lock(&codec->mutex);
#ifdef CONFIG_SND_SOC_AC97_BUS
- if (ac97)
- soc_ac97_dev_register(codec);
+ 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;
+ }
+ }
#endif
- snd_soc_dapm_sys_add(socdev->dev);
- device_create_file(socdev->dev, &dev_attr_codec_reg);
+ err = snd_soc_dapm_sys_add(socdev->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");
+
mutex_unlock(&codec->mutex);
+
+out:
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_register_card);
void snd_soc_free_pcms(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
+#ifdef CONFIG_SND_SOC_AC97_BUS
+ struct snd_soc_codec_dai *codec_dai;
+ int i;
+#endif
mutex_lock(&codec->mutex);
#ifdef CONFIG_SND_SOC_AC97_BUS
- if (codec->ac97)
- soc_ac97_dev_unregister(codec);
+ 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;
+ }
+ }
+free_card:
#endif
if (codec->card)
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);
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;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = mask;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext);
+ if (max == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-/**
- * snd_soc_info_bool_ext - external single boolean mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a single boolean external mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_bool_ext(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
+ uinfo->value.integer.max = max;
return 0;
}
-EXPORT_SYMBOL_GPL(snd_soc_info_bool_ext);
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext);
/**
* snd_soc_info_volsw - single mixer info callback
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);
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] =
(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;
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);
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);
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] =
(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;
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;
val2 = (ucontrol->value.integer.value[1] & mask);
if (invert) {
- val = mask - val;
- val2 = mask - val2;
+ val = max - val;
+ val2 = max - val2;
}
val = val << shift;
MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
MODULE_DESCRIPTION("ALSA SoC Core");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:soc-audio");