diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 7ee2f70ca42e..4af1083e3287 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -85,6 +85,10 @@ #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \ { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} +#define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \ +{ .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \ + .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ + .num_kcontrols = 1} /* path domain with event - event handler must return 0 for success */ #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \ @@ -172,6 +176,12 @@ .get = snd_soc_dapm_get_enum_double, \ .put = snd_soc_dapm_put_enum_double, \ .private_value = (unsigned long)&xenum } +#define SOC_DAPM_VALUE_ENUM(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_value_enum_double, \ + .get = snd_soc_dapm_get_value_enum_double, \ + .put = snd_soc_dapm_put_value_enum_double, \ + .private_value = (unsigned long)&xenum } /* dapm stream operations */ #define SND_SOC_DAPM_STREAM_NOP 0x0 @@ -214,6 +224,10 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_new_control(struct snd_soc_codec *codec, const struct snd_soc_dapm_widget *widget); int snd_soc_dapm_new_controls(struct snd_soc_codec *codec, @@ -247,6 +261,7 @@ enum snd_soc_dapm_type { snd_soc_dapm_input = 0, /* input pin */ snd_soc_dapm_output, /* output pin */ snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */ + snd_soc_dapm_value_mux, /* selects 1 analog signal from many inputs */ snd_soc_dapm_mixer, /* mixes several analog signals together */ snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */ snd_soc_dapm_adc, /* analog to digital converter */ diff --git a/include/sound/soc.h b/include/sound/soc.h index f86e455d3828..9b930d342116 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -94,11 +94,22 @@ SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts) #define SOC_ENUM_SINGLE_EXT(xmax, xtexts) \ { .max = xmax, .texts = xtexts } +#define SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xmax, xtexts, xvalues) \ +{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ + .mask = xmask, .max = xmax, .texts = xtexts, .values = xvalues} +#define SOC_VALUE_ENUM_SINGLE(xreg, xshift, xmask, xmax, xtexts, xvalues) \ + SOC_VALUE_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xmax, xtexts, xvalues) #define SOC_ENUM(xname, xenum) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\ .info = snd_soc_info_enum_double, \ .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \ .private_value = (unsigned long)&xenum } +#define SOC_VALUE_ENUM(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\ + .info = snd_soc_info_value_enum_double, \ + .get = snd_soc_get_value_enum_double, \ + .put = snd_soc_put_value_enum_double, \ + .private_value = (unsigned long)&xenum } #define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\ xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -200,6 +211,12 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_info_value_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, @@ -406,6 +423,19 @@ struct soc_enum { void *dapm; }; +/* semi enumerated kcontrol */ +struct soc_value_enum { + unsigned short reg; + unsigned short reg2; + unsigned char shift_l; + unsigned char shift_r; + unsigned int max; + unsigned int mask; + const char **texts; + const unsigned int *values; + void *dapm; +}; + #include #endif diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index ef6539eea579..35afd0c33be5 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -321,10 +321,6 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev) { int ret; - ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL); - if (ret < 0) - goto err; - if (cpu_is_pxa25x() || cpu_is_pxa27x()) { pxa_gpio_mode(GPIO31_SYNC_AC97_MD); pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); @@ -339,7 +335,7 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev) if (IS_ERR(ac97conf_clk)) { ret = PTR_ERR(ac97conf_clk); ac97conf_clk = NULL; - goto err_irq; + goto err_conf; } } @@ -347,19 +343,30 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev) if (IS_ERR(ac97_clk)) { ret = PTR_ERR(ac97_clk); ac97_clk = NULL; - goto err_irq; + goto err_clk; } - return clk_enable(ac97_clk); + ret = clk_enable(ac97_clk); + if (ret) + goto err_clk2; + + ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL); + if (ret < 0) + goto err_irq; + + return 0; err_irq: GCR |= GCR_ACLINK_OFF; +err_clk2: + clk_put(ac97_clk); + ac97_clk = NULL; +err_clk: if (ac97conf_clk) { clk_put(ac97conf_clk); ac97conf_clk = NULL; } - free_irq(IRQ_AC97, NULL); -err: +err_conf: return ret; } EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_probe); diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index 1fb59a9d3719..6ea04be911d0 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -221,8 +221,8 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); /* not connected */ - snd_soc_dapm_disable_pin(codec, "RLINEIN"); - snd_soc_dapm_disable_pin(codec, "LLINEIN"); + snd_soc_dapm_nc_pin(codec, "RLINEIN"); + snd_soc_dapm_nc_pin(codec, "LLINEIN"); /* always connected */ snd_soc_dapm_enable_pin(codec, "Int Mic"); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c41289b5f586..d0e0d691ae51 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1,3 +1,13 @@ +# Helper to resolve issues with configs that have SPI enabled but I2C +# modular, meaning we can't build the codec driver in with I2C support. +# We use an ordered list of conditional defaults to pick the appropriate +# setting - SPI can't be modular so that case doesn't need to be covered. +config SND_SOC_I2C_AND_SPI + tristate + default m if I2C=m + default y if I2C=y + default y if SPI_MASTER=y + config SND_SOC_ALL_CODECS tristate "Build all ASoC CODEC drivers" select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS @@ -14,12 +24,12 @@ config SND_SOC_ALL_CODECS select SND_SOC_UDA134X select SND_SOC_UDA1380 if I2C select SND_SOC_WM8350 if MFD_WM8350 - select SND_SOC_WM8510 if (I2C || SPI_MASTER) + select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8580 if I2C - select SND_SOC_WM8728 if (I2C || SPI_MASTER) - select SND_SOC_WM8731 if (I2C || SPI_MASTER) - select SND_SOC_WM8750 if (I2C || SPI_MASTER) - select SND_SOC_WM8753 if (I2C || SPI_MASTER) + select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI + select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI + select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI + select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8900 if I2C select SND_SOC_WM8903 if I2C select SND_SOC_WM8971 if I2C diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 31e44e346dc8..fd0f338374a7 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -192,39 +192,51 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) /* Earpiece */ static const char *twl4030_earpiece_texts[] = - {"Off", "DACL1", "DACL2", "Invalid", "DACR1"}; + {"Off", "DACL1", "DACL2", "DACR1"}; -static const struct soc_enum twl4030_earpiece_enum = - SOC_ENUM_SINGLE(TWL4030_REG_EAR_CTL, 1, +static const unsigned int twl4030_earpiece_values[] = + {0x0, 0x1, 0x2, 0x4}; + +static const struct soc_value_enum twl4030_earpiece_enum = + SOC_VALUE_ENUM_SINGLE(TWL4030_REG_EAR_CTL, 1, 0x7, ARRAY_SIZE(twl4030_earpiece_texts), - twl4030_earpiece_texts); + twl4030_earpiece_texts, + twl4030_earpiece_values); static const struct snd_kcontrol_new twl4030_dapm_earpiece_control = -SOC_DAPM_ENUM("Route", twl4030_earpiece_enum); +SOC_DAPM_VALUE_ENUM("Route", twl4030_earpiece_enum); /* PreDrive Left */ static const char *twl4030_predrivel_texts[] = - {"Off", "DACL1", "DACL2", "Invalid", "DACR2"}; + {"Off", "DACL1", "DACL2", "DACR2"}; -static const struct soc_enum twl4030_predrivel_enum = - SOC_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 1, +static const unsigned int twl4030_predrivel_values[] = + {0x0, 0x1, 0x2, 0x4}; + +static const struct soc_value_enum twl4030_predrivel_enum = + SOC_VALUE_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 1, 0x7, ARRAY_SIZE(twl4030_predrivel_texts), - twl4030_predrivel_texts); + twl4030_predrivel_texts, + twl4030_predrivel_values); static const struct snd_kcontrol_new twl4030_dapm_predrivel_control = -SOC_DAPM_ENUM("Route", twl4030_predrivel_enum); +SOC_DAPM_VALUE_ENUM("Route", twl4030_predrivel_enum); /* PreDrive Right */ static const char *twl4030_predriver_texts[] = - {"Off", "DACR1", "DACR2", "Invalid", "DACL2"}; + {"Off", "DACR1", "DACR2", "DACL2"}; -static const struct soc_enum twl4030_predriver_enum = - SOC_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 1, +static const unsigned int twl4030_predriver_values[] = + {0x0, 0x1, 0x2, 0x4}; + +static const struct soc_value_enum twl4030_predriver_enum = + SOC_VALUE_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 1, 0x7, ARRAY_SIZE(twl4030_predriver_texts), - twl4030_predriver_texts); + twl4030_predriver_texts, + twl4030_predriver_values); static const struct snd_kcontrol_new twl4030_dapm_predriver_control = -SOC_DAPM_ENUM("Route", twl4030_predriver_enum); +SOC_DAPM_VALUE_ENUM("Route", twl4030_predriver_enum); /* Headset Left */ static const char *twl4030_hsol_texts[] = @@ -300,28 +312,35 @@ SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum); /* Left analog microphone selection */ static const char *twl4030_analoglmic_texts[] = - {"Off", "Main mic", "Headset mic", "Invalid", "AUXL", - "Invalid", "Invalid", "Invalid", "Carkit mic"}; + {"Off", "Main mic", "Headset mic", "AUXL", "Carkit mic"}; -static const struct soc_enum twl4030_analoglmic_enum = - SOC_ENUM_SINGLE(TWL4030_REG_ANAMICL, 0, +static const unsigned int twl4030_analoglmic_values[] = + {0x0, 0x1, 0x2, 0x4, 0x8}; + +static const struct soc_value_enum twl4030_analoglmic_enum = + SOC_VALUE_ENUM_SINGLE(TWL4030_REG_ANAMICL, 0, 0xf, ARRAY_SIZE(twl4030_analoglmic_texts), - twl4030_analoglmic_texts); + twl4030_analoglmic_texts, + twl4030_analoglmic_values); static const struct snd_kcontrol_new twl4030_dapm_analoglmic_control = -SOC_DAPM_ENUM("Route", twl4030_analoglmic_enum); +SOC_DAPM_VALUE_ENUM("Route", twl4030_analoglmic_enum); /* Right analog microphone selection */ static const char *twl4030_analogrmic_texts[] = - {"Off", "Sub mic", "Invalid", "Invalid", "AUXR"}; + {"Off", "Sub mic", "AUXR"}; -static const struct soc_enum twl4030_analogrmic_enum = - SOC_ENUM_SINGLE(TWL4030_REG_ANAMICR, 0, +static const unsigned int twl4030_analogrmic_values[] = + {0x0, 0x1, 0x4}; + +static const struct soc_value_enum twl4030_analogrmic_enum = + SOC_VALUE_ENUM_SINGLE(TWL4030_REG_ANAMICR, 0, 0x5, ARRAY_SIZE(twl4030_analogrmic_texts), - twl4030_analogrmic_texts); + twl4030_analogrmic_texts, + twl4030_analogrmic_values); static const struct snd_kcontrol_new twl4030_dapm_analogrmic_control = -SOC_DAPM_ENUM("Route", twl4030_analogrmic_enum); +SOC_DAPM_VALUE_ENUM("Route", twl4030_analogrmic_enum); /* TX1 L/R Analog/Digital microphone selection */ static const char *twl4030_micpathtx1_texts[] = @@ -347,28 +366,6 @@ static const struct soc_enum twl4030_micpathtx2_enum = static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control = SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum); -/* - * This function filters out the non valid mux settings, named as "Invalid" - * in the enum texts. - * Just refuse to set an invalid mux mode. - */ -static int twl4030_enum_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - int ret = 0; - int val; - - val = w->value >> e->shift_l; - if (!strcmp("Invalid", e->texts[val])) { - printk(KERN_WARNING "Invalid MUX setting on 0x%02x (%d)\n", - e->reg, val); - ret = -1; - } - - return ret; -} - static int micpath_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -737,16 +734,13 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { /* Output MUX controls */ /* Earpiece */ - SND_SOC_DAPM_MUX_E("Earpiece Mux", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_earpiece_control, twl4030_enum_event, - SND_SOC_DAPM_PRE_REG), + SND_SOC_DAPM_VALUE_MUX("Earpiece Mux", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_earpiece_control), /* PreDrivL/R */ - SND_SOC_DAPM_MUX_E("PredriveL Mux", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_predrivel_control, twl4030_enum_event, - SND_SOC_DAPM_PRE_REG), - SND_SOC_DAPM_MUX_E("PredriveR Mux", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_predriver_control, twl4030_enum_event, - SND_SOC_DAPM_PRE_REG), + SND_SOC_DAPM_VALUE_MUX("PredriveL Mux", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_predrivel_control), + SND_SOC_DAPM_VALUE_MUX("PredriveR Mux", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_predriver_control), /* HeadsetL/R */ SND_SOC_DAPM_MUX("HeadsetL Mux", SND_SOC_NOPM, 0, 0, &twl4030_dapm_hsol_control), @@ -789,12 +783,10 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_POST_REG), /* Analog input muxes with power switch for the physical ADCL/R */ - SND_SOC_DAPM_MUX_E("Analog Left Capture Route", - TWL4030_REG_AVADC_CTL, 3, 0, &twl4030_dapm_analoglmic_control, - twl4030_enum_event, SND_SOC_DAPM_PRE_REG), - SND_SOC_DAPM_MUX_E("Analog Right Capture Route", - TWL4030_REG_AVADC_CTL, 1, 0, &twl4030_dapm_analogrmic_control, - twl4030_enum_event, SND_SOC_DAPM_PRE_REG), + SND_SOC_DAPM_VALUE_MUX("Analog Left Capture Route", + TWL4030_REG_AVADC_CTL, 3, 0, &twl4030_dapm_analoglmic_control), + SND_SOC_DAPM_VALUE_MUX("Analog Right Capture Route", + TWL4030_REG_AVADC_CTL, 1, 0, &twl4030_dapm_analogrmic_control), SND_SOC_DAPM_PGA("Analog Left Amplifier", TWL4030_REG_ANAMICL, 4, 0, NULL, 0), diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 01b948bb55a1..54851f318568 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -26,7 +26,6 @@ #include "davinci-pcm.h" #include "davinci-i2s.h" -#define EVM_CODEC_CLOCK 22579200 #define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \ SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF) @@ -37,6 +36,21 @@ static int evm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; int ret = 0; + unsigned sysclk; + + /* ASP1 on DM355 EVM is clocked by an external oscillator */ + if (machine_is_davinci_dm355_evm()) + sysclk = 27000000; + + /* ASP0 in DM6446 EVM is clocked by U55, as configured by + * board-dm644x-evm.c using GPIOs from U18. There are six + * options; here we "know" we use a 48 KHz sample rate. + */ + else if (machine_is_davinci_evm()) + sysclk = 12288000; + + else + return -EINVAL; /* set codec DAI configuration */ ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT); @@ -49,8 +63,7 @@ static int evm_hw_params(struct snd_pcm_substream *substream, return ret; /* set the codec system clock */ - ret = snd_soc_dai_set_sysclk(codec_dai, 0, EVM_CODEC_CLOCK, - SND_SOC_CLOCK_OUT); + ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT); if (ret < 0) return ret; diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index a7b1d77b2105..4f7f04014585 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -10,6 +10,7 @@ config SND_OMAP_SOC_N810 tristate "SoC Audio support for Nokia N810" depends on SND_OMAP_SOC && MACH_NOKIA_N810 select SND_OMAP_SOC_MCBSP + select OMAP_MUX select SND_SOC_TLV320AIC3X help Say Y if you want to add support for SoC audio on Nokia N810. diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c index bd91594496b1..fcc2f5d9a878 100644 --- a/sound/soc/omap/omap3pandora.c +++ b/sound/soc/omap/omap3pandora.c @@ -180,6 +180,19 @@ static int omap3pandora_in_init(struct snd_soc_codec *codec) { int ret; + /* All TWL4030 output pins are floating */ + snd_soc_dapm_nc_pin(codec, "OUTL"), + snd_soc_dapm_nc_pin(codec, "OUTR"), + snd_soc_dapm_nc_pin(codec, "EARPIECE"), + snd_soc_dapm_nc_pin(codec, "PREDRIVEL"), + snd_soc_dapm_nc_pin(codec, "PREDRIVER"), + snd_soc_dapm_nc_pin(codec, "HSOL"), + snd_soc_dapm_nc_pin(codec, "HSOR"), + snd_soc_dapm_nc_pin(codec, "CARKITL"), + snd_soc_dapm_nc_pin(codec, "CARKITR"), + snd_soc_dapm_nc_pin(codec, "HFL"), + snd_soc_dapm_nc_pin(codec, "HFR"), + ret = snd_soc_dapm_new_controls(codec, omap3pandora_in_dapm_widgets, ARRAY_SIZE(omap3pandora_in_dapm_widgets)); if (ret < 0) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index f73c1341437c..6cbe7e82f238 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1584,6 +1584,113 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); +/** + * snd_soc_info_value_enum_double - semi enumerated double mixer info callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information about a double semi enumerated + * mixer control. + * + * 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_info_value_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_value_enum *e = (struct soc_value_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->max; + + if (uinfo->value.enumerated.item > e->max - 1) + uinfo->value.enumerated.item = e->max - 1; + strcpy(uinfo->value.enumerated.name, + e->texts[uinfo->value.enumerated.item]); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_value_enum_double); + +/** + * snd_soc_get_value_enum_double - semi enumerated double mixer get callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get 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_get_value_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_value_enum *e = (struct soc_value_enum *) + kcontrol->private_value; + unsigned short 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_value_enum *e = (struct soc_value_enum *) + kcontrol->private_value; + unsigned short val; + unsigned short 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; + } + + return snd_soc_update_bits(codec, e->reg, mask, val); +} +EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double); + /** * snd_soc_info_enum_ext - external enumerated single mixer info callback * @kcontrol: mixer control diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 6c79ca6df0bf..ad0d801677c1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -53,13 +53,15 @@ /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic, - snd_soc_dapm_mux, snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_pga, - snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post + snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_dac, + snd_soc_dapm_mixer, snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp, + snd_soc_dapm_spk, snd_soc_dapm_post }; static int dapm_down_seq[] = { snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic, - snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_post + snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_value_mux, + snd_soc_dapm_post }; static int dapm_status = 1; @@ -134,6 +136,25 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, } } break; + case snd_soc_dapm_value_mux: { + struct soc_value_enum *e = (struct soc_value_enum *) + w->kcontrols[i].private_value; + int val, item; + + val = snd_soc_read(w->codec, e->reg); + val = (val >> e->shift_l) & e->mask; + for (item = 0; item < e->max; item++) { + if (val == e->values[item]) + break; + } + + p->connect = 0; + for (i = 0; i < e->max; i++) { + if (!(strcmp(p->name, e->texts[i])) && item == i) + p->connect = 1; + } + } + break; /* does not effect routing - always connected */ case snd_soc_dapm_pga: case snd_soc_dapm_output: @@ -179,6 +200,30 @@ static int dapm_connect_mux(struct snd_soc_codec *codec, return -ENODEV; } +/* connect value_mux widget to it's interconnecting audio paths */ +static int dapm_connect_value_mux(struct snd_soc_codec *codec, + struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, + struct snd_soc_dapm_path *path, const char *control_name, + const struct snd_kcontrol_new *kcontrol) +{ + struct soc_value_enum *e = (struct soc_value_enum *) + kcontrol->private_value; + int i; + + for (i = 0; i < e->max; i++) { + if (!(strcmp(control_name, e->texts[i]))) { + list_add(&path->list, &codec->dapm_paths); + list_add(&path->list_sink, &dest->sources); + list_add(&path->list_source, &src->sinks); + path->name = (char *)e->texts[i]; + dapm_set_path_status(dest, path, 0); + return 0; + } + } + + return -ENODEV; +} + /* connect mixer widget to it's interconnecting audio paths */ static int dapm_connect_mixer(struct snd_soc_codec *codec, struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, @@ -653,6 +698,7 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action) case snd_soc_dapm_vmid: continue; case snd_soc_dapm_mux: + case snd_soc_dapm_value_mux: case snd_soc_dapm_output: case snd_soc_dapm_input: case snd_soc_dapm_switch: @@ -728,6 +774,45 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, return 0; } +/* test and update the power status of a value_mux widget */ +static int dapm_value_mux_update_power(struct snd_soc_dapm_widget *widget, + struct snd_kcontrol *kcontrol, int mask, + int mux, int val, struct soc_value_enum *e) +{ + struct snd_soc_dapm_path *path; + int found = 0; + + if (widget->id != snd_soc_dapm_value_mux) + return -ENODEV; + + if (!snd_soc_test_bits(widget->codec, e->reg, mask, val)) + return 0; + + /* find dapm widget path assoc with kcontrol */ + list_for_each_entry(path, &widget->codec->dapm_paths, list) { + if (path->kcontrol != kcontrol) + continue; + + if (!path->name || !e->texts[mux]) + continue; + + found = 1; + /* we now need to match the string in the enum to the path */ + if (!(strcmp(path->name, e->texts[mux]))) + path->connect = 1; /* new connection */ + else + path->connect = 0; /* old connection must be + powered down */ + } + + if (found) { + dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP); + dump_dapm(widget->codec, "mux power update"); + } + + return 0; +} + /* test and update the power status of a mixer or switch widget */ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kcontrol, int reg, @@ -965,6 +1050,12 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, if (ret != 0) goto err; break; + case snd_soc_dapm_value_mux: + ret = dapm_connect_value_mux(codec, wsource, wsink, path, + control, &wsink->kcontrols[0]); + if (ret != 0) + goto err; + break; case snd_soc_dapm_switch: case snd_soc_dapm_mixer: ret = dapm_connect_mixer(codec, wsource, wsink, path, control); @@ -1047,6 +1138,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) dapm_new_mixer(codec, w); break; case snd_soc_dapm_mux: + case snd_soc_dapm_value_mux: dapm_new_mux(codec, w); break; case snd_soc_dapm_adc: @@ -1273,6 +1365,105 @@ out: } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); +/** + * snd_soc_dapm_get_value_enum_double - dapm semi enumerated double mixer get + * callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value of a dapm semi enumerated double mixer control. + * + * 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_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + struct soc_value_enum *e = (struct soc_value_enum *) + kcontrol->private_value; + unsigned short reg_val, val, mux; + + reg_val = snd_soc_read(widget->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_dapm_get_value_enum_double); + +/** + * snd_soc_dapm_put_value_enum_double - dapm semi enumerated double mixer set + * callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to set the value of a dapm semi enumerated double mixer control. + * + * 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_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + struct soc_value_enum *e = (struct soc_value_enum *) + kcontrol->private_value; + unsigned short val, mux; + unsigned short mask; + int ret = 0; + + if (ucontrol->value.enumerated.item[0] > e->max - 1) + return -EINVAL; + mux = ucontrol->value.enumerated.item[0]; + 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; + } + + mutex_lock(&widget->codec->mutex); + widget->value = val; + dapm_value_mux_update_power(widget, kcontrol, mask, mux, val, e); + if (widget->event) { + if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { + ret = widget->event(widget, + kcontrol, SND_SOC_DAPM_PRE_REG); + if (ret < 0) + goto out; + } + ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); + if (widget->event_flags & SND_SOC_DAPM_POST_REG) + ret = widget->event(widget, + kcontrol, SND_SOC_DAPM_POST_REG); + } else + ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); + +out: + mutex_unlock(&widget->codec->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); + /** * snd_soc_dapm_new_control - create new dapm control * @codec: audio codec