Merge tag 'asoc-v3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next
ASoC: Updates for v3.15 Quite a busy release for ASoC this time, more on janitorial work than exciting new features but welcome nontheless: - Lots of cleanups from Takashi for enumerations; the original API for these was error prone so he's refactored lots of code to use more modern APIs which avoid issues. - Elimination of the ASoC level wrappers for I2C and SPI moving us closer to converting to regmap completely and avoiding some randconfig hassle. - Provide both manually and transparently locked DAPM APIs rather than a mix of the two fixing some concurrency issues. - Start converting CODEC drivers to use separate bus interface drivers rather than having them all in one file helping avoid dependency issues. - DPCM support for Intel Haswell and Bay Trail platforms. - Lots of work on improvements for simple-card, DaVinci and the Renesas rcar drivers. - New drivers for Analog Devices ADAU1977, TI PCM512x and parts of the CSR SiRF SoC.
This commit is contained in:
@@ -56,7 +56,6 @@ EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
|
||||
#endif
|
||||
|
||||
static DEFINE_MUTEX(client_mutex);
|
||||
static LIST_HEAD(dai_list);
|
||||
static LIST_HEAD(platform_list);
|
||||
static LIST_HEAD(codec_list);
|
||||
static LIST_HEAD(component_list);
|
||||
@@ -370,18 +369,22 @@ static ssize_t dai_list_read_file(struct file *file, char __user *user_buf,
|
||||
{
|
||||
char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
ssize_t len, ret = 0;
|
||||
struct snd_soc_component *component;
|
||||
struct snd_soc_dai *dai;
|
||||
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
list_for_each_entry(dai, &dai_list, list) {
|
||||
len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n", dai->name);
|
||||
if (len >= 0)
|
||||
ret += len;
|
||||
if (ret > PAGE_SIZE) {
|
||||
ret = PAGE_SIZE;
|
||||
break;
|
||||
list_for_each_entry(component, &component_list, list) {
|
||||
list_for_each_entry(dai, &component->dai_list, list) {
|
||||
len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
|
||||
dai->name);
|
||||
if (len >= 0)
|
||||
ret += len;
|
||||
if (ret > PAGE_SIZE) {
|
||||
ret = PAGE_SIZE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -855,6 +858,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
|
||||
{
|
||||
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
|
||||
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
|
||||
struct snd_soc_component *component;
|
||||
struct snd_soc_codec *codec;
|
||||
struct snd_soc_platform *platform;
|
||||
struct snd_soc_dai *codec_dai, *cpu_dai;
|
||||
@@ -863,18 +867,20 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
|
||||
dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);
|
||||
|
||||
/* Find CPU DAI from registered DAIs*/
|
||||
list_for_each_entry(cpu_dai, &dai_list, list) {
|
||||
list_for_each_entry(component, &component_list, list) {
|
||||
if (dai_link->cpu_of_node &&
|
||||
(cpu_dai->dev->of_node != dai_link->cpu_of_node))
|
||||
component->dev->of_node != dai_link->cpu_of_node)
|
||||
continue;
|
||||
if (dai_link->cpu_name &&
|
||||
strcmp(dev_name(cpu_dai->dev), dai_link->cpu_name))
|
||||
continue;
|
||||
if (dai_link->cpu_dai_name &&
|
||||
strcmp(cpu_dai->name, dai_link->cpu_dai_name))
|
||||
strcmp(dev_name(component->dev), dai_link->cpu_name))
|
||||
continue;
|
||||
list_for_each_entry(cpu_dai, &component->dai_list, list) {
|
||||
if (dai_link->cpu_dai_name &&
|
||||
strcmp(cpu_dai->name, dai_link->cpu_dai_name))
|
||||
continue;
|
||||
|
||||
rtd->cpu_dai = cpu_dai;
|
||||
rtd->cpu_dai = cpu_dai;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rtd->cpu_dai) {
|
||||
@@ -899,12 +905,10 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
|
||||
* CODEC found, so find CODEC DAI from registered DAIs from
|
||||
* this CODEC
|
||||
*/
|
||||
list_for_each_entry(codec_dai, &dai_list, list) {
|
||||
if (codec->dev == codec_dai->dev &&
|
||||
!strcmp(codec_dai->name,
|
||||
dai_link->codec_dai_name)) {
|
||||
|
||||
list_for_each_entry(codec_dai, &codec->component.dai_list, list) {
|
||||
if (!strcmp(codec_dai->name, dai_link->codec_dai_name)) {
|
||||
rtd->codec_dai = codec_dai;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1128,12 +1132,8 @@ static int soc_probe_codec(struct snd_soc_card *card,
|
||||
driver->num_dapm_widgets);
|
||||
|
||||
/* Create DAPM widgets for each DAI stream */
|
||||
list_for_each_entry(dai, &dai_list, list) {
|
||||
if (dai->dev != codec->dev)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(dai, &codec->component.dai_list, list)
|
||||
snd_soc_dapm_new_dai_widgets(&codec->dapm, dai);
|
||||
}
|
||||
|
||||
codec->dapm.idle_bias_off = driver->idle_bias_off;
|
||||
|
||||
@@ -1180,6 +1180,7 @@ static int soc_probe_platform(struct snd_soc_card *card,
|
||||
{
|
||||
int ret = 0;
|
||||
const struct snd_soc_platform_driver *driver = platform->driver;
|
||||
struct snd_soc_component *component;
|
||||
struct snd_soc_dai *dai;
|
||||
|
||||
platform->card = card;
|
||||
@@ -1195,11 +1196,11 @@ static int soc_probe_platform(struct snd_soc_card *card,
|
||||
driver->dapm_widgets, driver->num_dapm_widgets);
|
||||
|
||||
/* Create DAPM widgets for each DAI stream */
|
||||
list_for_each_entry(dai, &dai_list, list) {
|
||||
if (dai->dev != platform->dev)
|
||||
list_for_each_entry(component, &component_list, list) {
|
||||
if (component->dev != platform->dev)
|
||||
continue;
|
||||
|
||||
snd_soc_dapm_new_dai_widgets(&platform->dapm, dai);
|
||||
list_for_each_entry(dai, &component->dai_list, list)
|
||||
snd_soc_dapm_new_dai_widgets(&platform->dapm, dai);
|
||||
}
|
||||
|
||||
platform->dapm.idle_bias_off = 1;
|
||||
@@ -2570,10 +2571,10 @@ int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
|
||||
uinfo->value.enumerated.items = e->max;
|
||||
uinfo->value.enumerated.items = e->items;
|
||||
|
||||
if (uinfo->value.enumerated.item > e->max - 1)
|
||||
uinfo->value.enumerated.item = e->max - 1;
|
||||
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));
|
||||
@@ -2595,14 +2596,18 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
||||
unsigned int val;
|
||||
unsigned int val, item;
|
||||
unsigned int reg_val;
|
||||
|
||||
val = snd_soc_read(codec, e->reg);
|
||||
ucontrol->value.enumerated.item[0]
|
||||
= (val >> e->shift_l) & e->mask;
|
||||
if (e->shift_l != e->shift_r)
|
||||
ucontrol->value.enumerated.item[1] =
|
||||
(val >> e->shift_r) & e->mask;
|
||||
reg_val = snd_soc_read(codec, e->reg);
|
||||
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;
|
||||
}
|
||||
@@ -2622,17 +2627,18 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
|
||||
{
|
||||
struct snd_soc_codec *codec = 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 (ucontrol->value.enumerated.item[0] > e->max - 1)
|
||||
if (item[0] >= e->items)
|
||||
return -EINVAL;
|
||||
val = ucontrol->value.enumerated.item[0] << e->shift_l;
|
||||
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 (ucontrol->value.enumerated.item[1] > e->max - 1)
|
||||
if (item[1] >= e->items)
|
||||
return -EINVAL;
|
||||
val |= ucontrol->value.enumerated.item[1] << e->shift_r;
|
||||
val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
|
||||
mask |= e->mask << e->shift_r;
|
||||
}
|
||||
|
||||
@@ -2641,78 +2647,46 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
|
||||
EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
|
||||
|
||||
/**
|
||||
* snd_soc_get_value_enum_double - semi enumerated double mixer get callback
|
||||
* @kcontrol: mixer control
|
||||
* @ucontrol: control element information
|
||||
* snd_soc_read_signed - Read a codec register and interprete as signed value
|
||||
* @codec: codec
|
||||
* @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.
|
||||
*
|
||||
* Callback to get the value of a double semi enumerated mixer.
|
||||
* 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.
|
||||
*
|
||||
* Semi enumerated mixer: the enumerated items are referred as values. Can be
|
||||
* used for handling bitfield coded enumeration for example.
|
||||
*
|
||||
* Returns 0 for success.
|
||||
* Returns the register value as signed int.
|
||||
*/
|
||||
int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
static int snd_soc_read_signed(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int mask, unsigned int shift, unsigned int sign_bit)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
||||
unsigned int reg_val, val, mux;
|
||||
|
||||
reg_val = snd_soc_read(codec, e->reg);
|
||||
val = (reg_val >> e->shift_l) & e->mask;
|
||||
for (mux = 0; mux < e->max; mux++) {
|
||||
if (val == e->values[mux])
|
||||
break;
|
||||
}
|
||||
ucontrol->value.enumerated.item[0] = mux;
|
||||
if (e->shift_l != e->shift_r) {
|
||||
val = (reg_val >> e->shift_r) & e->mask;
|
||||
for (mux = 0; mux < e->max; mux++) {
|
||||
if (val == e->values[mux])
|
||||
break;
|
||||
}
|
||||
ucontrol->value.enumerated.item[1] = mux;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_get_value_enum_double);
|
||||
|
||||
/**
|
||||
* snd_soc_put_value_enum_double - semi enumerated double mixer put callback
|
||||
* @kcontrol: mixer control
|
||||
* @ucontrol: control element information
|
||||
*
|
||||
* Callback to set the value of a double semi enumerated mixer.
|
||||
*
|
||||
* Semi enumerated mixer: the enumerated items are referred as values. Can be
|
||||
* used for handling bitfield coded enumeration for example.
|
||||
*
|
||||
* Returns 0 for success.
|
||||
*/
|
||||
int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
unsigned int mask;
|
||||
|
||||
if (ucontrol->value.enumerated.item[0] > e->max - 1)
|
||||
return -EINVAL;
|
||||
val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l;
|
||||
mask = e->mask << e->shift_l;
|
||||
if (e->shift_l != e->shift_r) {
|
||||
if (ucontrol->value.enumerated.item[1] > e->max - 1)
|
||||
return -EINVAL;
|
||||
val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r;
|
||||
mask |= e->mask << e->shift_r;
|
||||
}
|
||||
val = (snd_soc_read(codec, reg) >> shift) & mask;
|
||||
|
||||
return snd_soc_update_bits_locked(codec, e->reg, mask, val);
|
||||
if (!sign_bit)
|
||||
return val;
|
||||
|
||||
/* non-negative number */
|
||||
if (!(val & BIT(sign_bit)))
|
||||
return val;
|
||||
|
||||
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));
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double);
|
||||
|
||||
/**
|
||||
* snd_soc_info_volsw - single mixer info callback
|
||||
@@ -2742,7 +2716,7 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
|
||||
|
||||
uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = platform_max;
|
||||
uinfo->value.integer.max = platform_max - mc->min;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
|
||||
@@ -2768,11 +2742,16 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
|
||||
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;
|
||||
|
||||
ucontrol->value.integer.value[0] =
|
||||
(snd_soc_read(codec, reg) >> shift) & mask;
|
||||
if (sign_bit)
|
||||
mask = BIT(sign_bit + 1) - 1;
|
||||
|
||||
ucontrol->value.integer.value[0] = snd_soc_read_signed(codec, reg, mask,
|
||||
shift, sign_bit) - min;
|
||||
if (invert)
|
||||
ucontrol->value.integer.value[0] =
|
||||
max - ucontrol->value.integer.value[0];
|
||||
@@ -2780,10 +2759,12 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
|
||||
if (snd_soc_volsw_is_stereo(mc)) {
|
||||
if (reg == reg2)
|
||||
ucontrol->value.integer.value[1] =
|
||||
(snd_soc_read(codec, reg) >> rshift) & mask;
|
||||
snd_soc_read_signed(codec, reg, mask, rshift,
|
||||
sign_bit) - min;
|
||||
else
|
||||
ucontrol->value.integer.value[1] =
|
||||
(snd_soc_read(codec, reg2) >> shift) & mask;
|
||||
snd_soc_read_signed(codec, reg2, mask, shift,
|
||||
sign_bit) - min;
|
||||
if (invert)
|
||||
ucontrol->value.integer.value[1] =
|
||||
max - ucontrol->value.integer.value[1];
|
||||
@@ -2814,20 +2795,25 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
|
||||
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 = 0;
|
||||
bool type_2r = false;
|
||||
unsigned int val2 = 0;
|
||||
unsigned int val, val_mask;
|
||||
|
||||
val = (ucontrol->value.integer.value[0] & 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] & mask);
|
||||
val2 = ((ucontrol->value.integer.value[1] + min) & mask);
|
||||
if (invert)
|
||||
val2 = max - val2;
|
||||
if (reg == reg2) {
|
||||
@@ -2835,7 +2821,7 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
|
||||
val |= val2 << rshift;
|
||||
} else {
|
||||
val2 = val2 << shift;
|
||||
type_2r = 1;
|
||||
type_2r = true;
|
||||
}
|
||||
}
|
||||
err = snd_soc_update_bits_locked(codec, reg, val_mask, val);
|
||||
@@ -3233,7 +3219,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
|
||||
struct soc_bytes *params = (void *)kcontrol->private_value;
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
int ret, len;
|
||||
unsigned int val;
|
||||
unsigned int val, mask;
|
||||
void *data;
|
||||
|
||||
if (!codec->using_regmap)
|
||||
@@ -3263,12 +3249,36 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
|
||||
((u8 *)data)[0] |= val;
|
||||
break;
|
||||
case 2:
|
||||
((u16 *)data)[0] &= cpu_to_be16(~params->mask);
|
||||
((u16 *)data)[0] |= cpu_to_be16(val);
|
||||
mask = ~params->mask;
|
||||
ret = regmap_parse_val(codec->control_data,
|
||||
&mask, &mask);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
|
||||
((u16 *)data)[0] &= mask;
|
||||
|
||||
ret = regmap_parse_val(codec->control_data,
|
||||
&val, &val);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
|
||||
((u16 *)data)[0] |= val;
|
||||
break;
|
||||
case 4:
|
||||
((u32 *)data)[0] &= cpu_to_be32(~params->mask);
|
||||
((u32 *)data)[0] |= cpu_to_be32(val);
|
||||
mask = ~params->mask;
|
||||
ret = regmap_parse_val(codec->control_data,
|
||||
&mask, &mask);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
|
||||
((u32 *)data)[0] &= mask;
|
||||
|
||||
ret = regmap_parse_val(codec->control_data,
|
||||
&val, &val);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
|
||||
((u32 *)data)[0] |= val;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
@@ -3607,6 +3617,30 @@ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
|
||||
|
||||
/**
|
||||
* snd_soc_of_xlate_tdm_slot - generate tx/rx slot mask.
|
||||
* @slots: Number of slots in use.
|
||||
* @tx_mask: bitmask representing active TX slots.
|
||||
* @rx_mask: bitmask representing active RX slots.
|
||||
*
|
||||
* Generates the TDM tx and rx slot default masks for DAI.
|
||||
*/
|
||||
static int snd_soc_of_xlate_tdm_slot_mask(unsigned int slots,
|
||||
unsigned int *tx_mask,
|
||||
unsigned int *rx_mask)
|
||||
{
|
||||
if (*tx_mask || *rx_mask)
|
||||
return 0;
|
||||
|
||||
if (!slots)
|
||||
return -EINVAL;
|
||||
|
||||
*tx_mask = (1 << slots) - 1;
|
||||
*rx_mask = (1 << slots) - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_dai_set_tdm_slot - configure DAI TDM.
|
||||
* @dai: DAI
|
||||
@@ -3621,11 +3655,17 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
|
||||
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
|
||||
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
|
||||
{
|
||||
if (dai->driver && dai->driver->ops->of_xlate_tdm_slot_mask)
|
||||
dai->driver->ops->of_xlate_tdm_slot_mask(slots,
|
||||
&tx_mask, &rx_mask);
|
||||
else
|
||||
snd_soc_of_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask);
|
||||
|
||||
if (dai->driver && dai->driver->ops->set_tdm_slot)
|
||||
return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask,
|
||||
slots, slot_width);
|
||||
else
|
||||
return -EINVAL;
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
|
||||
|
||||
@@ -3881,95 +3921,42 @@ static inline char *fmt_multiple_name(struct device *dev,
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_register_dai - Register a DAI with the ASoC core
|
||||
* snd_soc_unregister_dai - Unregister DAIs from the ASoC core
|
||||
*
|
||||
* @dai: DAI to register
|
||||
* @component: The component for which the DAIs should be unregistered
|
||||
*/
|
||||
static int snd_soc_register_dai(struct device *dev,
|
||||
struct snd_soc_dai_driver *dai_drv)
|
||||
static void snd_soc_unregister_dais(struct snd_soc_component *component)
|
||||
{
|
||||
struct snd_soc_codec *codec;
|
||||
struct snd_soc_dai *dai;
|
||||
struct snd_soc_dai *dai, *_dai;
|
||||
|
||||
dev_dbg(dev, "ASoC: dai register %s\n", dev_name(dev));
|
||||
|
||||
dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
|
||||
if (dai == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* create DAI component name */
|
||||
dai->name = fmt_single_name(dev, &dai->id);
|
||||
if (dai->name == NULL) {
|
||||
list_for_each_entry_safe(dai, _dai, &component->dai_list, list) {
|
||||
dev_dbg(component->dev, "ASoC: Unregistered DAI '%s'\n",
|
||||
dai->name);
|
||||
list_del(&dai->list);
|
||||
kfree(dai->name);
|
||||
kfree(dai);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dai->dev = dev;
|
||||
dai->driver = dai_drv;
|
||||
dai->dapm.dev = dev;
|
||||
if (!dai->driver->ops)
|
||||
dai->driver->ops = &null_dai_ops;
|
||||
|
||||
mutex_lock(&client_mutex);
|
||||
|
||||
list_for_each_entry(codec, &codec_list, list) {
|
||||
if (codec->dev == dev) {
|
||||
dev_dbg(dev, "ASoC: Mapped DAI %s to CODEC %s\n",
|
||||
dai->name, codec->name);
|
||||
dai->codec = codec;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dai->codec)
|
||||
dai->dapm.idle_bias_off = 1;
|
||||
|
||||
list_add(&dai->list, &dai_list);
|
||||
|
||||
mutex_unlock(&client_mutex);
|
||||
|
||||
dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_unregister_dai - Unregister a DAI from the ASoC core
|
||||
* snd_soc_register_dais - Register a DAI with the ASoC core
|
||||
*
|
||||
* @dai: DAI to unregister
|
||||
*/
|
||||
static void snd_soc_unregister_dai(struct device *dev)
|
||||
{
|
||||
struct snd_soc_dai *dai;
|
||||
|
||||
list_for_each_entry(dai, &dai_list, list) {
|
||||
if (dev == dai->dev)
|
||||
goto found;
|
||||
}
|
||||
return;
|
||||
|
||||
found:
|
||||
mutex_lock(&client_mutex);
|
||||
list_del(&dai->list);
|
||||
mutex_unlock(&client_mutex);
|
||||
|
||||
dev_dbg(dev, "ASoC: Unregistered DAI '%s'\n", dai->name);
|
||||
kfree(dai->name);
|
||||
kfree(dai);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_register_dais - Register multiple DAIs with the ASoC core
|
||||
*
|
||||
* @dai: Array of DAIs to register
|
||||
* @component: The component the DAIs are registered for
|
||||
* @codec: The CODEC that the DAIs are registered for, NULL if the component is
|
||||
* not a CODEC.
|
||||
* @dai_drv: DAI driver to use for the DAIs
|
||||
* @count: Number of DAIs
|
||||
* @legacy_dai_naming: Use the legacy naming scheme and let the DAI inherit the
|
||||
* parent's name.
|
||||
*/
|
||||
static int snd_soc_register_dais(struct device *dev,
|
||||
struct snd_soc_dai_driver *dai_drv, size_t count)
|
||||
static int snd_soc_register_dais(struct snd_soc_component *component,
|
||||
struct snd_soc_codec *codec, struct snd_soc_dai_driver *dai_drv,
|
||||
size_t count, bool legacy_dai_naming)
|
||||
{
|
||||
struct snd_soc_codec *codec;
|
||||
struct device *dev = component->dev;
|
||||
struct snd_soc_dai *dai;
|
||||
int i, ret = 0;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "ASoC: dai register %s #%Zu\n", dev_name(dev), count);
|
||||
|
||||
@@ -3981,69 +3968,53 @@ static int snd_soc_register_dais(struct device *dev,
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* create DAI component name */
|
||||
dai->name = fmt_multiple_name(dev, &dai_drv[i]);
|
||||
/*
|
||||
* Back in the old days when we still had component-less DAIs,
|
||||
* instead of having a static name, component-less DAIs would
|
||||
* inherit the name of the parent device so it is possible to
|
||||
* register multiple instances of the DAI. We still need to keep
|
||||
* the same naming style even though those DAIs are not
|
||||
* component-less anymore.
|
||||
*/
|
||||
if (count == 1 && legacy_dai_naming) {
|
||||
dai->name = fmt_single_name(dev, &dai->id);
|
||||
} else {
|
||||
dai->name = fmt_multiple_name(dev, &dai_drv[i]);
|
||||
if (dai_drv[i].id)
|
||||
dai->id = dai_drv[i].id;
|
||||
else
|
||||
dai->id = i;
|
||||
}
|
||||
if (dai->name == NULL) {
|
||||
kfree(dai);
|
||||
ret = -EINVAL;
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dai->component = component;
|
||||
dai->codec = codec;
|
||||
dai->dev = dev;
|
||||
dai->driver = &dai_drv[i];
|
||||
if (dai->driver->id)
|
||||
dai->id = dai->driver->id;
|
||||
else
|
||||
dai->id = i;
|
||||
dai->dapm.dev = dev;
|
||||
if (!dai->driver->ops)
|
||||
dai->driver->ops = &null_dai_ops;
|
||||
|
||||
mutex_lock(&client_mutex);
|
||||
|
||||
list_for_each_entry(codec, &codec_list, list) {
|
||||
if (codec->dev == dev) {
|
||||
dev_dbg(dev,
|
||||
"ASoC: Mapped DAI %s to CODEC %s\n",
|
||||
dai->name, codec->name);
|
||||
dai->codec = codec;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dai->codec)
|
||||
dai->dapm.idle_bias_off = 1;
|
||||
|
||||
list_add(&dai->list, &dai_list);
|
||||
list_add(&dai->list, &component->dai_list);
|
||||
|
||||
mutex_unlock(&client_mutex);
|
||||
|
||||
dev_dbg(dai->dev, "ASoC: Registered DAI '%s'\n", dai->name);
|
||||
dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
for (i--; i >= 0; i--)
|
||||
snd_soc_unregister_dai(dev);
|
||||
snd_soc_unregister_dais(component);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_unregister_dais - Unregister multiple DAIs from the ASoC core
|
||||
*
|
||||
* @dai: Array of DAIs to unregister
|
||||
* @count: Number of DAIs
|
||||
*/
|
||||
static void snd_soc_unregister_dais(struct device *dev, size_t count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
snd_soc_unregister_dai(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_register_component - Register a component with the ASoC core
|
||||
*
|
||||
@@ -4052,6 +4023,7 @@ static int
|
||||
__snd_soc_register_component(struct device *dev,
|
||||
struct snd_soc_component *cmpnt,
|
||||
const struct snd_soc_component_driver *cmpnt_drv,
|
||||
struct snd_soc_codec *codec,
|
||||
struct snd_soc_dai_driver *dai_drv,
|
||||
int num_dai, bool allow_single_dai)
|
||||
{
|
||||
@@ -4074,20 +4046,10 @@ __snd_soc_register_component(struct device *dev,
|
||||
cmpnt->driver = cmpnt_drv;
|
||||
cmpnt->dai_drv = dai_drv;
|
||||
cmpnt->num_dai = num_dai;
|
||||
INIT_LIST_HEAD(&cmpnt->dai_list);
|
||||
|
||||
/*
|
||||
* snd_soc_register_dai() uses fmt_single_name(), and
|
||||
* snd_soc_register_dais() uses fmt_multiple_name()
|
||||
* for dai->name which is used for name based matching
|
||||
*
|
||||
* this function is used from cpu/codec.
|
||||
* allow_single_dai flag can ignore "codec" driver reworking
|
||||
* since it had been used snd_soc_register_dais(),
|
||||
*/
|
||||
if ((1 == num_dai) && allow_single_dai)
|
||||
ret = snd_soc_register_dai(dev, dai_drv);
|
||||
else
|
||||
ret = snd_soc_register_dais(dev, dai_drv, num_dai);
|
||||
ret = snd_soc_register_dais(cmpnt, codec, dai_drv, num_dai,
|
||||
allow_single_dai);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
|
||||
goto error_component_name;
|
||||
@@ -4120,7 +4082,9 @@ int snd_soc_register_component(struct device *dev,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return __snd_soc_register_component(dev, cmpnt, cmpnt_drv,
|
||||
cmpnt->ignore_pmdown_time = true;
|
||||
|
||||
return __snd_soc_register_component(dev, cmpnt, cmpnt_drv, NULL,
|
||||
dai_drv, num_dai, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_register_component);
|
||||
@@ -4140,7 +4104,7 @@ void snd_soc_unregister_component(struct device *dev)
|
||||
return;
|
||||
|
||||
found:
|
||||
snd_soc_unregister_dais(dev, cmpnt->num_dai);
|
||||
snd_soc_unregister_dais(cmpnt);
|
||||
|
||||
mutex_lock(&client_mutex);
|
||||
list_del(&cmpnt->list);
|
||||
@@ -4318,7 +4282,7 @@ int snd_soc_register_codec(struct device *dev,
|
||||
codec->volatile_register = codec_drv->volatile_register;
|
||||
codec->readable_register = codec_drv->readable_register;
|
||||
codec->writable_register = codec_drv->writable_register;
|
||||
codec->ignore_pmdown_time = codec_drv->ignore_pmdown_time;
|
||||
codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;
|
||||
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
|
||||
codec->dapm.dev = dev;
|
||||
codec->dapm.codec = codec;
|
||||
@@ -4341,7 +4305,7 @@ int snd_soc_register_codec(struct device *dev,
|
||||
/* register component */
|
||||
ret = __snd_soc_register_component(dev, &codec->component,
|
||||
&codec_drv->component_driver,
|
||||
dai_drv, num_dai, false);
|
||||
codec, dai_drv, num_dai, false);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "ASoC: Failed to regster component: %d\n", ret);
|
||||
goto fail_codec_name;
|
||||
@@ -4416,6 +4380,122 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name);
|
||||
|
||||
static const struct snd_soc_dapm_widget simple_widgets[] = {
|
||||
SND_SOC_DAPM_MIC("Microphone", NULL),
|
||||
SND_SOC_DAPM_LINE("Line", NULL),
|
||||
SND_SOC_DAPM_HP("Headphone", NULL),
|
||||
SND_SOC_DAPM_SPK("Speaker", NULL),
|
||||
};
|
||||
|
||||
int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
|
||||
const char *propname)
|
||||
{
|
||||
struct device_node *np = card->dev->of_node;
|
||||
struct snd_soc_dapm_widget *widgets;
|
||||
const char *template, *wname;
|
||||
int i, j, num_widgets, ret;
|
||||
|
||||
num_widgets = of_property_count_strings(np, propname);
|
||||
if (num_widgets < 0) {
|
||||
dev_err(card->dev,
|
||||
"ASoC: Property '%s' does not exist\n", propname);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (num_widgets & 1) {
|
||||
dev_err(card->dev,
|
||||
"ASoC: Property '%s' length is not even\n", propname);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
num_widgets /= 2;
|
||||
if (!num_widgets) {
|
||||
dev_err(card->dev, "ASoC: Property '%s's length is zero\n",
|
||||
propname);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
widgets = devm_kcalloc(card->dev, num_widgets, sizeof(*widgets),
|
||||
GFP_KERNEL);
|
||||
if (!widgets) {
|
||||
dev_err(card->dev,
|
||||
"ASoC: Could not allocate memory for widgets\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_widgets; i++) {
|
||||
ret = of_property_read_string_index(np, propname,
|
||||
2 * i, &template);
|
||||
if (ret) {
|
||||
dev_err(card->dev,
|
||||
"ASoC: Property '%s' index %d read error:%d\n",
|
||||
propname, 2 * i, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(simple_widgets); j++) {
|
||||
if (!strncmp(template, simple_widgets[j].name,
|
||||
strlen(simple_widgets[j].name))) {
|
||||
widgets[i] = simple_widgets[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j >= ARRAY_SIZE(simple_widgets)) {
|
||||
dev_err(card->dev,
|
||||
"ASoC: DAPM widget '%s' is not supported\n",
|
||||
template);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_string_index(np, propname,
|
||||
(2 * i) + 1,
|
||||
&wname);
|
||||
if (ret) {
|
||||
dev_err(card->dev,
|
||||
"ASoC: Property '%s' index %d read error:%d\n",
|
||||
propname, (2 * i) + 1, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
widgets[i].name = wname;
|
||||
}
|
||||
|
||||
card->dapm_widgets = widgets;
|
||||
card->num_dapm_widgets = num_widgets;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets);
|
||||
|
||||
int snd_soc_of_parse_tdm_slot(struct device_node *np,
|
||||
unsigned int *slots,
|
||||
unsigned int *slot_width)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (of_property_read_bool(np, "dai-tdm-slot-num")) {
|
||||
ret = of_property_read_u32(np, "dai-tdm-slot-num", &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (slots)
|
||||
*slots = val;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(np, "dai-tdm-slot-width")) {
|
||||
ret = of_property_read_u32(np, "dai-tdm-slot-width", &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (slot_width)
|
||||
*slot_width = val;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot);
|
||||
|
||||
int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
|
||||
const char *propname)
|
||||
{
|
||||
|
Fai riferimento in un nuovo problema
Block a user