Commit 941725f5 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'asoc/topic/core' into asoc-next

parents 3ee3f454 c362effe
...@@ -268,7 +268,6 @@ struct snd_soc_dai { ...@@ -268,7 +268,6 @@ struct snd_soc_dai {
unsigned int sample_bits; unsigned int sample_bits;
/* parent platform/codec */ /* parent platform/codec */
struct snd_soc_platform *platform;
struct snd_soc_codec *codec; struct snd_soc_codec *codec;
struct snd_soc_component *component; struct snd_soc_component *component;
...@@ -276,8 +275,6 @@ struct snd_soc_dai { ...@@ -276,8 +275,6 @@ struct snd_soc_dai {
unsigned int tx_mask; unsigned int tx_mask;
unsigned int rx_mask; unsigned int rx_mask;
struct snd_soc_card *card;
struct list_head list; struct list_head list;
}; };
......
...@@ -435,7 +435,7 @@ void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card); ...@@ -435,7 +435,7 @@ void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card);
unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol); unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol);
/* Mostly internal - should not normally be used */ /* Mostly internal - should not normally be used */
void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm); void dapm_mark_endpoints_dirty(struct snd_soc_card *card);
/* dapm path query */ /* dapm path query */
int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
...@@ -508,9 +508,9 @@ struct snd_soc_dapm_path { ...@@ -508,9 +508,9 @@ struct snd_soc_dapm_path {
/* status */ /* status */
u32 connect:1; /* source and sink widgets are connected */ u32 connect:1; /* source and sink widgets are connected */
u32 walked:1; /* path has been walked */
u32 walking:1; /* path is in the process of being walked */ u32 walking:1; /* path is in the process of being walked */
u32 weak:1; /* path ignored for power management */ u32 weak:1; /* path ignored for power management */
u32 is_supply:1; /* At least one of the connected widgets is a supply */
int (*connected)(struct snd_soc_dapm_widget *source, int (*connected)(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink); struct snd_soc_dapm_widget *sink);
...@@ -544,11 +544,13 @@ struct snd_soc_dapm_widget { ...@@ -544,11 +544,13 @@ struct snd_soc_dapm_widget {
unsigned char active:1; /* active stream on DAC, ADC's */ unsigned char active:1; /* active stream on DAC, ADC's */
unsigned char connected:1; /* connected codec pin */ unsigned char connected:1; /* connected codec pin */
unsigned char new:1; /* cnew complete */ unsigned char new:1; /* cnew complete */
unsigned char ext:1; /* has external widgets */
unsigned char force:1; /* force state */ unsigned char force:1; /* force state */
unsigned char ignore_suspend:1; /* kept enabled over suspend */ unsigned char ignore_suspend:1; /* kept enabled over suspend */
unsigned char new_power:1; /* power from this run */ unsigned char new_power:1; /* power from this run */
unsigned char power_checked:1; /* power checked this run */ unsigned char power_checked:1; /* power checked this run */
unsigned char is_supply:1; /* Widget is a supply type widget */
unsigned char is_sink:1; /* Widget is a sink type widget */
unsigned char is_source:1; /* Widget is a source type widget */
int subseq; /* sort within widget type */ int subseq; /* sort within widget type */
int (*power_check)(struct snd_soc_dapm_widget *w); int (*power_check)(struct snd_soc_dapm_widget *w);
...@@ -567,6 +569,7 @@ struct snd_soc_dapm_widget { ...@@ -567,6 +569,7 @@ struct snd_soc_dapm_widget {
struct list_head sinks; struct list_head sinks;
/* used during DAPM updates */ /* used during DAPM updates */
struct list_head work_list;
struct list_head power_list; struct list_head power_list;
struct list_head dirty; struct list_head dirty;
int inputs; int inputs;
......
...@@ -36,6 +36,11 @@ ...@@ -36,6 +36,11 @@
{.reg = xreg, .rreg = xreg, .shift = shift_left, \ {.reg = xreg, .rreg = xreg, .shift = shift_left, \
.rshift = shift_right, .max = xmax, .platform_max = xmax, \ .rshift = shift_right, .max = xmax, .platform_max = xmax, \
.invert = xinvert, .autodisable = xautodisable}) .invert = xinvert, .autodisable = xautodisable})
#define SOC_DOUBLE_S_VALUE(xreg, shift_left, shift_right, xmin, xmax, xsign_bit, xinvert, xautodisable) \
((unsigned long)&(struct soc_mixer_control) \
{.reg = xreg, .rreg = xreg, .shift = shift_left, \
.rshift = shift_right, .min = xmin, .max = xmax, .platform_max = xmax, \
.sign_bit = xsign_bit, .invert = xinvert, .autodisable = xautodisable})
#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, xautodisable) \ #define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, xautodisable) \
SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert, xautodisable) SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert, xautodisable)
#define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \ #define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \
...@@ -171,11 +176,9 @@ ...@@ -171,11 +176,9 @@
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
SNDRV_CTL_ELEM_ACCESS_READWRITE, \ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
.tlv.p = (tlv_array), \ .tlv.p = (tlv_array), \
.info = snd_soc_info_volsw_s8, .get = snd_soc_get_volsw_s8, \ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
.put = snd_soc_put_volsw_s8, \ .put = snd_soc_put_volsw, \
.private_value = (unsigned long)&(struct soc_mixer_control) \ .private_value = SOC_DOUBLE_S_VALUE(xreg, 0, 8, xmin, xmax, 7, 0, 0) }
{.reg = xreg, .min = xmin, .max = xmax, \
.platform_max = xmax} }
#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xitems, xtexts) \ #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xitems, xtexts) \
{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ { .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
.items = xitems, .texts = xtexts, \ .items = xitems, .texts = xtexts, \
...@@ -541,12 +544,6 @@ int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol, ...@@ -541,12 +544,6 @@ int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol); struct snd_ctl_elem_value *ucontrol);
int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol); struct snd_ctl_elem_value *ucontrol);
int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol, int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo); struct snd_ctl_elem_info *uinfo);
int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
...@@ -854,8 +851,6 @@ struct snd_soc_platform_driver { ...@@ -854,8 +851,6 @@ struct snd_soc_platform_driver {
int (*probe)(struct snd_soc_platform *); int (*probe)(struct snd_soc_platform *);
int (*remove)(struct snd_soc_platform *); int (*remove)(struct snd_soc_platform *);
int (*suspend)(struct snd_soc_dai *dai);
int (*resume)(struct snd_soc_dai *dai);
struct snd_soc_component_driver component_driver; struct snd_soc_component_driver component_driver;
/* pcm creation and destruction */ /* pcm creation and destruction */
...@@ -880,7 +875,7 @@ struct snd_soc_platform_driver { ...@@ -880,7 +875,7 @@ struct snd_soc_platform_driver {
struct snd_soc_dai_link_component { struct snd_soc_dai_link_component {
const char *name; const char *name;
const struct device_node *of_node; struct device_node *of_node;
const char *dai_name; const char *dai_name;
}; };
...@@ -888,8 +883,6 @@ struct snd_soc_platform { ...@@ -888,8 +883,6 @@ struct snd_soc_platform {
struct device *dev; struct device *dev;
const struct snd_soc_platform_driver *driver; const struct snd_soc_platform_driver *driver;
unsigned int suspended:1; /* platform is suspended */
struct list_head list; struct list_head list;
struct snd_soc_component component; struct snd_soc_component component;
...@@ -984,7 +977,7 @@ struct snd_soc_codec_conf { ...@@ -984,7 +977,7 @@ struct snd_soc_codec_conf {
* DT/OF node, but not both. * DT/OF node, but not both.
*/ */
const char *dev_name; const char *dev_name;
const struct device_node *of_node; struct device_node *of_node;
/* /*
* optional map of kcontrol, widget and path name prefixes that are * optional map of kcontrol, widget and path name prefixes that are
...@@ -1001,7 +994,7 @@ struct snd_soc_aux_dev { ...@@ -1001,7 +994,7 @@ struct snd_soc_aux_dev {
* DT/OF node, but not both. * DT/OF node, but not both.
*/ */
const char *codec_name; const char *codec_name;
const struct device_node *codec_of_node; struct device_node *codec_of_node;
/* codec/machine specific init - e.g. add machine controls */ /* codec/machine specific init - e.g. add machine controls */
int (*init)(struct snd_soc_component *component); int (*init)(struct snd_soc_component *component);
......
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o
ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),) ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
snd-soc-core-objs += soc-generic-dmaengine-pcm.o snd-soc-core-objs += soc-generic-dmaengine-pcm.o
......
...@@ -589,17 +589,12 @@ int snd_soc_suspend(struct device *dev) ...@@ -589,17 +589,12 @@ int snd_soc_suspend(struct device *dev)
for (i = 0; i < card->num_rtd; i++) { for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
struct snd_soc_platform *platform = card->rtd[i].platform;
if (card->rtd[i].dai_link->ignore_suspend) if (card->rtd[i].dai_link->ignore_suspend)
continue; continue;
if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control) if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control)
cpu_dai->driver->suspend(cpu_dai); cpu_dai->driver->suspend(cpu_dai);
if (platform->driver->suspend && !platform->suspended) {
platform->driver->suspend(cpu_dai);
platform->suspended = 1;
}
} }
/* close any waiting streams and save state */ /* close any waiting streams and save state */
...@@ -626,8 +621,8 @@ int snd_soc_suspend(struct device *dev) ...@@ -626,8 +621,8 @@ int snd_soc_suspend(struct device *dev)
SND_SOC_DAPM_STREAM_SUSPEND); SND_SOC_DAPM_STREAM_SUSPEND);
} }
/* Recheck all analogue paths too */ /* Recheck all endpoints too, their state is affected by suspend */
dapm_mark_io_dirty(&card->dapm); dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm); snd_soc_dapm_sync(&card->dapm);
/* suspend all CODECs */ /* suspend all CODECs */
...@@ -771,17 +766,12 @@ static void soc_resume_deferred(struct work_struct *work) ...@@ -771,17 +766,12 @@ static void soc_resume_deferred(struct work_struct *work)
for (i = 0; i < card->num_rtd; i++) { for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
struct snd_soc_platform *platform = card->rtd[i].platform;
if (card->rtd[i].dai_link->ignore_suspend) if (card->rtd[i].dai_link->ignore_suspend)
continue; continue;
if (cpu_dai->driver->resume && !cpu_dai->driver->ac97_control) if (cpu_dai->driver->resume && !cpu_dai->driver->ac97_control)
cpu_dai->driver->resume(cpu_dai); cpu_dai->driver->resume(cpu_dai);
if (platform->driver->resume && platform->suspended) {
platform->driver->resume(cpu_dai);
platform->suspended = 0;
}
} }
if (card->resume_post) if (card->resume_post)
...@@ -792,8 +782,8 @@ static void soc_resume_deferred(struct work_struct *work) ...@@ -792,8 +782,8 @@ static void soc_resume_deferred(struct work_struct *work)
/* userspace can access us now we are back as we were before */ /* userspace can access us now we are back as we were before */
snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0); snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
/* Recheck all analogue paths too */ /* Recheck all endpoints too, their state is affected by suspend */
dapm_mark_io_dirty(&card->dapm); dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm); snd_soc_dapm_sync(&card->dapm);
} }
...@@ -1247,25 +1237,22 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num, ...@@ -1247,25 +1237,22 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num,
return 0; return 0;
} }
static int soc_probe_codec_dai(struct snd_soc_card *card, static int soc_probe_dai(struct snd_soc_dai *dai, int order)
struct snd_soc_dai *codec_dai,
int order)
{ {
int ret; int ret;
if (!codec_dai->probed && codec_dai->driver->probe_order == order) { if (!dai->probed && dai->driver->probe_order == order) {
if (codec_dai->driver->probe) { if (dai->driver->probe) {
ret = codec_dai->driver->probe(codec_dai); ret = dai->driver->probe(dai);
if (ret < 0) { if (ret < 0) {
dev_err(codec_dai->dev, dev_err(dai->dev,
"ASoC: failed to probe CODEC DAI %s: %d\n", "ASoC: failed to probe DAI %s: %d\n",
codec_dai->name, ret); dai->name, ret);
return ret; return ret;
} }
} }
/* mark codec_dai as probed and add to card dai list */ dai->probed = 1;
codec_dai->probed = 1;
} }
return 0; return 0;
...@@ -1315,40 +1302,22 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) ...@@ -1315,40 +1302,22 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
{ {
struct snd_soc_dai_link *dai_link = &card->dai_link[num]; struct snd_soc_dai_link *dai_link = &card->dai_link[num];
struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int i, ret; int i, ret;
dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n", dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n",
card->name, num, order); card->name, num, order);
/* config components */
cpu_dai->platform = platform;
cpu_dai->card = card;
for (i = 0; i < rtd->num_codecs; i++)
rtd->codec_dais[i]->card = card;
/* set default power off timeout */ /* set default power off timeout */
rtd->pmdown_time = pmdown_time; rtd->pmdown_time = pmdown_time;
/* probe the cpu_dai */ ret = soc_probe_dai(cpu_dai, order);
if (!cpu_dai->probed && if (ret)
cpu_dai->driver->probe_order == order) { return ret;
if (cpu_dai->driver->probe) {
ret = cpu_dai->driver->probe(cpu_dai);
if (ret < 0) {
dev_err(cpu_dai->dev,
"ASoC: failed to probe CPU DAI %s: %d\n",
cpu_dai->name, ret);
return ret;
}
}
cpu_dai->probed = 1;
}
/* probe the CODEC DAI */ /* probe the CODEC DAI */
for (i = 0; i < rtd->num_codecs; i++) { for (i = 0; i < rtd->num_codecs; i++) {
ret = soc_probe_codec_dai(card, rtd->codec_dais[i], order); ret = soc_probe_dai(rtd->codec_dais[i], order);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -2322,1027 +2291,13 @@ EXPORT_SYMBOL_GPL(snd_soc_add_card_controls); ...@@ -2322,1027 +2291,13 @@ EXPORT_SYMBOL_GPL(snd_soc_add_card_controls);
int snd_soc_add_dai_controls(struct snd_soc_dai *dai, int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
const struct snd_kcontrol_new *controls, int num_controls) const struct snd_kcontrol_new *controls, int num_controls)
{ {
struct snd_card *card = dai->card->snd_card; struct snd_card *card = dai->component->card->snd_card;
return snd_soc_add_controls(card, dai->dev, controls, num_controls, return snd_soc_add_controls(card, dai->dev, controls, num_controls,
NULL, dai); NULL, dai);
} }
EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls); EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls);
/**
* snd_soc_info_enum_double - enumerated double mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information about a double enumerated
* mixer control.
*
* Returns 0 for success.
*/
int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
uinfo->value.enumerated.items = e->items;
if (uinfo->value.enumerated.item >= e->items)
uinfo->value.enumerated.item = e->items - 1;
strlcpy(uinfo->value.enumerated.name,
e->texts[uinfo->value.enumerated.item],
sizeof(uinfo->value.enumerated.name));
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
/**
* snd_soc_get_enum_double - enumerated double mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a double enumerated mixer.
*
* Returns 0 for success.
*/
int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, item;
unsigned int reg_val;
int ret;
ret = snd_soc_component_read(component, e->reg, &reg_val);
if (ret)
return ret;
val = (reg_val >> e->shift_l) & e->mask;
item = snd_soc_enum_val_to_item(e, val);
ucontrol->value.enumerated.item[0] = item;
if (e->shift_l != e->shift_r) {
val = (reg_val >> e->shift_l) & e->mask;
item = snd_soc_enum_val_to_item(e, val);
ucontrol->value.enumerated.item[1] = item;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
/**
* snd_soc_put_enum_double - enumerated double mixer put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to set the value of a double enumerated mixer.
*
* Returns 0 for success.
*/
int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int *item = ucontrol->value.enumerated.item;
unsigned int val;
unsigned int mask;
if (item[0] >= e->items)
return -EINVAL;
val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
mask = e->mask << e->shift_l;
if (e->shift_l != e->shift_r) {
if (item[1] >= e->items)
return -EINVAL;
val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
mask |= e->mask << e->shift_r;
}
return snd_soc_component_update_bits(component, e->reg, mask, val);
}
EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
/**
* snd_soc_read_signed - Read a codec register and interprete as signed value
* @component: component
* @reg: Register to read
* @mask: Mask to use after shifting the register value
* @shift: Right shift of register value
* @sign_bit: Bit that describes if a number is negative or not.
* @signed_val: Pointer to where the read value should be stored
*
* This functions reads a codec register. The register value is shifted right
* by 'shift' bits and masked with the given 'mask'. Afterwards it translates
* the given registervalue into a signed integer if sign_bit is non-zero.
*
* Returns 0 on sucess, otherwise an error value
*/
static int snd_soc_read_signed(struct snd_soc_component *component,
unsigned int reg, unsigned int mask, unsigned int shift,
unsigned int sign_bit, int *signed_val)
{
int ret;
unsigned int val;
ret = snd_soc_component_read(component, reg, &val);
if (ret < 0)
return ret;
val = (val >> shift) & mask;
if (!sign_bit) {
*signed_val = val;
return 0;
}
/* non-negative number */
if (!(val & BIT(sign_bit))) {
*signed_val = val;
return 0;
}
ret = val;
/*
* The register most probably does not contain a full-sized int.
* Instead we have an arbitrary number of bits in a signed
* representation which has to be translated into a full-sized int.
* This is done by filling up all bits above the sign-bit.
*/
ret |= ~((int)(BIT(sign_bit) - 1));
*signed_val = ret;
return 0;
}
/**
* snd_soc_info_volsw - single mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information about a single mixer control, or a double
* mixer control that spans 2 registers.
*
* Returns 0 for success.
*/
int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
int platform_max;
if (!mc->platform_max)
mc->platform_max = mc->max;
platform_max = mc->platform_max;
if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
else
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = platform_max - mc->min;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
/**
* snd_soc_get_volsw - single mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a single mixer control, or a double mixer
* control that spans 2 registers.
*
* Returns 0 for success.
*/
int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
int max = mc->max;
int min = mc->min;
int sign_bit = mc->sign_bit;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
int val;
int ret;
if (sign_bit)
mask = BIT(sign_bit + 1) - 1;
ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val);
if (ret)
return ret;
ucontrol->value.integer.value[0] = val - min;
if (invert)
ucontrol->value.integer.value[0] =
max - ucontrol->value.integer.value[0];
if (snd_soc_volsw_is_stereo(mc)) {
if (reg == reg2)
ret = snd_soc_read_signed(component, reg, mask, rshift,
sign_bit, &val);
else
ret = snd_soc_read_signed(component, reg2, mask, shift,
sign_bit, &val);
if (ret)
return ret;
ucontrol->value.integer.value[1] = val - min;
if (invert)
ucontrol->value.integer.value[1] =
max - ucontrol->value.integer.value[1];
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
/**
* snd_soc_put_volsw - single mixer put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to set the value of a single mixer control, or a double mixer
* control that spans 2 registers.
*
* Returns 0 for success.
*/
int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
int max = mc->max;
int min = mc->min;
unsigned int sign_bit = mc->sign_bit;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
int err;
bool type_2r = false;
unsigned int val2 = 0;
unsigned int val, val_mask;
if (sign_bit)
mask = BIT(sign_bit + 1) - 1;
val = ((ucontrol->value.integer.value[0] + min) & mask);
if (invert)
val = max - val;
val_mask = mask << shift;
val = val << shift;
if (snd_soc_volsw_is_stereo(mc)) {
val2 = ((ucontrol->value.integer.value[1] + min) & mask);
if (invert)
val2 = max - val2;
if (reg == reg2) {
val_mask |= mask << rshift;
val |= val2 << rshift;
} else {
val2 = val2 << shift;
type_2r = true;
}
}
err = snd_soc_component_update_bits(component, reg, val_mask, val);
if (err < 0)
return err;
if (type_2r)
err = snd_soc_component_update_bits(component, reg2, val_mask,
val2);
return err;
}
EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
/**
* snd_soc_get_volsw_sx - single mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a single mixer control, or a double mixer
* control that spans 2 registers.
*
* Returns 0 for success.
*/
int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
int max = mc->max;
int min = mc->min;
int mask = (1 << (fls(min + max) - 1)) - 1;
unsigned int val;
int ret;
ret = snd_soc_component_read(component, reg, &val);
if (ret < 0)
return ret;
ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask;
if (snd_soc_volsw_is_stereo(mc)) {
ret = snd_soc_component_read(component, reg2, &val);
if (ret < 0)
return ret;
val = ((val >> rshift) - min) & mask;
ucontrol->value.integer.value[1] = val;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
/**
* snd_soc_put_volsw_sx - double mixer set callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to set the value of a double mixer control that spans 2 registers.
*
* Returns 0 for success.
*/
int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
int max = mc->max;
int min = mc->min;
int mask = (1 << (fls(min + max) - 1)) - 1;
int err = 0;
unsigned int val, val_mask, val2 = 0;
val_mask = mask << shift;
val = (ucontrol->value.integer.value[0] + min) & mask;
val = val << shift;
err = snd_soc_component_update_bits(component, reg, val_mask, val);
if (err < 0)
return err;
if (snd_soc_volsw_is_stereo(mc)) {
val_mask = mask << rshift;
val2 = (ucontrol->value.integer.value[1] + min) & mask;
val2 = val2 << rshift;
err = snd_soc_component_update_bits(component, reg2, val_mask,
val2);
}
return err;
}
EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
/**
* snd_soc_info_volsw_s8 - signed mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information about a signed mixer control.
*
* Returns 0 for success.
*/
int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
int platform_max;
int min = mc->min;
if (!mc->platform_max)
mc->platform_max = mc->max;
platform_max = mc->platform_max;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = platform_max - min;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8);
/**
* snd_soc_get_volsw_s8 - signed mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a signed mixer control.
*
* Returns 0 for success.
*/
int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg;
unsigned int val;
int min = mc->min;
int ret;
ret = snd_soc_component_read(component, reg, &val);
if (ret)
return ret;
ucontrol->value.integer.value[0] =
((signed char)(val & 0xff))-min;
ucontrol->value.integer.value[1] =
((signed char)((val >> 8) & 0xff))-min;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8);
/**
* snd_soc_put_volsw_sgn - signed mixer put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to set the value of a signed mixer control.
*
* Returns 0 for success.
*/
int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg;
int min = mc->min;
unsigned int val;
val = (ucontrol->value.integer.value[0]+min) & 0xff;
val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
return snd_soc_component_update_bits(component, reg, 0xffff, val);
}
EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
/**
* snd_soc_info_volsw_range - single mixer info callback with range.
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information, within a range, about a single
* mixer control.
*
* returns 0 for success.
*/
int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
int platform_max;
int min = mc->min;
if (!mc->platform_max)
mc->platform_max = mc->max;
platform_max = mc->platform_max;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = platform_max - min;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range);
/**
* snd_soc_put_volsw_range - single mixer put value callback with range.
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to set the value, within a range, for a single mixer control.
*
* Returns 0 for success.
*/
int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg;
unsigned int rreg = mc->rreg;
unsigned int shift = mc->shift;
int min = mc->min;
int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
unsigned int val, val_mask;
int ret;
if (invert)
val = (max - ucontrol->value.integer.value[0]) & mask;
else
val = ((ucontrol->value.integer.value[0] + min) & mask);
val_mask = mask << shift;
val = val << shift;
ret = snd_soc_component_update_bits(component, reg, val_mask, val);
if (ret < 0)
return ret;
if (snd_soc_volsw_is_stereo(mc)) {
if (invert)
val = (max - ucontrol->value.integer.value[1]) & mask;
else
val = ((ucontrol->value.integer.value[1] + min) & mask);
val_mask = mask << shift;
val = val << shift;
ret = snd_soc_component_update_bits(component, rreg, val_mask,
val);
}
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
/**
* snd_soc_get_volsw_range - single mixer get callback with range
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value, within a range, of a single mixer control.
*
* Returns 0 for success.
*/
int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int rreg = mc->rreg;
unsigned int shift = mc->shift;
int min = mc->min;
int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
unsigned int val;
int ret;
ret = snd_soc_component_read(component, reg, &val);
if (ret)
return ret;
ucontrol->value.integer.value[0] = (val >> shift) & mask;
if (invert)
ucontrol->value.integer.value[0] =
max - ucontrol->value.integer.value[0];
else
ucontrol->value.integer.value[0] =
ucontrol->value.integer.value[0] - min;
if (snd_soc_volsw_is_stereo(mc)) {
ret = snd_soc_component_read(component, rreg, &val);
if (ret)
return ret;
ucontrol->value.integer.value[1] = (val >> shift) & mask;
if (invert)
ucontrol->value.integer.value[1] =
max - ucontrol->value.integer.value[1];
else
ucontrol->value.integer.value[1] =
ucontrol->value.integer.value[1] - min;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
/**
* snd_soc_limit_volume - Set new limit to an existing volume control.
*
* @codec: where to look for the control
* @name: Name of the control
* @max: new maximum limit
*
* Return 0 for success, else error.
*/
int snd_soc_limit_volume(struct snd_soc_codec *codec,
const char *name, int max)
{
struct snd_card *card = codec->component.card->snd_card;
struct snd_kcontrol *kctl;
struct soc_mixer_control *mc;
int found = 0;
int ret = -EINVAL;
/* Sanity check for name and max */
if (unlikely(!name || max <= 0))
return -EINVAL;
list_for_each_entry(kctl, &card->controls, list) {
if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
found = 1;
break;
}
}
if (found) {
mc = (struct soc_mixer_control *)kctl->private_value;
if (max <= mc->max) {
mc->platform_max = max;
ret = 0;
}
}
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_bytes *params = (void *)kcontrol->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = params->num_regs * component->val_bytes;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_bytes_info);
int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_bytes *params = (void *)kcontrol->private_value;
int ret;
if (component->regmap)
ret = regmap_raw_read(component->regmap, params->base,
ucontrol->value.bytes.data,
params->num_regs * component->val_bytes);
else
ret = -EINVAL;
/* Hide any masked bytes to ensure consistent data reporting */
if (ret == 0 && params->mask) {
switch (component->val_bytes) {
case 1:
ucontrol->value.bytes.data[0] &= ~params->mask;
break;
case 2:
((u16 *)(&ucontrol->value.bytes.data))[0]
&= cpu_to_be16(~params->mask);
break;
case 4:
((u32 *)(&ucontrol->value.bytes.data))[0]
&= cpu_to_be32(~params->mask);
break;
default:
return -EINVAL;
}
}
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_bytes_get);
int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_bytes *params = (void *)kcontrol->private_value;
int ret, len;
unsigned int val, mask;
void *data;
if (!component->regmap || !params->num_regs)
return -EINVAL;
len = params->num_regs * component->val_bytes;
data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
if (!data)
return -ENOMEM;
/*
* If we've got a mask then we need to preserve the register
* bits. We shouldn't modify the incoming data so take a
* copy.
*/
if (params->mask) {
ret = regmap_read(component->regmap, params->base, &val);
if (ret != 0)
goto out;
val &= params->mask;
switch (component->val_bytes) {
case 1:
((u8 *)data)[0] &= ~params->mask;
((u8 *)data)[0] |= val;
break;
case 2:
mask = ~params->mask;
ret = regmap_parse_val(component->regmap,
&mask, &mask);
if (ret != 0)
goto out;
((u16 *)data)[0] &= mask;
ret = regmap_parse_val(component->regmap,
&val, &val);
if (ret != 0)
goto out;
((u16 *)data)[0] |= val;
break;
case 4:
mask = ~params->mask;
ret = regmap_parse_val(component->regmap,
&mask, &mask);
if (ret != 0)
goto out;
((u32 *)data)[0] &= mask;
ret = regmap_parse_val(component->regmap,
&val, &val);
if (ret != 0)
goto out;
((u32 *)data)[0] |= val;
break;
default:
ret = -EINVAL;
goto out;
}
}
ret = regmap_raw_write(component->regmap, params->base,
data, len);
out:
kfree(data);
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *ucontrol)
{
struct soc_bytes_ext *params = (void *)kcontrol->private_value;
ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
ucontrol->count = params->max;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext);
int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *tlv)
{
struct soc_bytes_ext *params = (void *)kcontrol->private_value;
unsigned int count = size < params->max ? size : params->max;
int ret = -ENXIO;
switch (op_flag) {
case SNDRV_CTL_TLV_OP_READ:
if (params->get)
ret = params->get(tlv, count);
break;
case SNDRV_CTL_TLV_OP_WRITE:
if (params->put)
ret = params->put(tlv, count);
break;
}
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback);
/**
* snd_soc_info_xr_sx - signed multi register info callback
* @kcontrol: mreg control
* @uinfo: control element information
*
* Callback to provide information of a control that can
* span multiple codec registers which together
* forms a single signed value in a MSB/LSB manner.
*
* Returns 0 for success.
*/
int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct soc_mreg_control *mc =
(struct soc_mreg_control *)kcontrol->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = mc->min;
uinfo->value.integer.max = mc->max;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
/**
* snd_soc_get_xr_sx - signed multi register get callback
* @kcontrol: mreg control
* @ucontrol: control element information
*
* Callback to get the value of a control that can span
* multiple codec registers which together forms a single
* signed value in a MSB/LSB manner. The control supports
* specifying total no of bits used to allow for bitfields
* across the multiple codec registers.
*
* Returns 0 for success.
*/
int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mreg_control *mc =
(struct soc_mreg_control *)kcontrol->private_value;
unsigned int regbase = mc->regbase;
unsigned int regcount = mc->regcount;
unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
unsigned int regwmask = (1<<regwshift)-1;
unsigned int invert = mc->invert;
unsigned long mask = (1UL<<mc->nbits)-1;
long min = mc->min;
long max = mc->max;
long val = 0;
unsigned int regval;
unsigned int i;
int ret;
for (i = 0; i < regcount; i++) {
ret = snd_soc_component_read(component, regbase+i, &regval);
if (ret)
return ret;
val |= (regval & regwmask) << (regwshift*(regcount-i-1));
}
val &= mask;
if (min < 0 && val > max)
val |= ~mask;
if (invert)
val = max - val;
ucontrol->value.integer.value[0] = val;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
/**
* snd_soc_put_xr_sx - signed multi register get callback
* @kcontrol: mreg control
* @ucontrol: control element information
*
* Callback to set the value of a control that can span
* multiple codec registers which together forms a single
* signed value in a MSB/LSB manner. The control supports
* specifying total no of bits used to allow for bitfields
* across the multiple codec registers.
*
* Returns 0 for success.
*/
int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mreg_control *mc =
(struct soc_mreg_control *)kcontrol->private_value;
unsigned int regbase = mc->regbase;
unsigned int regcount = mc->regcount;
unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
unsigned int regwmask = (1<<regwshift)-1;
unsigned int invert = mc->invert;
unsigned long mask = (1UL<<mc->nbits)-1;
long max = mc->max;
long val = ucontrol->value.integer.value[0];
unsigned int i, regval, regmask;
int err;
if (invert)
val = max - val;
val &= mask;
for (i = 0; i < regcount; i++) {
regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
err = snd_soc_component_update_bits(component, regbase+i,
regmask, regval);
if (err < 0)
return err;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
/**
* snd_soc_get_strobe - strobe get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback get the value of a strobe mixer control.
*
* Returns 0 for success.
*/
int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int mask = 1 << shift;
unsigned int invert = mc->invert != 0;
unsigned int val;
int ret;
ret = snd_soc_component_read(component, reg, &val);
if (ret)
return ret;
val &= mask;
if (shift != 0 && val != 0)
val = val >> shift;
ucontrol->value.enumerated.item[0] = val ^ invert;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
/**
* snd_soc_put_strobe - strobe put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback strobe a register bit to high then low (or the inverse)
* in one pass of a single mixer enum control.
*
* Returns 1 for success.
*/
int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int mask = 1 << shift;
unsigned int invert = mc->invert != 0;
unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
unsigned int val1 = (strobe ^ invert) ? mask : 0;
unsigned int val2 = (strobe ^ invert) ? 0 : mask;
int err;
err = snd_soc_component_update_bits(component, reg, mask, val1);
if (err < 0)
return err;
return snd_soc_component_update_bits(component, reg, mask, val2);
}
EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
/** /**
* snd_soc_dai_set_sysclk - configure DAI system or master clock. * snd_soc_dai_set_sysclk - configure DAI system or master clock.
* @dai: DAI * @dai: DAI
......
...@@ -159,27 +159,135 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) ...@@ -159,27 +159,135 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
} }
} }
void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm) /*
* dapm_widget_invalidate_input_paths() - Invalidate the cached number of input
* paths
* @w: The widget for which to invalidate the cached number of input paths
*
* The function resets the cached number of inputs for the specified widget and
* all widgets that can be reached via outgoing paths from the widget.
*
* This function must be called if the number of input paths for a widget might
* have changed. E.g. if the source state of a widget changes or a path is added
* or activated with the widget as the sink.
*/
static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w)
{
struct snd_soc_dapm_widget *sink;
struct snd_soc_dapm_path *p;
LIST_HEAD(list);
dapm_assert_locked(w->dapm);
if (w->inputs == -1)
return;
w->inputs = -1;
list_add_tail(&w->work_list, &list);
list_for_each_entry(w, &list, work_list) {
list_for_each_entry(p, &w->sinks, list_source) {
if (p->is_supply || p->weak || !p->connect)
continue;
sink = p->sink;
if (sink->inputs != -1) {
sink->inputs = -1;
list_add_tail(&sink->work_list, &list);
}
}
}
}
/*
* dapm_widget_invalidate_output_paths() - Invalidate the cached number of
* output paths
* @w: The widget for which to invalidate the cached number of output paths
*
* Resets the cached number of outputs for the specified widget and all widgets
* that can be reached via incoming paths from the widget.
*
* This function must be called if the number of output paths for a widget might
* have changed. E.g. if the sink state of a widget changes or a path is added
* or activated with the widget as the source.
*/
static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w)
{
struct snd_soc_dapm_widget *source;
struct snd_soc_dapm_path *p;
LIST_HEAD(list);
dapm_assert_locked(w->dapm);
if (w->outputs == -1)
return;
w->outputs = -1;
list_add_tail(&w->work_list, &list);
list_for_each_entry(w, &list, work_list) {
list_for_each_entry(p, &w->sources, list_sink) {
if (p->is_supply || p->weak || !p->connect)
continue;
source = p->source;
if (source->outputs != -1) {
source->outputs = -1;
list_add_tail(&source->work_list, &list);
}
}
}
}
/*
* dapm_path_invalidate() - Invalidates the cached number of inputs and outputs
* for the widgets connected to a path
* @p: The path to invalidate
*
* Resets the cached number of inputs for the sink of the path and the cached
* number of outputs for the source of the path.
*
* This function must be called when a path is added, removed or the connected
* state changes.
*/
static void dapm_path_invalidate(struct snd_soc_dapm_path *p)
{
/*
* Weak paths or supply paths do not influence the number of input or
* output paths of their neighbors.
*/
if (p->weak || p->is_supply)
return;
/*
* The number of connected endpoints is the sum of the number of
* connected endpoints of all neighbors. If a node with 0 connected
* endpoints is either connected or disconnected that sum won't change,
* so there is no need to re-check the path.
*/
if (p->source->inputs != 0)
dapm_widget_invalidate_input_paths(p->sink);
if (p->sink->outputs != 0)
dapm_widget_invalidate_output_paths(p->source);
}
void dapm_mark_endpoints_dirty(struct snd_soc_card *card)
{ {
struct snd_soc_card *card = dapm->card;
struct snd_soc_dapm_widget *w; struct snd_soc_dapm_widget *w;
mutex_lock(&card->dapm_mutex); mutex_lock(&card->dapm_mutex);
list_for_each_entry(w, &card->widgets, list) { list_for_each_entry(w, &card->widgets, list) {
switch (w->id) { if (w->is_sink || w->is_source) {
case snd_soc_dapm_input: dapm_mark_dirty(w, "Rechecking endpoints");
case snd_soc_dapm_output: if (w->is_sink)
dapm_mark_dirty(w, "Rechecking inputs and outputs"); dapm_widget_invalidate_output_paths(w);
break; if (w->is_source)
default: dapm_widget_invalidate_input_paths(w);
break;
} }
} }
mutex_unlock(&card->dapm_mutex); mutex_unlock(&card->dapm_mutex);
} }
EXPORT_SYMBOL_GPL(dapm_mark_io_dirty); EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty);
/* create a new dapm widget */ /* create a new dapm widget */
static inline struct snd_soc_dapm_widget *dapm_cnew_widget( static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
...@@ -386,8 +494,6 @@ static void dapm_reset(struct snd_soc_card *card) ...@@ -386,8 +494,6 @@ static void dapm_reset(struct snd_soc_card *card)
list_for_each_entry(w, &card->widgets, list) { list_for_each_entry(w, &card->widgets, list) {
w->new_power = w->power; w->new_power = w->power;
w->power_checked = false; w->power_checked = false;
w->inputs = -1;
w->outputs = -1;
} }
} }
...@@ -469,10 +575,9 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, ...@@ -469,10 +575,9 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
/* connect mux widget to its interconnecting audio paths */ /* connect mux widget to its interconnecting audio paths */
static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, struct snd_soc_dapm_path *path, const char *control_name)
struct snd_soc_dapm_path *path, const char *control_name,
const struct snd_kcontrol_new *kcontrol)
{ {
const struct snd_kcontrol_new *kcontrol = &path->sink->kcontrol_news[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, item; unsigned int val, item;
int i; int i;
...@@ -493,10 +598,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, ...@@ -493,10 +598,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
for (i = 0; i < e->items; i++) { for (i = 0; i < e->items; i++) {
if (!(strcmp(control_name, e->texts[i]))) { if (!(strcmp(control_name, e->texts[i]))) {
list_add(&path->list, &dapm->card->paths); path->name = e->texts[i];
list_add(&path->list_sink, &dest->sources);
list_add(&path->list_source, &src->sinks);
path->name = (char*)e->texts[i];
if (i == item) if (i == item)
path->connect = 1; path->connect = 1;
else else
...@@ -509,11 +611,10 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, ...@@ -509,11 +611,10 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
} }
/* set up initial codec paths */ /* set up initial codec paths */
static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w, static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
struct snd_soc_dapm_path *p, int i)
{ {
struct soc_mixer_control *mc = (struct soc_mixer_control *) struct soc_mixer_control *mc = (struct soc_mixer_control *)
w->kcontrol_news[i].private_value; p->sink->kcontrol_news[i].private_value;
unsigned int reg = mc->reg; unsigned int reg = mc->reg;
unsigned int shift = mc->shift; unsigned int shift = mc->shift;
unsigned int max = mc->max; unsigned int max = mc->max;
...@@ -522,7 +623,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w, ...@@ -522,7 +623,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
unsigned int val; unsigned int val;
if (reg != SND_SOC_NOPM) { if (reg != SND_SOC_NOPM) {
soc_dapm_read(w->dapm, reg, &val); soc_dapm_read(p->sink->dapm, reg, &val);
val = (val >> shift) & mask; val = (val >> shift) & mask;
if (invert) if (invert)
val = max - val; val = max - val;
...@@ -534,19 +635,15 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w, ...@@ -534,19 +635,15 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
/* connect mixer widget to its interconnecting audio paths */ /* connect mixer widget to its interconnecting audio paths */
static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
struct snd_soc_dapm_path *path, const char *control_name) struct snd_soc_dapm_path *path, const char *control_name)
{ {
int i; int i;
/* search for mixer kcontrol */ /* search for mixer kcontrol */
for (i = 0; i < dest->num_kcontrols; i++) { for (i = 0; i < path->sink->num_kcontrols; i++) {
if (!strcmp(control_name, dest->kcontrol_news[i].name)) { if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) {
list_add(&path->list, &dapm->card->paths); path->name = path->sink->kcontrol_news[i].name;
list_add(&path->list_sink, &dest->sources); dapm_set_mixer_path_status(path, i);
list_add(&path->list_source, &src->sinks);
path->name = dest->kcontrol_news[i].name;
dapm_set_mixer_path_status(dest, path, i);
return 0; return 0;
} }
} }
...@@ -738,8 +835,10 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) ...@@ -738,8 +835,10 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
if (ret < 0) if (ret < 0)
return ret; return ret;
list_for_each_entry(path, &w->sources, list_sink) list_for_each_entry(path, &w->sources, list_sink) {
dapm_kcontrol_add_path(w->kcontrols[0], path); if (path->name)
dapm_kcontrol_add_path(w->kcontrols[0], path);
}
return 0; return 0;
} }
...@@ -754,34 +853,6 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w) ...@@ -754,34 +853,6 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w)
return 0; return 0;
} }
/* reset 'walked' bit for each dapm path */
static void dapm_clear_walk_output(struct snd_soc_dapm_context *dapm,
struct list_head *sink)
{
struct snd_soc_dapm_path *p;
list_for_each_entry(p, sink, list_source) {
if (p->walked) {
p->walked = 0;
dapm_clear_walk_output(dapm, &p->sink->sinks);
}
}
}
static void dapm_clear_walk_input(struct snd_soc_dapm_context *dapm,
struct list_head *source)
{
struct snd_soc_dapm_path *p;
list_for_each_entry(p, source, list_sink) {
if (p->walked) {
p->walked = 0;
dapm_clear_walk_input(dapm, &p->source->sources);
}
}
}
/* We implement power down on suspend by checking the power state of /* We implement power down on suspend by checking the power state of
* the ALSA card - when we are suspending the ALSA state for the card * the ALSA card - when we are suspending the ALSA state for the card
* is set to D3. * is set to D3.
...@@ -856,61 +927,23 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, ...@@ -856,61 +927,23 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
DAPM_UPDATE_STAT(widget, path_checks); DAPM_UPDATE_STAT(widget, path_checks);
switch (widget->id) { if (widget->is_sink && widget->connected) {
case snd_soc_dapm_supply: widget->outputs = snd_soc_dapm_suspend_check(widget);
case snd_soc_dapm_regulator_supply: return widget->outputs;
case snd_soc_dapm_clock_supply:
case snd_soc_dapm_kcontrol:
return 0;
default:
break;
}
switch (widget->id) {
case snd_soc_dapm_adc:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dai_out:
if (widget->active) {
widget->outputs = snd_soc_dapm_suspend_check(widget);
return widget->outputs;
}
default:
break;
}
if (widget->connected) {
/* connected pin ? */
if (widget->id == snd_soc_dapm_output && !widget->ext) {
widget->outputs = snd_soc_dapm_suspend_check(widget);
return widget->outputs;
}
/* connected jack or spk ? */
if (widget->id == snd_soc_dapm_hp ||
widget->id == snd_soc_dapm_spk ||
(widget->id == snd_soc_dapm_line &&
!list_empty(&widget->sources))) {
widget->outputs = snd_soc_dapm_suspend_check(widget);
return widget->outputs;
}
} }
list_for_each_entry(path, &widget->sinks, list_source) { list_for_each_entry(path, &widget->sinks, list_source) {
DAPM_UPDATE_STAT(widget, neighbour_checks); DAPM_UPDATE_STAT(widget, neighbour_checks);
if (path->weak) if (path->weak || path->is_supply)
continue; continue;
if (path->walking) if (path->walking)
return 1; return 1;
if (path->walked)
continue;
trace_snd_soc_dapm_output_path(widget, path); trace_snd_soc_dapm_output_path(widget, path);
if (path->sink && path->connect) { if (path->connect) {
path->walked = 1;
path->walking = 1; path->walking = 1;
/* do we need to add this widget to the list ? */ /* do we need to add this widget to the list ? */
...@@ -952,73 +985,23 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, ...@@ -952,73 +985,23 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
DAPM_UPDATE_STAT(widget, path_checks); DAPM_UPDATE_STAT(widget, path_checks);
switch (widget->id) { if (widget->is_source && widget->connected) {
case snd_soc_dapm_supply: widget->inputs = snd_soc_dapm_suspend_check(widget);
case snd_soc_dapm_regulator_supply: return widget->inputs;
case snd_soc_dapm_clock_supply:
case snd_soc_dapm_kcontrol:
return 0;
default:
break;
}
/* active stream ? */
switch (widget->id) {
case snd_soc_dapm_dac:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_dai_in:
if (widget->active) {
widget->inputs = snd_soc_dapm_suspend_check(widget);
return widget->inputs;
}
default:
break;
}
if (widget->connected) {
/* connected pin ? */
if (widget->id == snd_soc_dapm_input && !widget->ext) {
widget->inputs = snd_soc_dapm_suspend_check(widget);
return widget->inputs;
}
/* connected VMID/Bias for lower pops */
if (widget->id == snd_soc_dapm_vmid) {
widget->inputs = snd_soc_dapm_suspend_check(widget);
return widget->inputs;
}
/* connected jack ? */
if (widget->id == snd_soc_dapm_mic ||
(widget->id == snd_soc_dapm_line &&
!list_empty(&widget->sinks))) {
widget->inputs = snd_soc_dapm_suspend_check(widget);
return widget->inputs;
}
/* signal generator */
if (widget->id == snd_soc_dapm_siggen) {
widget->inputs = snd_soc_dapm_suspend_check(widget);
return widget->inputs;
}
} }
list_for_each_entry(path, &widget->sources, list_sink) { list_for_each_entry(path, &widget->sources, list_sink) {
DAPM_UPDATE_STAT(widget, neighbour_checks); DAPM_UPDATE_STAT(widget, neighbour_checks);
if (path->weak) if (path->weak || path->is_supply)
continue; continue;
if (path->walking) if (path->walking)
return 1; return 1;
if (path->walked)
continue;
trace_snd_soc_dapm_input_path(widget, path); trace_snd_soc_dapm_input_path(widget, path);
if (path->source && path->connect) { if (path->connect) {
path->walked = 1;
path->walking = 1; path->walking = 1;
/* do we need to add this widget to the list ? */ /* do we need to add this widget to the list ? */
...@@ -1060,21 +1043,25 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, ...@@ -1060,21 +1043,25 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
struct snd_soc_dapm_widget_list **list) struct snd_soc_dapm_widget_list **list)
{ {
struct snd_soc_card *card = dai->card; struct snd_soc_card *card = dai->component->card;
struct snd_soc_dapm_widget *w;
int paths; int paths;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
dapm_reset(card);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) { /*
* For is_connected_{output,input}_ep fully discover the graph we need
* to reset the cached number of inputs and outputs.
*/
list_for_each_entry(w, &card->widgets, list) {
w->inputs = -1;
w->outputs = -1;
}
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
paths = is_connected_output_ep(dai->playback_widget, list); paths = is_connected_output_ep(dai->playback_widget, list);
dapm_clear_walk_output(&card->dapm, else
&dai->playback_widget->sinks);
} else {
paths = is_connected_input_ep(dai->capture_widget, list); paths = is_connected_input_ep(dai->capture_widget, list);
dapm_clear_walk_input(&card->dapm,
&dai->capture_widget->sources);
}
trace_snd_soc_dapm_connected(paths, stream); trace_snd_soc_dapm_connected(paths, stream);
mutex_unlock(&card->dapm_mutex); mutex_unlock(&card->dapm_mutex);
...@@ -1163,44 +1150,10 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) ...@@ -1163,44 +1150,10 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
DAPM_UPDATE_STAT(w, power_checks); DAPM_UPDATE_STAT(w, power_checks);
in = is_connected_input_ep(w, NULL); in = is_connected_input_ep(w, NULL);
dapm_clear_walk_input(w->dapm, &w->sources);
out = is_connected_output_ep(w, NULL); out = is_connected_output_ep(w, NULL);
dapm_clear_walk_output(w->dapm, &w->sinks);
return out != 0 && in != 0; return out != 0 && in != 0;
} }
/* Check to see if an ADC has power */
static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
{
int in;
DAPM_UPDATE_STAT(w, power_checks);
if (w->active) {
in = is_connected_input_ep(w, NULL);
dapm_clear_walk_input(w->dapm, &w->sources);
return in != 0;
} else {
return dapm_generic_check_power(w);
}
}
/* Check to see if a DAC has power */
static int dapm_dac_check_power(struct snd_soc_dapm_widget *w)
{
int out;
DAPM_UPDATE_STAT(w, power_checks);
if (w->active) {
out = is_connected_output_ep(w, NULL);
dapm_clear_walk_output(w->dapm, &w->sinks);
return out != 0;
} else {
return dapm_generic_check_power(w);
}
}
/* Check to see if a power supply is needed */ /* Check to see if a power supply is needed */
static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
{ {
...@@ -1219,9 +1172,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) ...@@ -1219,9 +1172,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
!path->connected(path->source, path->sink)) !path->connected(path->source, path->sink))
continue; continue;
if (!path->sink)
continue;
if (dapm_widget_power_check(path->sink)) if (dapm_widget_power_check(path->sink))
return 1; return 1;
} }
...@@ -1636,27 +1586,14 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, ...@@ -1636,27 +1586,14 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
/* If we changed our power state perhaps our neigbours changed /* If we changed our power state perhaps our neigbours changed
* also. * also.
*/ */
list_for_each_entry(path, &w->sources, list_sink) { list_for_each_entry(path, &w->sources, list_sink)
if (path->source) { dapm_widget_set_peer_power(path->source, power, path->connect);
dapm_widget_set_peer_power(path->source, power,
/* Supplies can't affect their outputs, only their inputs */
if (!w->is_supply) {
list_for_each_entry(path, &w->sinks, list_source)
dapm_widget_set_peer_power(path->sink, power,
path->connect); path->connect);
}
}
switch (w->id) {
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_clock_supply:
case snd_soc_dapm_kcontrol:
/* Supplies can't affect their outputs, only their inputs */
break;
default:
list_for_each_entry(path, &w->sinks, list_source) {
if (path->sink) {
dapm_widget_set_peer_power(path->sink, power,
path->connect);
}
}
break;
} }
if (power) if (power)
...@@ -1863,10 +1800,14 @@ static ssize_t dapm_widget_power_read_file(struct file *file, ...@@ -1863,10 +1800,14 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
in = is_connected_input_ep(w, NULL); /* Supply widgets are not handled by is_connected_{input,output}_ep() */
dapm_clear_walk_input(w->dapm, &w->sources); if (w->is_supply) {
out = is_connected_output_ep(w, NULL); in = 0;
dapm_clear_walk_output(w->dapm, &w->sinks); out = 0;
} else {
in = is_connected_input_ep(w, NULL);
out = is_connected_output_ep(w, NULL);
}
ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d",
w->name, w->power ? "On" : "Off", w->name, w->power ? "On" : "Off",
...@@ -2011,32 +1952,45 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) ...@@ -2011,32 +1952,45 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
#endif #endif
/*
* soc_dapm_connect_path() - Connects or disconnects a path
* @path: The path to update
* @connect: The new connect state of the path. True if the path is connected,
* false if it is disconneted.
* @reason: The reason why the path changed (for debugging only)
*/
static void soc_dapm_connect_path(struct snd_soc_dapm_path *path,
bool connect, const char *reason)
{
if (path->connect == connect)
return;
path->connect = connect;
dapm_mark_dirty(path->source, reason);
dapm_mark_dirty(path->sink, reason);
dapm_path_invalidate(path);
}
/* test and update the power status of a mux widget */ /* test and update the power status of a mux widget */
static int soc_dapm_mux_update_power(struct snd_soc_card *card, static int soc_dapm_mux_update_power(struct snd_soc_card *card,
struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
{ {
struct snd_soc_dapm_path *path; struct snd_soc_dapm_path *path;
int found = 0; int found = 0;
bool connect;
lockdep_assert_held(&card->dapm_mutex); lockdep_assert_held(&card->dapm_mutex);
/* find dapm widget path assoc with kcontrol */ /* find dapm widget path assoc with kcontrol */
dapm_kcontrol_for_each_path(path, kcontrol) { dapm_kcontrol_for_each_path(path, kcontrol) {
if (!path->name || !e->texts[mux])
continue;
found = 1; found = 1;
/* we now need to match the string in the enum to the path */ /* we now need to match the string in the enum to the path */
if (!(strcmp(path->name, e->texts[mux]))) { if (!(strcmp(path->name, e->texts[mux])))
path->connect = 1; /* new connection */ connect = true;
dapm_mark_dirty(path->source, "mux connection"); else
} else { connect = false;
if (path->connect)
dapm_mark_dirty(path->source, soc_dapm_connect_path(path, connect, "mux update");
"mux disconnection");
path->connect = 0; /* old connection must be powered down */
}
dapm_mark_dirty(path->sink, "mux change");
} }
if (found) if (found)
...@@ -2075,9 +2029,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, ...@@ -2075,9 +2029,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
/* find dapm widget path assoc with kcontrol */ /* find dapm widget path assoc with kcontrol */
dapm_kcontrol_for_each_path(path, kcontrol) { dapm_kcontrol_for_each_path(path, kcontrol) {
found = 1; found = 1;
path->connect = connect; soc_dapm_connect_path(path, connect, "mixer update");
dapm_mark_dirty(path->source, "mixer connection");
dapm_mark_dirty(path->sink, "mixer update");
} }
if (found) if (found)
...@@ -2255,8 +2207,11 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, ...@@ -2255,8 +2207,11 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
return -EINVAL; return -EINVAL;
} }
if (w->connected != status) if (w->connected != status) {
dapm_mark_dirty(w, "pin configuration"); dapm_mark_dirty(w, "pin configuration");
dapm_widget_invalidate_input_paths(w);
dapm_widget_invalidate_output_paths(w);
}
w->connected = status; w->connected = status;
if (status == 0) if (status == 0)
...@@ -2309,6 +2264,53 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) ...@@ -2309,6 +2264,53 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
} }
EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
/*
* dapm_update_widget_flags() - Re-compute widget sink and source flags
* @w: The widget for which to update the flags
*
* Some widgets have a dynamic category which depends on which neighbors they
* are connected to. This function update the category for these widgets.
*
* This function must be called whenever a path is added or removed to a widget.
*/
static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
{
struct snd_soc_dapm_path *p;
switch (w->id) {
case snd_soc_dapm_input:
w->is_source = 1;
list_for_each_entry(p, &w->sources, list_sink) {
if (p->source->id == snd_soc_dapm_micbias ||
p->source->id == snd_soc_dapm_mic ||
p->source->id == snd_soc_dapm_line ||
p->source->id == snd_soc_dapm_output) {
w->is_source = 0;
break;
}
}
break;
case snd_soc_dapm_output:
w->is_sink = 1;
list_for_each_entry(p, &w->sinks, list_source) {
if (p->sink->id == snd_soc_dapm_spk ||
p->sink->id == snd_soc_dapm_hp ||
p->sink->id == snd_soc_dapm_line ||
p->sink->id == snd_soc_dapm_input) {
w->is_sink = 0;
break;
}
}
break;
case snd_soc_dapm_line:
w->is_sink = !list_empty(&w->sources);
w->is_source = !list_empty(&w->sinks);
break;
default:
break;
}
}
static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
const char *control, const char *control,
...@@ -2318,6 +2320,27 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, ...@@ -2318,6 +2320,27 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_path *path; struct snd_soc_dapm_path *path;
int ret; int ret;
if (wsink->is_supply && !wsource->is_supply) {
dev_err(dapm->dev,
"Connecting non-supply widget to supply widget is not supported (%s -> %s)\n",
wsource->name, wsink->name);
return -EINVAL;
}
if (connected && !wsource->is_supply) {
dev_err(dapm->dev,
"connected() callback only supported for supply widgets (%s -> %s)\n",
wsource->name, wsink->name);
return -EINVAL;
}
if (wsource->is_supply && control) {
dev_err(dapm->dev,
"Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n",
wsource->name, control, wsink->name);
return -EINVAL;
}
path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
if (!path) if (!path)
return -ENOMEM; return -ENOMEM;
...@@ -2330,85 +2353,49 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, ...@@ -2330,85 +2353,49 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
INIT_LIST_HEAD(&path->list_source); INIT_LIST_HEAD(&path->list_source);
INIT_LIST_HEAD(&path->list_sink); INIT_LIST_HEAD(&path->list_sink);
/* check for external widgets */ if (wsource->is_supply || wsink->is_supply)
if (wsink->id == snd_soc_dapm_input) { path->is_supply = 1;
if (wsource->id == snd_soc_dapm_micbias ||
wsource->id == snd_soc_dapm_mic ||
wsource->id == snd_soc_dapm_line ||
wsource->id == snd_soc_dapm_output)
wsink->ext = 1;
}
if (wsource->id == snd_soc_dapm_output) {
if (wsink->id == snd_soc_dapm_spk ||
wsink->id == snd_soc_dapm_hp ||
wsink->id == snd_soc_dapm_line ||
wsink->id == snd_soc_dapm_input)
wsource->ext = 1;
}
dapm_mark_dirty(wsource, "Route added");
dapm_mark_dirty(wsink, "Route added");
/* connect static paths */ /* connect static paths */
if (control == NULL) { if (control == NULL) {
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
path->connect = 1; path->connect = 1;
return 0; } else {
} /* connect dynamic paths */
switch (wsink->id) {
/* connect dynamic paths */ case snd_soc_dapm_mux:
switch (wsink->id) { ret = dapm_connect_mux(dapm, path, control);
case snd_soc_dapm_adc: if (ret != 0)
case snd_soc_dapm_dac: goto err;
case snd_soc_dapm_pga: break;
case snd_soc_dapm_out_drv: case snd_soc_dapm_switch:
case snd_soc_dapm_input: case snd_soc_dapm_mixer:
case snd_soc_dapm_output: case snd_soc_dapm_mixer_named_ctl:
case snd_soc_dapm_siggen: ret = dapm_connect_mixer(dapm, path, control);
case snd_soc_dapm_micbias: if (ret != 0)
case snd_soc_dapm_vmid: goto err;
case snd_soc_dapm_pre: break;
case snd_soc_dapm_post: default:
case snd_soc_dapm_supply: dev_err(dapm->dev,
case snd_soc_dapm_regulator_supply: "Control not supported for path %s -> [%s] -> %s\n",
case snd_soc_dapm_clock_supply: wsource->name, control, wsink->name);
case snd_soc_dapm_aif_in: ret = -EINVAL;
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dai_in:
case snd_soc_dapm_dai_out:
case snd_soc_dapm_dai_link:
case snd_soc_dapm_kcontrol:
list_add(&path->list, &dapm->card->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(dapm, wsource, wsink, path, control,
&wsink->kcontrol_news[0]);
if (ret != 0)
goto err;
break;
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
ret = dapm_connect_mixer(dapm, wsource, wsink, path, control);
if (ret != 0)
goto err; goto err;
break; }
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_line:
case snd_soc_dapm_spk:
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
path->connect = 0;
return 0;
} }
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
dapm_update_widget_flags(wsource);
dapm_update_widget_flags(wsink);
dapm_mark_dirty(wsource, "Route added");
dapm_mark_dirty(wsink, "Route added");
if (dapm->card->instantiated && path->connect)
dapm_path_invalidate(path);
return 0; return 0;
err: err:
kfree(path); kfree(path);
...@@ -2489,6 +2476,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, ...@@ -2489,6 +2476,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route) const struct snd_soc_dapm_route *route)
{ {
struct snd_soc_dapm_widget *wsource, *wsink;
struct snd_soc_dapm_path *path, *p; struct snd_soc_dapm_path *path, *p;
const char *sink; const char *sink;
const char *source; const char *source;
...@@ -2526,10 +2514,19 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, ...@@ -2526,10 +2514,19 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
} }
if (path) { if (path) {
dapm_mark_dirty(path->source, "Route removed"); wsource = path->source;
dapm_mark_dirty(path->sink, "Route removed"); wsink = path->sink;
dapm_mark_dirty(wsource, "Route removed");
dapm_mark_dirty(wsink, "Route removed");
if (path->connect)
dapm_path_invalidate(path);
dapm_free_path(path); dapm_free_path(path);
/* Update any path related flags */
dapm_update_widget_flags(wsource);
dapm_update_widget_flags(wsink);
} else { } else {
dev_warn(dapm->dev, "ASoC: Route %s->%s does not exist\n", dev_warn(dapm->dev, "ASoC: Route %s->%s does not exist\n",
source, sink); source, sink);
...@@ -3087,40 +3084,44 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, ...@@ -3087,40 +3084,44 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
} }
switch (w->id) { switch (w->id) {
case snd_soc_dapm_switch: case snd_soc_dapm_mic:
case snd_soc_dapm_mixer: case snd_soc_dapm_input:
case snd_soc_dapm_mixer_named_ctl: w->is_source = 1;
w->power_check = dapm_generic_check_power; w->power_check = dapm_generic_check_power;
break; break;
case snd_soc_dapm_mux: case snd_soc_dapm_spk:
case snd_soc_dapm_hp:
case snd_soc_dapm_output:
w->is_sink = 1;
w->power_check = dapm_generic_check_power; w->power_check = dapm_generic_check_power;
break; break;
case snd_soc_dapm_dai_out: case snd_soc_dapm_vmid:
w->power_check = dapm_adc_check_power; case snd_soc_dapm_siggen:
break; w->is_source = 1;
case snd_soc_dapm_dai_in: w->power_check = dapm_always_on_check_power;
w->power_check = dapm_dac_check_power;
break; break;
case snd_soc_dapm_mux:
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
case snd_soc_dapm_adc: case snd_soc_dapm_adc:
case snd_soc_dapm_aif_out: case snd_soc_dapm_aif_out:
case snd_soc_dapm_dac: case snd_soc_dapm_dac:
case snd_soc_dapm_aif_in: case snd_soc_dapm_aif_in:
case snd_soc_dapm_pga: case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv: case snd_soc_dapm_out_drv:
case snd_soc_dapm_input:
case snd_soc_dapm_output:
case snd_soc_dapm_micbias: case snd_soc_dapm_micbias:
case snd_soc_dapm_spk:
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_line: case snd_soc_dapm_line:
case snd_soc_dapm_dai_link: case snd_soc_dapm_dai_link:
case snd_soc_dapm_dai_out:
case snd_soc_dapm_dai_in:
w->power_check = dapm_generic_check_power; w->power_check = dapm_generic_check_power;
break; break;
case snd_soc_dapm_supply: case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply: case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_clock_supply: case snd_soc_dapm_clock_supply:
case snd_soc_dapm_kcontrol: case snd_soc_dapm_kcontrol:
w->is_supply = 1;
w->power_check = dapm_supply_check_power; w->power_check = dapm_supply_check_power;
break; break;
default: default:
...@@ -3137,6 +3138,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, ...@@ -3137,6 +3138,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
INIT_LIST_HEAD(&w->dirty); INIT_LIST_HEAD(&w->dirty);
list_add(&w->list, &dapm->card->widgets); list_add(&w->list, &dapm->card->widgets);
w->inputs = -1;
w->outputs = -1;
/* machine layer set ups unconnected pins and insertions */ /* machine layer set ups unconnected pins and insertions */
w->connected = 1; w->connected = 1;
return w; return w;
...@@ -3484,6 +3488,14 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, ...@@ -3484,6 +3488,14 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
break; break;
} }
if (w->id == snd_soc_dapm_dai_in) {
w->is_source = w->active;
dapm_widget_invalidate_input_paths(w);
} else {
w->is_sink = w->active;
dapm_widget_invalidate_output_paths(w);
}
} }
} }
...@@ -3610,7 +3622,15 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, ...@@ -3610,7 +3622,15 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
} }
dev_dbg(w->dapm->dev, "ASoC: force enable pin %s\n", pin); dev_dbg(w->dapm->dev, "ASoC: force enable pin %s\n", pin);
w->connected = 1; if (!w->connected) {
/*
* w->force does not affect the number of input or output paths,
* so we only have to recheck if w->connected is changed
*/
dapm_widget_invalidate_input_paths(w);
dapm_widget_invalidate_output_paths(w);
w->connected = 1;
}
w->force = 1; w->force = 1;
dapm_mark_dirty(w, "force enable"); dapm_mark_dirty(w, "force enable");
...@@ -3788,35 +3808,54 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, ...@@ -3788,35 +3808,54 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
} }
EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
/**
* dapm_is_external_path() - Checks if a path is a external path
* @card: The card the path belongs to
* @path: The path to check
*
* Returns true if the path is either between two different DAPM contexts or
* between two external pins of the same DAPM context. Otherwise returns
* false.
*/
static bool dapm_is_external_path(struct snd_soc_card *card,
struct snd_soc_dapm_path *path)
{
dev_dbg(card->dev,
"... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n",
path->source->name, path->source->id, path->source->dapm,
path->sink->name, path->sink->id, path->sink->dapm);
/* Connection between two different DAPM contexts */
if (path->source->dapm != path->sink->dapm)
return true;
/* Loopback connection from external pin to external pin */
if (path->sink->id == snd_soc_dapm_input) {
switch (path->source->id) {
case snd_soc_dapm_output:
case snd_soc_dapm_micbias:
return true;
default:
break;
}
}
return false;
}
static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card, static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card,
struct snd_soc_dapm_widget *w) struct snd_soc_dapm_widget *w)
{ {
struct snd_soc_dapm_path *p; struct snd_soc_dapm_path *p;
list_for_each_entry(p, &card->paths, list) { list_for_each_entry(p, &w->sources, list_sink) {
if ((p->source == w) || (p->sink == w)) { if (dapm_is_external_path(card, p))
dev_dbg(card->dev, return true;
"... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n", }
p->source->name, p->source->id, p->source->dapm,
p->sink->name, p->sink->id, p->sink->dapm);
/* Connected to something other than the codec */ list_for_each_entry(p, &w->sinks, list_source) {
if (p->source->dapm != p->sink->dapm) if (dapm_is_external_path(card, p))
return true; return true;
/*
* Loopback connection from codec external pin to
* codec external pin
*/
if (p->sink->id == snd_soc_dapm_input) {
switch (p->source->id) {
case snd_soc_dapm_output:
case snd_soc_dapm_micbias:
return true;
default:
break;
}
}
}
} }
return false; return false;
......
/*
* soc-ops.c -- Generic ASoC operations
*
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand Ltd.
* Copyright (C) 2010 Slimlogic Ltd.
* Copyright (C) 2010 Texas Instruments Inc.
*
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
* 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.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/bitops.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dpcm.h>
#include <sound/initval.h>
/**
* snd_soc_info_enum_double - enumerated double mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information about a double enumerated
* mixer control.
*
* Returns 0 for success.
*/
int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2,
e->items, e->texts);
}
EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
/**
* snd_soc_get_enum_double - enumerated double mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a double enumerated mixer.
*
* Returns 0 for success.
*/
int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, item;
unsigned int reg_val;
int ret;
ret = snd_soc_component_read(component, e->reg, &reg_val);
if (ret)
return ret;
val = (reg_val >> e->shift_l) & e->mask;
item = snd_soc_enum_val_to_item(e, val);
ucontrol->value.enumerated.item[0] = item;
if (e->shift_l != e->shift_r) {
val = (reg_val >> e->shift_l) & e->mask;
item = snd_soc_enum_val_to_item(e, val);
ucontrol->value.enumerated.item[1] = item;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
/**
* snd_soc_put_enum_double - enumerated double mixer put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to set the value of a double enumerated mixer.
*
* Returns 0 for success.
*/
int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int *item = ucontrol->value.enumerated.item;
unsigned int val;
unsigned int mask;
if (item[0] >= e->items)
return -EINVAL;
val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
mask = e->mask << e->shift_l;
if (e->shift_l != e->shift_r) {
if (item[1] >= e->items)
return -EINVAL;
val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
mask |= e->mask << e->shift_r;
}
return snd_soc_component_update_bits(component, e->reg, mask, val);
}
EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
/**
* snd_soc_read_signed - Read a codec register and interprete as signed value
* @component: component
* @reg: Register to read
* @mask: Mask to use after shifting the register value
* @shift: Right shift of register value
* @sign_bit: Bit that describes if a number is negative or not.
* @signed_val: Pointer to where the read value should be stored
*
* This functions reads a codec register. The register value is shifted right
* by 'shift' bits and masked with the given 'mask'. Afterwards it translates
* the given registervalue into a signed integer if sign_bit is non-zero.
*
* Returns 0 on sucess, otherwise an error value
*/
static int snd_soc_read_signed(struct snd_soc_component *component,
unsigned int reg, unsigned int mask, unsigned int shift,
unsigned int sign_bit, int *signed_val)
{
int ret;
unsigned int val;
ret = snd_soc_component_read(component, reg, &val);
if (ret < 0)
return ret;
val = (val >> shift) & mask;
if (!sign_bit) {
*signed_val = val;
return 0;
}
/* non-negative number */
if (!(val & BIT(sign_bit))) {
*signed_val = val;
return 0;
}
ret = val;
/*
* The register most probably does not contain a full-sized int.
* Instead we have an arbitrary number of bits in a signed
* representation which has to be translated into a full-sized int.
* This is done by filling up all bits above the sign-bit.
*/
ret |= ~((int)(BIT(sign_bit) - 1));
*signed_val = ret;
return 0;
}
/**
* snd_soc_info_volsw - single mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information about a single mixer control, or a double
* mixer control that spans 2 registers.
*
* Returns 0 for success.
*/
int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
int platform_max;
if (!mc->platform_max)
mc->platform_max = mc->max;
platform_max = mc->platform_max;
if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
else
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = platform_max - mc->min;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
/**
* snd_soc_get_volsw - single mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a single mixer control, or a double mixer
* control that spans 2 registers.
*
* Returns 0 for success.
*/
int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
int max = mc->max;
int min = mc->min;
int sign_bit = mc->sign_bit;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
int val;
int ret;
if (sign_bit)
mask = BIT(sign_bit + 1) - 1;
ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val);
if (ret)
return ret;
ucontrol->value.integer.value[0] = val - min;
if (invert)
ucontrol->value.integer.value[0] =
max - ucontrol->value.integer.value[0];
if (snd_soc_volsw_is_stereo(mc)) {
if (reg == reg2)
ret = snd_soc_read_signed(component, reg, mask, rshift,
sign_bit, &val);
else
ret = snd_soc_read_signed(component, reg2, mask, shift,
sign_bit, &val);
if (ret)
return ret;
ucontrol->value.integer.value[1] = val - min;
if (invert)
ucontrol->value.integer.value[1] =
max - ucontrol->value.integer.value[1];
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
/**
* snd_soc_put_volsw - single mixer put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to set the value of a single mixer control, or a double mixer
* control that spans 2 registers.
*
* Returns 0 for success.
*/
int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
int max = mc->max;
int min = mc->min;
unsigned int sign_bit = mc->sign_bit;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
int err;
bool type_2r = false;
unsigned int val2 = 0;
unsigned int val, val_mask;
if (sign_bit)
mask = BIT(sign_bit + 1) - 1;
val = ((ucontrol->value.integer.value[0] + min) & mask);
if (invert)
val = max - val;
val_mask = mask << shift;
val = val << shift;
if (snd_soc_volsw_is_stereo(mc)) {
val2 = ((ucontrol->value.integer.value[1] + min) & mask);
if (invert)
val2 = max - val2;
if (reg == reg2) {
val_mask |= mask << rshift;
val |= val2 << rshift;
} else {
val2 = val2 << shift;
type_2r = true;
}
}
err = snd_soc_component_update_bits(component, reg, val_mask, val);
if (err < 0)
return err;
if (type_2r)
err = snd_soc_component_update_bits(component, reg2, val_mask,
val2);
return err;
}
EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
/**
* snd_soc_get_volsw_sx - single mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a single mixer control, or a double mixer
* control that spans 2 registers.
*
* Returns 0 for success.
*/
int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
int max = mc->max;
int min = mc->min;
int mask = (1 << (fls(min + max) - 1)) - 1;
unsigned int val;
int ret;
ret = snd_soc_component_read(component, reg, &val);
if (ret < 0)
return ret;
ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask;
if (snd_soc_volsw_is_stereo(mc)) {
ret = snd_soc_component_read(component, reg2, &val);
if (ret < 0)
return ret;
val = ((val >> rshift) - min) & mask;
ucontrol->value.integer.value[1] = val;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
/**
* snd_soc_put_volsw_sx - double mixer set callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to set the value of a double mixer control that spans 2 registers.
*
* Returns 0 for success.
*/
int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
int max = mc->max;
int min = mc->min;
int mask = (1 << (fls(min + max) - 1)) - 1;
int err = 0;
unsigned int val, val_mask, val2 = 0;
val_mask = mask << shift;
val = (ucontrol->value.integer.value[0] + min) & mask;
val = val << shift;
err = snd_soc_component_update_bits(component, reg, val_mask, val);
if (err < 0)
return err;
if (snd_soc_volsw_is_stereo(mc)) {
val_mask = mask << rshift;
val2 = (ucontrol->value.integer.value[1] + min) & mask;
val2 = val2 << rshift;
err = snd_soc_component_update_bits(component, reg2, val_mask,
val2);
}
return err;
}
EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
/**
* snd_soc_info_volsw_range - single mixer info callback with range.
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information, within a range, about a single
* mixer control.
*
* returns 0 for success.
*/
int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
int platform_max;
int min = mc->min;
if (!mc->platform_max)
mc->platform_max = mc->max;
platform_max = mc->platform_max;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = platform_max - min;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range);
/**
* snd_soc_put_volsw_range - single mixer put value callback with range.
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to set the value, within a range, for a single mixer control.
*
* Returns 0 for success.
*/
int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg;
unsigned int rreg = mc->rreg;
unsigned int shift = mc->shift;
int min = mc->min;
int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
unsigned int val, val_mask;
int ret;
if (invert)
val = (max - ucontrol->value.integer.value[0]) & mask;
else
val = ((ucontrol->value.integer.value[0] + min) & mask);
val_mask = mask << shift;
val = val << shift;
ret = snd_soc_component_update_bits(component, reg, val_mask, val);
if (ret < 0)
return ret;
if (snd_soc_volsw_is_stereo(mc)) {
if (invert)
val = (max - ucontrol->value.integer.value[1]) & mask;
else
val = ((ucontrol->value.integer.value[1] + min) & mask);
val_mask = mask << shift;
val = val << shift;
ret = snd_soc_component_update_bits(component, rreg, val_mask,
val);
}
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
/**
* snd_soc_get_volsw_range - single mixer get callback with range
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value, within a range, of a single mixer control.
*
* Returns 0 for success.
*/
int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int rreg = mc->rreg;
unsigned int shift = mc->shift;
int min = mc->min;
int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
unsigned int val;
int ret;
ret = snd_soc_component_read(component, reg, &val);
if (ret)
return ret;
ucontrol->value.integer.value[0] = (val >> shift) & mask;
if (invert)
ucontrol->value.integer.value[0] =
max - ucontrol->value.integer.value[0];
else
ucontrol->value.integer.value[0] =
ucontrol->value.integer.value[0] - min;
if (snd_soc_volsw_is_stereo(mc)) {
ret = snd_soc_component_read(component, rreg, &val);
if (ret)
return ret;
ucontrol->value.integer.value[1] = (val >> shift) & mask;
if (invert)
ucontrol->value.integer.value[1] =
max - ucontrol->value.integer.value[1];
else
ucontrol->value.integer.value[1] =
ucontrol->value.integer.value[1] - min;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
/**
* snd_soc_limit_volume - Set new limit to an existing volume control.
*
* @codec: where to look for the control
* @name: Name of the control
* @max: new maximum limit
*
* Return 0 for success, else error.
*/
int snd_soc_limit_volume(struct snd_soc_codec *codec,
const char *name, int max)
{
struct snd_card *card = codec->component.card->snd_card;
struct snd_kcontrol *kctl;
struct soc_mixer_control *mc;
int found = 0;
int ret = -EINVAL;
/* Sanity check for name and max */
if (unlikely(!name || max <= 0))
return -EINVAL;
list_for_each_entry(kctl, &card->controls, list) {
if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
found = 1;
break;
}
}
if (found) {
mc = (struct soc_mixer_control *)kctl->private_value;
if (max <= mc->max) {
mc->platform_max = max;
ret = 0;
}
}
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_bytes *params = (void *)kcontrol->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = params->num_regs * component->val_bytes;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_bytes_info);
int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_bytes *params = (void *)kcontrol->private_value;
int ret;
if (component->regmap)
ret = regmap_raw_read(component->regmap, params->base,
ucontrol->value.bytes.data,
params->num_regs * component->val_bytes);
else
ret = -EINVAL;
/* Hide any masked bytes to ensure consistent data reporting */
if (ret == 0 && params->mask) {
switch (component->val_bytes) {
case 1:
ucontrol->value.bytes.data[0] &= ~params->mask;
break;
case 2:
((u16 *)(&ucontrol->value.bytes.data))[0]
&= cpu_to_be16(~params->mask);
break;
case 4:
((u32 *)(&ucontrol->value.bytes.data))[0]
&= cpu_to_be32(~params->mask);
break;
default:
return -EINVAL;
}
}
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_bytes_get);
int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_bytes *params = (void *)kcontrol->private_value;
int ret, len;
unsigned int val, mask;
void *data;
if (!component->regmap || !params->num_regs)
return -EINVAL;
len = params->num_regs * component->val_bytes;
data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
if (!data)
return -ENOMEM;
/*
* If we've got a mask then we need to preserve the register
* bits. We shouldn't modify the incoming data so take a
* copy.
*/
if (params->mask) {
ret = regmap_read(component->regmap, params->base, &val);
if (ret != 0)
goto out;
val &= params->mask;
switch (component->val_bytes) {
case 1:
((u8 *)data)[0] &= ~params->mask;
((u8 *)data)[0] |= val;
break;
case 2:
mask = ~params->mask;
ret = regmap_parse_val(component->regmap,
&mask, &mask);
if (ret != 0)
goto out;
((u16 *)data)[0] &= mask;
ret = regmap_parse_val(component->regmap,
&val, &val);
if (ret != 0)
goto out;
((u16 *)data)[0] |= val;
break;
case 4:
mask = ~params->mask;
ret = regmap_parse_val(component->regmap,
&mask, &mask);
if (ret != 0)
goto out;
((u32 *)data)[0] &= mask;
ret = regmap_parse_val(component->regmap,
&val, &val);
if (ret != 0)
goto out;
((u32 *)data)[0] |= val;
break;
default:
ret = -EINVAL;
goto out;
}
}
ret = regmap_raw_write(component->regmap, params->base,
data, len);
out:
kfree(data);
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *ucontrol)
{
struct soc_bytes_ext *params = (void *)kcontrol->private_value;
ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
ucontrol->count = params->max;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext);
int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *tlv)
{
struct soc_bytes_ext *params = (void *)kcontrol->private_value;
unsigned int count = size < params->max ? size : params->max;
int ret = -ENXIO;
switch (op_flag) {
case SNDRV_CTL_TLV_OP_READ:
if (params->get)
ret = params->get(tlv, count);
break;
case SNDRV_CTL_TLV_OP_WRITE:
if (params->put)
ret = params->put(tlv, count);
break;
}
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback);
/**
* snd_soc_info_xr_sx - signed multi register info callback
* @kcontrol: mreg control
* @uinfo: control element information
*
* Callback to provide information of a control that can
* span multiple codec registers which together
* forms a single signed value in a MSB/LSB manner.
*
* Returns 0 for success.
*/
int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct soc_mreg_control *mc =
(struct soc_mreg_control *)kcontrol->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = mc->min;
uinfo->value.integer.max = mc->max;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
/**
* snd_soc_get_xr_sx - signed multi register get callback
* @kcontrol: mreg control
* @ucontrol: control element information
*
* Callback to get the value of a control that can span
* multiple codec registers which together forms a single
* signed value in a MSB/LSB manner. The control supports
* specifying total no of bits used to allow for bitfields
* across the multiple codec registers.
*
* Returns 0 for success.
*/
int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mreg_control *mc =
(struct soc_mreg_control *)kcontrol->private_value;
unsigned int regbase = mc->regbase;
unsigned int regcount = mc->regcount;
unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
unsigned int regwmask = (1<<regwshift)-1;
unsigned int invert = mc->invert;
unsigned long mask = (1UL<<mc->nbits)-1;
long min = mc->min;
long max = mc->max;
long val = 0;
unsigned int regval;
unsigned int i;
int ret;
for (i = 0; i < regcount; i++) {
ret = snd_soc_component_read(component, regbase+i, &regval);
if (ret)
return ret;
val |= (regval & regwmask) << (regwshift*(regcount-i-1));
}
val &= mask;
if (min < 0 && val > max)
val |= ~mask;
if (invert)
val = max - val;
ucontrol->value.integer.value[0] = val;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
/**
* snd_soc_put_xr_sx - signed multi register get callback
* @kcontrol: mreg control
* @ucontrol: control element information
*
* Callback to set the value of a control that can span
* multiple codec registers which together forms a single
* signed value in a MSB/LSB manner. The control supports
* specifying total no of bits used to allow for bitfields
* across the multiple codec registers.
*
* Returns 0 for success.
*/
int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mreg_control *mc =
(struct soc_mreg_control *)kcontrol->private_value;
unsigned int regbase = mc->regbase;
unsigned int regcount = mc->regcount;
unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
unsigned int regwmask = (1<<regwshift)-1;
unsigned int invert = mc->invert;
unsigned long mask = (1UL<<mc->nbits)-1;
long max = mc->max;
long val = ucontrol->value.integer.value[0];
unsigned int i, regval, regmask;
int err;
if (invert)
val = max - val;
val &= mask;
for (i = 0; i < regcount; i++) {
regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
err = snd_soc_component_update_bits(component, regbase+i,
regmask, regval);
if (err < 0)
return err;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
/**
* snd_soc_get_strobe - strobe get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback get the value of a strobe mixer control.
*
* Returns 0 for success.
*/
int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int mask = 1 << shift;
unsigned int invert = mc->invert != 0;
unsigned int val;
int ret;
ret = snd_soc_component_read(component, reg, &val);
if (ret)
return ret;
val &= mask;
if (shift != 0 && val != 0)
val = val >> shift;
ucontrol->value.enumerated.item[0] = val ^ invert;
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
/**
* snd_soc_put_strobe - strobe put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback strobe a register bit to high then low (or the inverse)
* in one pass of a single mixer enum control.
*
* Returns 1 for success.
*/
int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int mask = 1 << shift;
unsigned int invert = mc->invert != 0;
unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
unsigned int val1 = (strobe ^ invert) ? mask : 0;
unsigned int val2 = (strobe ^ invert) ? 0 : mask;
int err;
err = snd_soc_component_update_bits(component, reg, mask, val1);
if (err < 0)
return err;
return snd_soc_component_update_bits(component, reg, mask, val2);
}
EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
...@@ -654,6 +654,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) ...@@ -654,6 +654,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
codec_dai->rate = 0; codec_dai->rate = 0;
} }
snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream);
if (cpu_dai->driver->ops->shutdown) if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai); cpu_dai->driver->ops->shutdown(substream, cpu_dai);
...@@ -772,6 +774,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -772,6 +774,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
for (i = 0; i < rtd->num_codecs; i++) for (i = 0; i < rtd->num_codecs; i++)
snd_soc_dai_digital_mute(rtd->codec_dais[i], 0, snd_soc_dai_digital_mute(rtd->codec_dais[i], 0,
substream->stream); substream->stream);
snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream);
out: out:
mutex_unlock(&rtd->pcm_mutex); mutex_unlock(&rtd->pcm_mutex);
......
...@@ -292,7 +292,7 @@ static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd) ...@@ -292,7 +292,7 @@ static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd)
struct snd_card *card = rtd->card->snd_card; struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai; struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm; struct snd_pcm *pcm = rtd->pcm;
struct platform_device *pdev = to_platform_device(dai->platform->dev); struct platform_device *pdev = to_platform_device(rtd->platform->dev);
struct txx9aclc_soc_device *dev; struct txx9aclc_soc_device *dev;
struct resource *r; struct resource *r;
int i; int i;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment