[alsa-devel] [PATCH 00/19] ASoC for moorestown
This series of patches provides the ASoC implementation of moorestown audio.
This series adds a new codec called upd9976 and a new machine driver called mrst_machine. The platform driver is mostly re-used.
With this patchset, it can do: - playback via headphone - playback via internal speaker - capture via DMIC - capture via headset microphone - jack detection
However, other functionalities such as voice support in uPD9976 codec is not implemented yet. This patchset mainly focuses on the audio support in uPD9976 codec.
Any comments are welcome.
---
Lu Guanqun (17): ASoC: upd9976: Add Renesas uPD9976 codec driver ASoC: sst_platform: add cpu dai driver for moorestown platform ASoC: mrst_machine: add moorestown machine driver ASoC: mrst_machine: add speaker widget to moorestown machine driver ASoC: mrst_machine: enable user to select different output ASoC: upd9976: add DMIC support ASoC: upd9976: add Analog MIC support ASoC: upd9976: add microphone bias support ASoC: upd9976: add jack detection function ASoC: mrst_machine: add capture functionality ASoC: mrst_machine: add jack detection support ASoC: upd9976: add mute switch for DMIC ASoC: upd9976: add mute switch for analog ASoC: mrst_machine: make DMIC's output to PCM2 mono ASoC: mrst_machine: make MIC2 pseudo-differential ASoC: upd9976: add capture volume for analog inputs ASoC: upd9976: add capture volume for DMIC
Wang Xingchao (2): ASoC: upd9976: add capture ability for dai driver ASoC: sst_platform: add capture capability for cpu dai driver
sound/soc/codecs/Kconfig | 4 sound/soc/codecs/Makefile | 2 sound/soc/codecs/upd9976.c | 569 ++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/upd9976.h | 85 ++++++ sound/soc/mid-x86/Kconfig | 12 + sound/soc/mid-x86/Makefile | 2 sound/soc/mid-x86/mrst_machine.c | 365 ++++++++++++++++++++++++ sound/soc/mid-x86/sst_platform.c | 20 + 8 files changed, 1059 insertions(+), 0 deletions(-) create mode 100644 sound/soc/codecs/upd9976.c create mode 100644 sound/soc/codecs/upd9976.h create mode 100644 sound/soc/mid-x86/mrst_machine.c
uPD9976 is a complex codec, however this patch only provides basic playback functionality for headphone. More functionality will be added bit by bit in the following patches.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com Signed-off-by: Wang Xingchao xingchao.wang@intel.com --- sound/soc/codecs/Kconfig | 4 sound/soc/codecs/Makefile | 2 sound/soc/codecs/upd9976.c | 407 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/upd9976.h | 83 +++++++++ 4 files changed, 496 insertions(+), 0 deletions(-) create mode 100644 sound/soc/codecs/upd9976.c create mode 100644 sound/soc/codecs/upd9976.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 2a69718..b914bc6 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -52,6 +52,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TWL6040 if TWL4030_CORE select SND_SOC_UDA134X select SND_SOC_UDA1380 if I2C + select SND_SOC_UPD9976 if INTEL_SCU_IPC select SND_SOC_WL1273 if MFD_WL1273_CORE select SND_SOC_WM1250_EV1 if I2C select SND_SOC_WM2000 if I2C @@ -244,6 +245,9 @@ config SND_SOC_UDA134X config SND_SOC_UDA1380 tristate
+config SND_SOC_UPD9976 + tristate + config SND_SOC_WL1273 tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4cb2f42..163c623 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -37,6 +37,7 @@ snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o snd-soc-uda134x-objs := uda134x.o snd-soc-uda1380-objs := uda1380.o +snd-soc-upd9976-objs := upd9976.o snd-soc-wl1273-objs := wl1273.o snd-soc-wm1250-ev1-objs := wm1250-ev1.o snd-soc-wm8350-objs := wm8350.o @@ -128,6 +129,7 @@ obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o +obj-$(CONFIG_SND_SOC_UPD9976) += snd-soc-upd9976.o obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o diff --git a/sound/soc/codecs/upd9976.c b/sound/soc/codecs/upd9976.c new file mode 100644 index 0000000..cd68633 --- /dev/null +++ b/sound/soc/codecs/upd9976.c @@ -0,0 +1,407 @@ +/* + * upd9976.c - Renesas uPD9976 codec driver + * + * Copyright (C) 2011 Intel Corporation + * + * Maintainer: + * Lu Guanqun guanqun.lu@intel.com + * Wang Xingchao xingchao.wang@intel.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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <asm/intel_scu_ipc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/jack.h> +#include "upd9976.h" + +static inline unsigned int upd9976_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + u8 value = 0; + int ret; + + ret = intel_scu_ipc_ioread8(reg, &value); + if (ret) + pr_err("upd9976 read of 0x%x failed, error: %d\n", reg, ret); + return value; +} + +static inline int upd9976_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + int ret; + + ret = intel_scu_ipc_iowrite8(reg, value); + if (ret) + pr_err("upd9976 write of 0x%x failed, error: %d\n", reg, ret); + return ret; +} + +/* + * Mixing Volume: from -25 dB to 6 dB in 1 dB steps. + */ +static DECLARE_TLV_DB_SCALE(mixer_tlv, -2500, 100, 0); + +static const struct snd_kcontrol_new upd9976_snd_controls[] = { + SOC_DOUBLE_R_TLV("Headphone & Speaker Volume", + UPD9976_HPSPRLVOL, UPD9976_HPSPRRVOL, + 0, 0x1f, 1, mixer_tlv), +}; + +static const struct snd_kcontrol_new upd9976_hp_spkr_mixer_left_controls[] = { + SOC_DAPM_SINGLE("Audio DAC Left", UPD9976_HPLMIXSEL, 4, 1, 1), + SOC_DAPM_SINGLE("Audio DAC Right", UPD9976_HPLMIXSEL, 3, 1, 1), +}; + +static const struct snd_kcontrol_new upd9976_hp_spkr_mixer_right_controls[] = { + SOC_DAPM_SINGLE("Audio DAC Left", UPD9976_HPRMIXSEL, 4, 1, 1), + SOC_DAPM_SINGLE("Audio DAC Right", UPD9976_HPRMIXSEL, 3, 1, 1), +}; + +static const struct snd_soc_dapm_widget upd9976_dapm_widgets[] = { + /* Input */ + SND_SOC_DAPM_INPUT("LINEINL"), + SND_SOC_DAPM_INPUT("LINEINR"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("DMICDAT"), + SND_SOC_DAPM_INPUT("HPINL"), + SND_SOC_DAPM_INPUT("HPINR"), + + /* Output */ + SND_SOC_DAPM_OUTPUT("PREOUTL"), + SND_SOC_DAPM_OUTPUT("PREOUTR"), + SND_SOC_DAPM_OUTPUT("EPOUTP"), + SND_SOC_DAPM_OUTPUT("EPOUTN"), + SND_SOC_DAPM_OUTPUT("LINEOUTL"), + SND_SOC_DAPM_OUTPUT("LINEOUTR"), + SND_SOC_DAPM_OUTPUT("HPOUTL"), + SND_SOC_DAPM_OUTPUT("HPOUTR"), + + /* DAC */ + SND_SOC_DAPM_DAC("ADAC", "Audio Playback", UPD9976_POWERCTRL1, 0, 0), + SND_SOC_DAPM_DAC("VDAC", "Voice Playback", UPD9976_POWERCTRL1, 6, 0), + + /* ADC */ + SND_SOC_DAPM_ADC("AADC", "Audio Capture", UPD9976_POWERCTRL1, 1, 0), + SND_SOC_DAPM_ADC("VADC", "Voice Capture", UPD9976_POWERCTRL1, 7, 0), + + /* Mixer */ + SND_SOC_DAPM_MIXER("HP Spkr Mixer Left", UPD9976_POWERCTRL2, 3, 0, + upd9976_hp_spkr_mixer_left_controls, + ARRAY_SIZE(upd9976_hp_spkr_mixer_left_controls)), + SND_SOC_DAPM_MIXER("HP Spkr Mixer Right", UPD9976_POWERCTRL2, 2, 0, + upd9976_hp_spkr_mixer_right_controls, + ARRAY_SIZE(upd9976_hp_spkr_mixer_right_controls)), + + /* PGA */ + SND_SOC_DAPM_PGA("HP Playback Left", UPD9976_DRVPOWERCTRL, + 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP Playback Right", UPD9976_DRVPOWERCTRL, + 1, 0, NULL, 0), + +}; + +static const struct snd_soc_dapm_route upd9976_dapm_routes[] = { + {"HP Spkr Mixer Left", "Audio DAC Left", "ADAC"}, + {"HP Spkr Mixer Left", "Audio DAC Right", "ADAC"}, + + {"HP Spkr Mixer Right", "Audio DAC Left", "ADAC"}, + {"HP Spkr Mixer Right", "Audio DAC Right", "ADAC"}, + + {"PREOUTL", NULL, "HP Spkr Mixer Left"}, + {"PREOUTR", NULL, "HP Spkr Mixer Right"}, + + {"HP Playback Left", NULL, "HPINL"}, + {"HP Playback Right", NULL, "HPINR"}, + + {"HPOUTL", NULL, "HP Playback Left"}, + {"HPOUTR", NULL, "HP Playback Right"}, + +}; + +static int upd9976_audio_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int value, mask; + + /* soft mute on, fast 0.75dB/6fs */ + value = 0; + if (mute) + value = 1; + mask = BIT(1) | BIT(0); + snd_soc_update_bits(codec, UPD9976_SOFTMUTE, mask, value); + + /* mute headphone, internals speaker, internal earpiece mono */ + value = 0; + if (mute) + value = BIT(2) | BIT(1) | BIT(0); + mask = BIT(2) | BIT(1) | BIT(0); + snd_soc_update_bits(codec, UPD9976_LMUTE, mask, value); + + /* mute headphone, internal speaker */ + value = 0; + if (mute) + value = BIT(2) | BIT(1); + mask = BIT(2) | BIT(1); + snd_soc_update_bits(codec, UPD9976_RMUTE, mask, value); + + /* mute audio DAC */ + value = 0; + if (mute) + value = BIT(7); + mask = BIT(7); + snd_soc_update_bits(codec, UPD9976_AUDIOLVOL, mask, value); + snd_soc_update_bits(codec, UPD9976_AUDIORVOL, mask, value); + + return 0; +} + +static int upd9976_audio_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int mode, mask; + + mask = BIT(5) | BIT(4); + mode = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + mode |= BIT(4); + break; + case SND_SOC_DAIFMT_RIGHT_J: + mode |= BIT(5); + break; + case SND_SOC_DAIFMT_LEFT_J: + mode |= BIT(5) | BIT(4); + break; + } + + mask |= BIT(7) | BIT(3); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + mode |= BIT(7) | BIT(3); + break; + } + + return snd_soc_update_bits(codec, UPD9976_AUDIOPORT1, mask, mode); +} + +static int upd9976_audio_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int tmp; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + tmp = 0x00; + break; + case SNDRV_PCM_FORMAT_S24_LE: + tmp = 0x03; + break; + case SNDRV_PCM_FORMAT_S18_3LE: + tmp = 0x01; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + tmp = 0x02; + break; + default: + return -EINVAL; + } + snd_soc_update_bits(codec, UPD9976_AUDIOPORT1, + BIT(2)|BIT(1)|BIT(0), tmp); + + switch (params_rate(params)) { + case 8000: + tmp = 0x00; + break; + case 11025: + tmp = 0x01; + break; + case 12000: + tmp = 0x02; + break; + case 16000: + tmp = 0x03; + break; + case 22050: + tmp = 0x04; + break; + case 24000: + tmp = 0x05; + break; + case 32000: + tmp = 0x07; + break; + case 44100: + tmp = 0x08; + break; + case 48000: + tmp = 0x09; + break; + default: + return -EINVAL; + } + snd_soc_update_bits(codec, UPD9976_AUDIOPORT2, + BIT(3)|BIT(2)|BIT(1)|BIT(0), tmp); + + return 0; +} + +static int upd9976_audio_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_codec *codec = dai->codec; + if (tristate) + snd_soc_update_bits(codec, UPD9976_AUDIOPORT1, + BIT(4)|BIT(5), 0); + return 0; +} + +static struct snd_soc_dai_ops upd9976_audio_dai_ops = { + .digital_mute = upd9976_audio_digital_mute, + .set_fmt = upd9976_audio_set_dai_fmt, + .set_tristate = upd9976_audio_set_tristate, + .hw_params = upd9976_audio_hw_params, +}; + +static struct snd_soc_dai_driver upd9976_dais[] = { +{ + .name = "upd9976-audio", + .playback = { + .stream_name = "Audio Playback", + .channels_min = 1, + .channels_max = 2, + .rates = (SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_8000), + .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 | + SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 | + SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32), + }, + .ops = &upd9976_audio_dai_ops, +}, +}; + +static int upd9976_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + snd_soc_update_bits(codec, UPD9976_VAUDIOCNT, + 0x27, 0x27); + snd_soc_update_bits(codec, UPD9976_VREFPLL, + 0x35, 0x35); + } + break; + + case SND_SOC_BIAS_STANDBY: + snd_soc_write(codec, UPD9976_VAUDIOCNT, 0x25); + snd_soc_write(codec, UPD9976_VREFPLL, 0x10); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_write(codec, UPD9976_VREFPLL, 0); + snd_soc_write(codec, UPD9976_VAUDIOCNT, 0x24); + break; + } + + codec->dapm.bias_level = level; + return 0; +} + +static int upd9976_codec_probe(struct snd_soc_codec *codec) +{ + upd9976_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int upd9976_codec_remove(struct snd_soc_codec *codec) +{ + return 0; +} + +static struct snd_soc_codec_driver upd9976_codec = { + .probe = upd9976_codec_probe, + .remove = upd9976_codec_remove, + .read = upd9976_read, + .write = upd9976_write, + .set_bias_level = upd9976_set_bias_level, + + .controls = upd9976_snd_controls, + .num_controls = ARRAY_SIZE(upd9976_snd_controls), + .dapm_widgets = upd9976_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(upd9976_dapm_widgets), + .dapm_routes = upd9976_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(upd9976_dapm_routes), +}; + +static int __devinit upd9976_device_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &upd9976_codec, + upd9976_dais, ARRAY_SIZE(upd9976_dais)); +} + +static int __devexit upd9976_device_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver upd9976_codec_driver = { + .driver = { + .name = "upd9976", + .owner = THIS_MODULE, + }, + .probe = upd9976_device_probe, + .remove = __devexit_p(upd9976_device_remove), +}; + +static int __init upd9976_init(void) +{ + return platform_driver_register(&upd9976_codec_driver); +} +module_init(upd9976_init); + +static void __exit upd9976_exit(void) +{ + platform_driver_unregister(&upd9976_codec_driver); +} +module_exit(upd9976_exit); + +MODULE_DESCRIPTION("ASoC Renesas uPD9976 codec driver"); +MODULE_AUTHOR("Lu Guanqun guanqun.lu@intel.com"); +MODULE_AUTHOR("Wang Xingchao xingchao.wang@intel.com"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:upd9976"); diff --git a/sound/soc/codecs/upd9976.h b/sound/soc/codecs/upd9976.h new file mode 100644 index 0000000..ab2ea15 --- /dev/null +++ b/sound/soc/codecs/upd9976.h @@ -0,0 +1,83 @@ +/* + * upd9976.h - Renesas uPD9976 codec driver + * + * Copyright (C) 2011 Intel Corporation + * + * Maintainer: + * Lu Guanqun guanqun.lu@intel.com + * Wang Xingchao xingchao.wang@intel.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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#ifndef _UPD9976_H +#define _UPD9976_H + +#define UPD9976_VAUDIOCNT 0x51 + +#define UPD9976_VOICEPORT1 0x100 +#define UPD9976_VOICEPORT2 0x101 +#define UPD9976_AUDIOPORT1 0x102 +#define UPD9976_AUDIOPORT2 0x103 +#define UPD9976_ADCSAMPLERATE 0x104 +#define UPD9976_DMICCTRL1 0x105 +#define UPD9976_DMICCTRL2 0x106 +#define UPD9976_MICCTRL 0x107 +#define UPD9976_MICSELVOL 0x108 +#define UPD9976_LILSEL 0x109 +#define UPD9976_LIRSEL 0x10a +#define UPD9976_VOICEVOL 0x10b +#define UPD9976_AUDIOLVOL 0x10c +#define UPD9976_AUDIORVOL 0x10d +#define UPD9976_LMUTE 0x10e +#define UPD9976_RMUTE 0x10f +#define UPD9976_POWERCTRL1 0x110 +#define UPD9976_POWERCTRL2 0x111 +#define UPD9976_DRVPOWERCTRL 0x112 +#define UPD9976_VREFPLL 0x113 +#define UPD9976_PCMBUFCTRL 0x114 +#define UPD9976_SOFTMUTE 0x115 +#define UPD9976_DTMFPATH 0x116 +#define UPD9976_DTMFVOL 0x117 +#define UPD9976_DTMFFREQ 0x118 +#define UPD9976_DTMFHFREQ 0x119 +#define UPD9976_DTMFLFREQ 0x11a +#define UPD9976_DTMFCTRL 0x11b +#define UPD9976_DTMFASON 0x11c +#define UPD9976_DTMFASOFF 0x11d +#define UPD9976_DTMFASINUM 0x11e +#define UPD9976_CLASSDVOL 0x11f +#define UPD9976_VOICEDACAVOL 0x120 +#define UPD9976_AUDDACAVOL 0x121 +#define UPD9976_LOMUTEVOL 0x122 +#define UPD9976_HPSPRLVOL 0x123 +#define UPD9976_HPSPRRVOL 0x124 +#define UPD9976_MONOVOL 0x125 +#define UPD9976_LINEOUTMIXVOL 0x126 +#define UPD9976_EPMIXVOL 0x127 +#define UPD9976_LINEOUTLSEL 0x128 +#define UPD9976_LINEOUTRSEL 0x129 +#define UPD9976_EPMIXOUTSEL 0x12a +#define UPD9976_HPLMIXSEL 0x12b +#define UPD9976_HPRMIXSEL 0x12c +#define UPD9976_LOANTIPOP 0x12d +#define UPD9976_AUXDBNC 0x12f + +#define UPD9976_SAUXINT 0x132 + +#endif
On Wed, May 04, 2011 at 09:44:58PM +0800, Lu Guanqun wrote:
Overall this looks very good, a few minor points below.
+static inline unsigned int upd9976_read(struct snd_soc_codec *codec,
unsigned int reg)
+{
I guess these SCU write functions are going to be shared with other CODECs for this CPU - we should probably push this into the soc-cache for them.
+static const struct snd_kcontrol_new upd9976_snd_controls[] = {
- SOC_DOUBLE_R_TLV("Headphone & Speaker Volume",
Master Volume would be a better name.
+static const struct snd_kcontrol_new upd9976_hp_spkr_mixer_left_controls[] = {
- SOC_DAPM_SINGLE("Audio DAC Left", UPD9976_HPLMIXSEL, 4, 1, 1),
- SOC_DAPM_SINGLE("Audio DAC Right", UPD9976_HPLMIXSEL, 3, 1, 1),
These need Switch at the end of the name.
+static struct snd_soc_dai_driver upd9976_dais[] = { +{
- .name = "upd9976-audio",
- .playback = {
Just drop the audio from the name, it's a CODEC so it's obviously audio.
On Wed, May 04, 2011 at 10:34:51PM +0800, Mark Brown wrote:
On Wed, May 04, 2011 at 09:44:58PM +0800, Lu Guanqun wrote:
Overall this looks very good, a few minor points below.
+static inline unsigned int upd9976_read(struct snd_soc_codec *codec,
unsigned int reg)
+{
I guess these SCU write functions are going to be shared with other CODECs for this CPU - we should probably push this into the soc-cache for them.
Yes, currently it's the same read/write operation as in sn95031 codec.
+static const struct snd_kcontrol_new upd9976_snd_controls[] = {
- SOC_DOUBLE_R_TLV("Headphone & Speaker Volume",
Master Volume would be a better name.
OK. I was trying to be more clear on what this volume represents.
+static const struct snd_kcontrol_new upd9976_hp_spkr_mixer_left_controls[] = {
- SOC_DAPM_SINGLE("Audio DAC Left", UPD9976_HPLMIXSEL, 4, 1, 1),
- SOC_DAPM_SINGLE("Audio DAC Right", UPD9976_HPLMIXSEL, 3, 1, 1),
These need Switch at the end of the name.
OK.
+static struct snd_soc_dai_driver upd9976_dais[] = { +{
- .name = "upd9976-audio",
- .playback = {
Just drop the audio from the name, it's a CODEC so it's obviously audio.
OK. Thanks for the review.
On Wed, 2011-05-04 at 20:35 +0530, Lu, Guanqun wrote:
On Wed, May 04, 2011 at 10:34:51PM +0800, Mark Brown wrote:
On Wed, May 04, 2011 at 09:44:58PM +0800, Lu Guanqun wrote:
Overall this looks very good, a few minor points below.
+static inline unsigned int upd9976_read(struct snd_soc_codec *codec,
unsigned int reg)
+{
I guess these SCU write functions are going to be shared with other CODECs for this CPU - we should probably push this into the soc-cache for them.
Yes, currently it's the same read/write operation as in sn95031 codec.
I would not do that yet, as I don't see any more codecs using this interface today or in near future :)
On Thu, May 05, 2011 at 11:14:19AM +0800, Koul, Vinod wrote:
On Wed, 2011-05-04 at 20:35 +0530, Lu, Guanqun wrote:
On Wed, May 04, 2011 at 10:34:51PM +0800, Mark Brown wrote:
On Wed, May 04, 2011 at 09:44:58PM +0800, Lu Guanqun wrote:
Overall this looks very good, a few minor points below.
+static inline unsigned int upd9976_read(struct snd_soc_codec *codec,
unsigned int reg)
+{
I guess these SCU write functions are going to be shared with other CODECs for this CPU - we should probably push this into the soc-cache for them.
Yes, currently it's the same read/write operation as in sn95031 codec.
I would not do that yet, as I don't see any more codecs using this interface today or in near future :)
In this series of patches, I'll not refactor them out and turn them into some common code. We can do it later when it's necessary.
At Wed, 4 May 2011 15:34:51 +0100, Mark Brown wrote:
On Wed, May 04, 2011 at 09:44:58PM +0800, Lu Guanqun wrote:
+static const struct snd_kcontrol_new upd9976_snd_controls[] = {
- SOC_DOUBLE_R_TLV("Headphone & Speaker Volume",
Master Volume would be a better name.
Yes, also in general, try to avoid special letters like "&".
Takashi
On Wed, May 04, 2011 at 11:07:24PM +0800, Takashi Iwai wrote:
At Wed, 4 May 2011 15:34:51 +0100, Mark Brown wrote:
On Wed, May 04, 2011 at 09:44:58PM +0800, Lu Guanqun wrote:
+static const struct snd_kcontrol_new upd9976_snd_controls[] = {
- SOC_DOUBLE_R_TLV("Headphone & Speaker Volume",
Master Volume would be a better name.
Yes, also in general, try to avoid special letters like "&".
I once used letter "/", but then it failed to create the corresponding debugfs directory... :)
Oh, btw: There are another register that's related to volume control, it's before DAC (not added in this patchset yet, for simplicity, will add it later). so the connection looks something like this:
[volume gain A] --> [DAC] --> [Mixer Volume] -> [Output Pins]
Which volume control should be the master volume? I'm a bit unclear on this one.
At Wed, 4 May 2011 23:18:27 +0800, Lu Guanqun wrote:
On Wed, May 04, 2011 at 11:07:24PM +0800, Takashi Iwai wrote:
At Wed, 4 May 2011 15:34:51 +0100, Mark Brown wrote:
On Wed, May 04, 2011 at 09:44:58PM +0800, Lu Guanqun wrote:
+static const struct snd_kcontrol_new upd9976_snd_controls[] = {
- SOC_DOUBLE_R_TLV("Headphone & Speaker Volume",
Master Volume would be a better name.
Yes, also in general, try to avoid special letters like "&".
I once used letter "/", but then it failed to create the corresponding debugfs directory... :)
Yeah, "/" can be used for control names for now because they aren't exposed in fs, but better to avoid if possible, too.
Oh, btw: There are another register that's related to volume control, it's before DAC (not added in this patchset yet, for simplicity, will add it later). so the connection looks something like this:
[volume gain A] --> [DAC] --> [Mixer Volume] -> [Output Pins]
Which volume control should be the master volume? I'm a bit unclear on this one.
If it's the only output, any of them can be a "Master" volume. But if there are other outputs, it shouldn't be named as "Master" at all.
Master volume is like Highlander(*), there can be only one :)
Takashi
(*) doesn't mean guys in Edinburgh
On Wed, May 04, 2011 at 05:38:33PM +0200, Takashi Iwai wrote:
If it's the only output, any of them can be a "Master" volume. But if there are other outputs, it shouldn't be named as "Master" at all.
If you've got a choice you probably want to pick an analogue control over a digital one as this will give better performance.
Master volume is like Highlander(*), there can be only one :)
(*) doesn't mean guys in Edinburgh
Definitely not, Edinburgh is in the lowlands not the highlands!
On Thu, May 05, 2011 at 12:15:46AM +0800, Mark Brown wrote:
On Wed, May 04, 2011 at 05:38:33PM +0200, Takashi Iwai wrote:
If it's the only output, any of them can be a "Master" volume. But if there are other outputs, it shouldn't be named as "Master" at all.
If you've got a choice you probably want to pick an analogue control over a digital one as this will give better performance.
Good to know that. But there's no single volume control after DAC that can control all the outputs. poor about that...
Master volume is like Highlander(*), there can be only one :)
(*) doesn't mean guys in Edinburgh
Definitely not, Edinburgh is in the lowlands not the highlands!
On Wed, May 04, 2011 at 11:38:33PM +0800, Takashi Iwai wrote:
At Wed, 4 May 2011 23:18:27 +0800, Lu Guanqun wrote:
On Wed, May 04, 2011 at 11:07:24PM +0800, Takashi Iwai wrote:
At Wed, 4 May 2011 15:34:51 +0100, Mark Brown wrote:
On Wed, May 04, 2011 at 09:44:58PM +0800, Lu Guanqun wrote:
+static const struct snd_kcontrol_new upd9976_snd_controls[] = {
- SOC_DOUBLE_R_TLV("Headphone & Speaker Volume",
Master Volume would be a better name.
Yes, also in general, try to avoid special letters like "&".
I once used letter "/", but then it failed to create the corresponding debugfs directory... :)
Yeah, "/" can be used for control names for now because they aren't exposed in fs, but better to avoid if possible, too.
Oh, btw: There are another register that's related to volume control, it's before DAC (not added in this patchset yet, for simplicity, will add it later). so the connection looks something like this:
[volume gain A] --> [DAC] --> [Mixer Volume] -> [Output Pins]
Which volume control should be the master volume? I'm a bit unclear on this one.
If it's the only output, any of them can be a "Master" volume. But if there are other outputs, it shouldn't be named as "Master" at all.
Thanks for the info. There are other outputs in this codec. .-> [Mixer Volume for Earpiece] -> [output pins] / [volume gain A] --> [DAC] --> [Mixer Volume for HP/Spkr] -> [Output Pins] \ `-> [Mixer Volume for LineOut] -> [LineOut Pins]
So in this case, I'll make the [volume gain A] as the master volume.
Master volume is like Highlander(*), there can be only one :)
Takashi
(*) doesn't mean guys in Edinburgh
On Wed, May 04, 2011 at 11:18:27PM +0800, Lu Guanqun wrote:
Oh, btw: There are another register that's related to volume control, it's before DAC (not added in this patchset yet, for simplicity, will add it later). so the connection looks something like this:
[volume gain A] --> [DAC] --> [Mixer Volume] -> [Output Pins]
Which volume control should be the master volume? I'm a bit unclear on this one.
Probably the mixer if that's the only path out.
On Wed, May 04, 2011 at 10:34:51PM +0800, Mark Brown wrote:
+static struct snd_soc_dai_driver upd9976_dais[] = { +{
- .name = "upd9976-audio",
- .playback = {
Just drop the audio from the name, it's a CODEC so it's obviously audio.
Giving it a second look, I recall why I added this "-audio", because there is another interface on the codec driver called "voice port", so in the future, I would expect to add a new dai driver called "upd9976-voice". So that's the differentiation. :)
I'm finished with all the changes according to you guys' comments. After I do a thorough test, I'll resend this patchset. Thanks.
On Fri, May 06, 2011 at 12:26:25AM +0800, Lu Guanqun wrote:
Giving it a second look, I recall why I added this "-audio", because there is another interface on the codec driver called "voice port", so in the future, I would expect to add a new dai driver called "upd9976-voice". So that's the differentiation. :)
Something like -hifi would make more sense, -audio is completely non-descriptive.
On Wed, May 04, 2011 at 09:44:58PM +0800, Lu Guanqun wrote:
uPD9976 is a complex codec, however this patch only provides basic playback functionality for headphone. More functionality will be added bit by bit in the following patches.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com Signed-off-by: Wang Xingchao xingchao.wang@intel.com
+static inline unsigned int upd9976_read(struct snd_soc_codec *codec,
unsigned int reg)
+{
- u8 value = 0;
- int ret;
- ret = intel_scu_ipc_ioread8(reg, &value);
- if (ret)
pr_err("upd9976 read of 0x%x failed, error: %d\n", reg, ret);
- return value;
+}
dev_err() would be more preferable.
+static int upd9976_audio_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{
- struct snd_soc_codec *codec = dai->codec;
- unsigned int mode, mask;
- mask = BIT(5) | BIT(4);
- mode = 0;
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
mode |= BIT(4);
break;
- case SND_SOC_DAIFMT_RIGHT_J:
mode |= BIT(5);
break;
- case SND_SOC_DAIFMT_LEFT_J:
mode |= BIT(5) | BIT(4);
break;
- }
- mask |= BIT(7) | BIT(3);
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
mode |= BIT(7) | BIT(3);
break;
- }
- return snd_soc_update_bits(codec, UPD9976_AUDIOPORT1, mask, mode);
+}
I am not sure whether the BIT() macro is more confusing than helpful.
+static int upd9976_audio_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
+{
- struct snd_soc_codec *codec = dai->codec;
- unsigned int tmp;
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
tmp = 0x00;
break;
- case SNDRV_PCM_FORMAT_S24_LE:
tmp = 0x03;
break;
- case SNDRV_PCM_FORMAT_S18_3LE:
tmp = 0x01;
break;
- case SNDRV_PCM_FORMAT_S20_3LE:
tmp = 0x02;
break;
- default:
return -EINVAL;
- }
- snd_soc_update_bits(codec, UPD9976_AUDIOPORT1,
BIT(2)|BIT(1)|BIT(0), tmp);
- switch (params_rate(params)) {
- case 8000:
tmp = 0x00;
break;
- case 11025:
tmp = 0x01;
break;
- case 12000:
tmp = 0x02;
break;
- case 16000:
tmp = 0x03;
break;
- case 22050:
tmp = 0x04;
break;
- case 24000:
tmp = 0x05;
break;
- case 32000:
tmp = 0x07;
break;
- case 44100:
tmp = 0x08;
break;
- case 48000:
tmp = 0x09;
break;
- default:
return -EINVAL;
- }
Looks fine, I'd rather use an array though.
+static int upd9976_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
+{
- switch (level) {
- case SND_SOC_BIAS_ON:
break;
- case SND_SOC_BIAS_PREPARE:
if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
snd_soc_update_bits(codec, UPD9976_VAUDIOCNT,
0x27, 0x27);
snd_soc_update_bits(codec, UPD9976_VREFPLL,
0x35, 0x35);
}
break;
- case SND_SOC_BIAS_STANDBY:
snd_soc_write(codec, UPD9976_VAUDIOCNT, 0x25);
snd_soc_write(codec, UPD9976_VREFPLL, 0x10);
break;
- case SND_SOC_BIAS_OFF:
snd_soc_write(codec, UPD9976_VREFPLL, 0);
snd_soc_write(codec, UPD9976_VAUDIOCNT, 0x24);
break;
- }
Why not snd_soc_update_bits()? These should normally be DAPM widgets.
+static int upd9976_codec_probe(struct snd_soc_codec *codec) +{
- upd9976_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
+}
Why SND_SOC_BIAS_OFF and not SND_SOC_BIAS_STANDBY?
+static int upd9976_codec_remove(struct snd_soc_codec *codec) +{
- return 0;
+}
You can call upd9976_set_bias_level(codec, SND_SOC_BIAS_OFF) here.
Thanks, Dimitris
On Wed, May 04, 2011 at 10:46:00PM +0800, Dimitris Papastamos wrote:
On Wed, May 04, 2011 at 09:44:58PM +0800, Lu Guanqun wrote:
- if (ret)
pr_err("upd9976 read of 0x%x failed, error: %d\n", reg, ret);
- return value;
+}
dev_err() would be more preferable.
OK.
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
mode |= BIT(7) | BIT(3);
break;
- }
- return snd_soc_update_bits(codec, UPD9976_AUDIOPORT1, mask, mode);
+}
I am not sure whether the BIT() macro is more confusing than helpful.
I was trying to be helpful. :) but if that confuses you.,I'm OK to use hex number.
- switch (params_rate(params)) {
- case 8000:
tmp = 0x00;
break;
- case 11025:
tmp = 0x01;
break;
- case 12000:
tmp = 0x02;
break;
- case 16000:
tmp = 0x03;
break;
- case 22050:
tmp = 0x04;
break;
- case 24000:
tmp = 0x05;
break;
- case 32000:
tmp = 0x07;
break;
- case 44100:
tmp = 0x08;
break;
- case 48000:
tmp = 0x09;
break;
- default:
return -EINVAL;
- }
Looks fine, I'd rather use an array though.
OK. Array makes it less code.
+static int upd9976_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
+{
- switch (level) {
- case SND_SOC_BIAS_ON:
break;
- case SND_SOC_BIAS_PREPARE:
if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
snd_soc_update_bits(codec, UPD9976_VAUDIOCNT,
0x27, 0x27);
snd_soc_update_bits(codec, UPD9976_VREFPLL,
0x35, 0x35);
}
break;
- case SND_SOC_BIAS_STANDBY:
snd_soc_write(codec, UPD9976_VAUDIOCNT, 0x25);
snd_soc_write(codec, UPD9976_VREFPLL, 0x10);
break;
- case SND_SOC_BIAS_OFF:
snd_soc_write(codec, UPD9976_VREFPLL, 0);
snd_soc_write(codec, UPD9976_VAUDIOCNT, 0x24);
break;
- }
Why not snd_soc_update_bits()? These should normally be DAPM widgets.
There's no DAPM widgets bound to these two registers. So I'm afraid it's OK to use snd_soc_write when it's been powered off totally.
+static int upd9976_codec_probe(struct snd_soc_codec *codec) +{
- upd9976_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
+}
Why SND_SOC_BIAS_OFF and not SND_SOC_BIAS_STANDBY?
Try to use as little power as possible.
+static int upd9976_codec_remove(struct snd_soc_codec *codec) +{
- return 0;
+}
You can call upd9976_set_bias_level(codec, SND_SOC_BIAS_OFF) here.
Thanks for reminding this.
On Wed, May 04, 2011 at 11:12:50PM +0800, Lu Guanqun wrote:
On Wed, May 04, 2011 at 10:46:00PM +0800, Dimitris Papastamos wrote:
On Wed, May 04, 2011 at 09:44:58PM +0800, Lu Guanqun wrote:
- case SND_SOC_BIAS_OFF:
snd_soc_write(codec, UPD9976_VREFPLL, 0);
snd_soc_write(codec, UPD9976_VAUDIOCNT, 0x24);
break;
- }
Why not snd_soc_update_bits()? These should normally be DAPM widgets.
There's no DAPM widgets bound to these two registers. So I'm afraid it's OK to use snd_soc_write when it's been powered off totally.
That's an orthogonal thing, the usual reason for pushing back on this stuff is that people have open coded a read/modify/write cycle which only updates a subset of bits. In this case I think the writes are fine as you're setting the full register to a specific value.
+static int upd9976_codec_probe(struct snd_soc_codec *codec) +{
- upd9976_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
+}
Why SND_SOC_BIAS_OFF and not SND_SOC_BIAS_STANDBY?
Try to use as little power as possible.
You should set idle_bias_off in the CODEC driver if you're doing this otherwise DAPM will just bring the CODEC up to _STANDBY at runtime.
On Wed, 2011-05-04 at 20:42 +0530, Lu, Guanqun wrote:
On Wed, May 04, 2011 at 10:46:00PM +0800, Dimitris Papastamos wrote:
On Wed, May 04, 2011 at 09:44:58PM +0800, Lu Guanqun wrote:
- if (ret)
pr_err("upd9976 read of 0x%x failed, error: %d\n", reg, ret);
- return value;
+}
dev_err() would be more preferable.
any specific reasons for that?
On Thu, May 05, 2011 at 08:42:57AM +0530, Koul, Vinod wrote:
On Wed, 2011-05-04 at 20:42 +0530, Lu, Guanqun wrote:
dev_err() would be more preferable.
any specific reasons for that?
It'll cause the logging to include the device name.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/mid-x86/sst_platform.c | 14 ++++++++++++++ 1 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c index 139db15..ce0ac3c 100644 --- a/sound/soc/mid-x86/sst_platform.c +++ b/sound/soc/mid-x86/sst_platform.c @@ -110,6 +110,20 @@ struct snd_soc_dai_driver sst_platform_dai[] = { .formats = SNDRV_PCM_FMTBIT_S24_LE, }, }, +{ + .name = "mrst-cpu-pcm2", + .id = 4, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = (SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_8000), + .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 | + SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 | + SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32), + }, +}, };
/* helper functions */
On Wed, 2011-05-04 at 19:15 +0530, Lu, Guanqun wrote:
Signed-off-by: Lu Guanqun guanqun.lu@intel.com
sound/soc/mid-x86/sst_platform.c | 14 ++++++++++++++ 1 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c index 139db15..ce0ac3c 100644 --- a/sound/soc/mid-x86/sst_platform.c +++ b/sound/soc/mid-x86/sst_platform.c @@ -110,6 +110,20 @@ struct snd_soc_dai_driver sst_platform_dai[] = { .formats = SNDRV_PCM_FMTBIT_S24_LE, }, }, +{
- .name = "mrst-cpu-pcm2",
- .id = 4,
- .playback = {
.channels_min = 1,
.channels_max = 2,
.rates = (SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_8000),
.formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 |
SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 |
SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32),
- },
+},
Nope, We cant keep on adding these DAIs for every new platform :( Even the current way is not best as it just creates 4 instance of DAI where we could have done with two (as they have same properties) I would use the current headset DAI and change the ops there, or create a new one for each type so that it can be reused.
On Thu, May 05, 2011 at 11:23:01AM +0800, Koul, Vinod wrote:
On Wed, 2011-05-04 at 19:15 +0530, Lu, Guanqun wrote:
Signed-off-by: Lu Guanqun guanqun.lu@intel.com
sound/soc/mid-x86/sst_platform.c | 14 ++++++++++++++ 1 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c index 139db15..ce0ac3c 100644 --- a/sound/soc/mid-x86/sst_platform.c +++ b/sound/soc/mid-x86/sst_platform.c @@ -110,6 +110,20 @@ struct snd_soc_dai_driver sst_platform_dai[] = { .formats = SNDRV_PCM_FMTBIT_S24_LE, }, }, +{
- .name = "mrst-cpu-pcm2",
- .id = 4,
- .playback = {
.channels_min = 1,
.channels_max = 2,
.rates = (SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_8000),
.formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 |
SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 |
SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32),
- },
+},
Nope, We cant keep on adding these DAIs for every new platform :( Even the current way is not best as it just creates 4 instance of DAI where we could have done with two (as they have same properties) I would use the current headset DAI and change the ops there, or create a new one for each type so that it can be reused.
So how about this one (not tested yet):
diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c index 7985cfe..78660e4 100644 --- a/sound/soc/mid-x86/sst_platform.c +++ b/sound/soc/mid-x86/sst_platform.c @@ -68,16 +68,21 @@ struct snd_soc_dai_driver sst_platform_dai[] = { .name = "Headset-cpu-dai", .id = 0, .playback = { - .channels_min = SST_STEREO, + .channels_min = SST_MONO, .channels_max = SST_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rates = (SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_8000), + .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 | + SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 | + SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32), }, .capture = { .channels_min = 1, .channels_max = 5, .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S24_LE, + .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 | + SNDRV_PCM_FMTBIT_S24_LE), }, }, {
On Thu, 2011-05-05 at 10:18 +0530, Lu, Guanqun wrote:
On Thu, May 05, 2011 at 11:23:01AM +0800, Koul, Vinod wrote:
On Wed, 2011-05-04 at 19:15 +0530, Lu, Guanqun wrote:
Signed-off-by: Lu Guanqun guanqun.lu@intel.com
sound/soc/mid-x86/sst_platform.c | 14 ++++++++++++++ 1 files changed, 14 insertions(+), 0 deletions(-)
Nope, We cant keep on adding these DAIs for every new platform :( Even the current way is not best as it just creates 4 instance of DAI where we could have done with two (as they have same properties) I would use the current headset DAI and change the ops there, or create a new one for each type so that it can be reused.
So how about this one (not tested yet):
diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c index 7985cfe..78660e4 100644 --- a/sound/soc/mid-x86/sst_platform.c +++ b/sound/soc/mid-x86/sst_platform.c @@ -68,16 +68,21 @@ struct snd_soc_dai_driver sst_platform_dai[] = { .name = "Headset-cpu-dai", .id = 0, .playback = {
.channels_min = SST_STEREO,
.channels_min = SST_MONO, .channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S24_LE,
.rates = (SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_8000),
.formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 |
SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 |
SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32), }, .capture = { .channels_min = 1, .channels_max = 5, .rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S24_LE,
.formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 |
SNDRV_PCM_FMTBIT_S24_LE),
You will have my Ack on this
On Thu, May 05, 2011 at 08:53:01AM +0530, Koul, Vinod wrote:
Nope, We cant keep on adding these DAIs for every new platform :( Even the current way is not best as it just creates 4 instance of DAI where we could have done with two (as they have same properties) I would use the current headset DAI and change the ops there, or create a new one for each type so that it can be reused.
What does this DAI actually represent? I'd assume from the description that it's another interface on the same CPU?
On Thu, 2011-05-05 at 13:35 +0530, Mark Brown wrote:
On Thu, May 05, 2011 at 08:53:01AM +0530, Koul, Vinod wrote:
Nope, We cant keep on adding these DAIs for every new platform :( Even the current way is not best as it just creates 4 instance of DAI where we could have done with two (as they have same properties) I would use the current headset DAI and change the ops there, or create a new one for each type so that it can be reused.
What does this DAI actually represent? I'd assume from the description that it's another interface on the same CPU?
The sst-platfrom driver sends the DAI ops to SST driver which sends to FW running on DSP. So it essentially represent the DSP side Note here DSP does DMA and sending data to PCM port.
On Thu, May 05, 2011 at 02:58:52PM +0530, Koul, Vinod wrote:
On Thu, 2011-05-05 at 13:35 +0530, Mark Brown wrote:
What does this DAI actually represent? I'd assume from the description that it's another interface on the same CPU?
The sst-platfrom driver sends the DAI ops to SST driver which sends to FW running on DSP. So it essentially represent the DSP side Note here DSP does DMA and sending data to PCM port.
Are there a fixed number of channels to the DSP or are these virtual?
On Thu, 2011-05-05 at 15:56 +0530, Mark Brown wrote:
On Thu, May 05, 2011 at 02:58:52PM +0530, Koul, Vinod wrote:
On Thu, 2011-05-05 at 13:35 +0530, Mark Brown wrote:
What does this DAI actually represent? I'd assume from the description that it's another interface on the same CPU?
The sst-platfrom driver sends the DAI ops to SST driver which sends to FW running on DSP. So it essentially represent the DSP side Note here DSP does DMA and sending data to PCM port.
Are there a fixed number of channels to the DSP or are these virtual?
Channels are fixed...
On Thu, May 05, 2011 at 03:30:59PM +0530, Koul, Vinod wrote:
On Thu, 2011-05-05 at 15:56 +0530, Mark Brown wrote:
Are there a fixed number of channels to the DSP or are these virtual?
Channels are fixed...
OK, DAIs make sense then - I'd been concerned that these weren't fixed and might need to be virtualised.
On Thu, 2011-05-05 at 19:16 +0530, Mark Brown wrote:
On Thu, May 05, 2011 at 03:30:59PM +0530, Koul, Vinod wrote:
On Thu, 2011-05-05 at 15:56 +0530, Mark Brown wrote:
Are there a fixed number of channels to the DSP or are these virtual?
Channels are fixed...
OK, DAIs make sense then - I'd been concerned that these weren't fixed and might need to be virtualised.
So how do we go about making them virtual? Thaat might be useful for some other things I had in mind...
On Thu, May 05, 2011 at 08:25:43PM +0530, Koul, Vinod wrote:
On Thu, 2011-05-05 at 19:16 +0530, Mark Brown wrote:
OK, DAIs make sense then - I'd been concerned that these weren't fixed and might need to be virtualised.
So how do we go about making them virtual? Thaat might be useful for some other things I had in mind...
You don't currently. The long term plan is to add support for this for DSPs and so on but it requires a bunch of framework work.
This machine driver glues upd9976 codec driver and sst_platform driver.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com Signed-off-by: Wang Xingchao xingchao.wang@intel.com --- sound/soc/mid-x86/Kconfig | 12 +++ sound/soc/mid-x86/Makefile | 2 sound/soc/mid-x86/mrst_machine.c | 163 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+), 0 deletions(-) create mode 100644 sound/soc/mid-x86/mrst_machine.c
diff --git a/sound/soc/mid-x86/Kconfig b/sound/soc/mid-x86/Kconfig index 2935042..f76acba 100644 --- a/sound/soc/mid-x86/Kconfig +++ b/sound/soc/mid-x86/Kconfig @@ -10,5 +10,17 @@ config SND_MFLD_MACHINE Say Y if you have such a device If unsure select "N".
+config SND_MRST_MACHINE + tristate "SoC Machine Audio Driver for Intel Moorestown Platform" + depends on INTEL_SCU_IPC + depends on SND_INTEL_SST + select SND_SOC_UPD9976 + select SND_SST_PLATFORM + help + This adds support for ASoC machine driver for Intel(R) MID Moorestown platform. + It is used as ALSA device in audio substream in Intel(R) MID devices. + Say Y if you have such a device. + If unsure, select 'N'. + config SND_SST_PLATFORM tristate diff --git a/sound/soc/mid-x86/Makefile b/sound/soc/mid-x86/Makefile index 6398833..d0f2c29 100644 --- a/sound/soc/mid-x86/Makefile +++ b/sound/soc/mid-x86/Makefile @@ -1,5 +1,7 @@ snd-soc-sst-platform-objs := sst_platform.o snd-soc-mfld-machine-objs := mfld_machine.o +snd-soc-mrst-machine-objs := mrst_machine.o
obj-$(CONFIG_SND_SST_PLATFORM) += snd-soc-sst-platform.o obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o +obj-$(CONFIG_SND_MRST_MACHINE) += snd-soc-mrst-machine.o diff --git a/sound/soc/mid-x86/mrst_machine.c b/sound/soc/mid-x86/mrst_machine.c new file mode 100644 index 0000000..11163e7 --- /dev/null +++ b/sound/soc/mid-x86/mrst_machine.c @@ -0,0 +1,163 @@ +/* + * mrst_machine.c - ASoC Machine driver for Intel Moorestown MID platform + * + * Copyright (C) 2011 Intel Corporation + * + * Maintainer: + * Lu Guanqun guanqun.lu@intel.com + * Wang Xingchao xingchao.wang@intel.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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/io.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +static const struct snd_soc_dapm_widget mrst_audio_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), +}; + +static const struct snd_soc_dapm_route mrst_audio_map[] = { + {"HPINL", NULL, "PREOUTL"}, + {"HPINR", NULL, "PREOUTR"}, + + {"Headphone", NULL, "HPOUTL"}, + {"Headphone", NULL, "HPOUTR"}, +}; + +static int mrst_audio_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + ret = snd_soc_dapm_new_controls(dapm, + mrst_audio_widgets, + ARRAY_SIZE(mrst_audio_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, + mrst_audio_map, + ARRAY_SIZE(mrst_audio_map)); + if (ret) + return ret; + + snd_soc_dapm_nc_pin(dapm, "LINEINL"); + snd_soc_dapm_nc_pin(dapm, "LINEINR"); + snd_soc_dapm_nc_pin(dapm, "LINEOUTL"); + snd_soc_dapm_nc_pin(dapm, "LINEOUTR"); + + snd_soc_dapm_enable_pin(dapm, "Headphone"); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +static int mrst_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM); + + return ret; +} + +static struct snd_soc_ops mrst_audio_ops = { + .hw_params = mrst_hw_params, +}; + +struct snd_soc_dai_link mrst_dailinks[] = { + { + .name = "Moorestown Audio", + .stream_name = "Audio", + .cpu_dai_name = "mrst-cpu-pcm2", + .codec_dai_name = "upd9976-audio", + .codec_name = "upd9976", + .platform_name = "sst-platform", + .init = mrst_audio_init, + .ops = &mrst_audio_ops, + }, +}; + +static struct snd_soc_card snd_soc_card_mrst = { + .name = "moorestown_audio", + .dai_link = mrst_dailinks, + .num_links = ARRAY_SIZE(mrst_dailinks), +}; + +static int __devinit snd_mrst_audio_probe(struct platform_device *pdev) +{ + int ret; + + snd_soc_card_mrst.dev = &pdev->dev; + ret = snd_soc_register_card(&snd_soc_card_mrst); + if (ret) + goto fail_register_card; + + return 0; + +fail_register_card: + return ret; +} + +static int __devexit snd_mrst_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&snd_soc_card_mrst); + return 0; +} + +static struct platform_driver snd_mrst_audio_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "pmic_audio", + }, + .probe = snd_mrst_audio_probe, + .remove = __devexit_p(snd_mrst_audio_remove), +}; + +static int __init snd_mrst_driver_init(void) +{ + return platform_driver_register(&snd_mrst_audio_driver); +} +module_init(snd_mrst_driver_init); + +static void __exit snd_mrst_driver_exit(void) +{ + platform_driver_unregister(&snd_mrst_audio_driver); +} +module_exit(snd_mrst_driver_exit); + +MODULE_DESCRIPTION("ASoC Intel(R) MID Moorestown Machine Driver"); +MODULE_AUTHOR("Lu Guanqun guanqun.lu@intel.com"); +MODULE_AUTHOR("Wang Xingchao xingchao.wang@intel.com"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mrst-audio");
On Wed, May 04, 2011 at 09:45:09PM +0800, Lu Guanqun wrote:
This machine driver glues upd9976 codec driver and sst_platform driver.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com Signed-off-by: Wang Xingchao xingchao.wang@intel.com
+static int mrst_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
+{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
- ret = snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM);
- return ret;
+}
Should we not be letting know the CPU that the CODEC is master? Or is it by default slave?
Thanks, Dimitris
On Wed, May 04, 2011 at 10:55:17PM +0800, Dimitris Papastamos wrote:
On Wed, May 04, 2011 at 09:45:09PM +0800, Lu Guanqun wrote:
This machine driver glues upd9976 codec driver and sst_platform driver.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com Signed-off-by: Wang Xingchao xingchao.wang@intel.com
+static int mrst_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
+{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
- ret = snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM);
- return ret;
+}
Should we not be letting know the CPU that the CODEC is master? Or is it by default slave?
by default, the corresponding bit is 0 and it's slave mode. we should change it to master somewhere. no sure about your first question...
Thanks, Dimitris _______________________________________________ Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
This internal speaker needs an extra GPIO to control its power.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/mid-x86/mrst_machine.c | 31 +++++++++++++++++++++++++++++++ 1 files changed, 31 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mid-x86/mrst_machine.c b/sound/soc/mid-x86/mrst_machine.c index 11163e7..0bce388 100644 --- a/sound/soc/mid-x86/mrst_machine.c +++ b/sound/soc/mid-x86/mrst_machine.c @@ -36,8 +36,24 @@ #include <sound/pcm_params.h> #include <sound/soc.h>
+/* + * GPIO pin to power on/off Internal Speaker + */ +static int mrst_gpio_amp; + +static int mrst_speaker_event(struct snd_soc_dapm_widget *widget, + struct snd_kcontrol *control, int event) +{ + if (mrst_gpio_amp) + gpio_set_value_cansleep(mrst_gpio_amp, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + static const struct snd_soc_dapm_widget mrst_audio_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_SPK("Speaker", mrst_speaker_event), };
static const struct snd_soc_dapm_route mrst_audio_map[] = { @@ -46,6 +62,9 @@ static const struct snd_soc_dapm_route mrst_audio_map[] = {
{"Headphone", NULL, "HPOUTL"}, {"Headphone", NULL, "HPOUTR"}, + + {"Speaker", NULL, "PREOUTL"}, + {"Speaker", NULL, "PREOUTR"}, };
static int mrst_audio_init(struct snd_soc_pcm_runtime *runtime) @@ -72,6 +91,7 @@ static int mrst_audio_init(struct snd_soc_pcm_runtime *runtime) snd_soc_dapm_nc_pin(dapm, "LINEOUTR");
snd_soc_dapm_enable_pin(dapm, "Headphone"); + snd_soc_dapm_disable_pin(dapm, "Speaker");
snd_soc_dapm_sync(dapm);
@@ -118,6 +138,13 @@ static int __devinit snd_mrst_audio_probe(struct platform_device *pdev) { int ret;
+ if (pdev->dev.platform_data) { + mrst_gpio_amp = *(int *)pdev->dev.platform_data; + if (gpio_request_one(mrst_gpio_amp, + GPIOF_OUT_INIT_LOW, "amp power")) + mrst_gpio_amp = 0; + } + snd_soc_card_mrst.dev = &pdev->dev; ret = snd_soc_register_card(&snd_soc_card_mrst); if (ret) @@ -126,11 +153,15 @@ static int __devinit snd_mrst_audio_probe(struct platform_device *pdev) return 0;
fail_register_card: + if (mrst_gpio_amp) + gpio_free(mrst_gpio_amp); return ret; }
static int __devexit snd_mrst_audio_remove(struct platform_device *pdev) { + if (mrst_gpio_amp) + gpio_free(mrst_gpio_amp); snd_soc_unregister_card(&snd_soc_card_mrst); return 0; }
On Wed, May 04, 2011 at 09:45:14PM +0800, Lu Guanqun wrote:
@@ -72,6 +91,7 @@ static int mrst_audio_init(struct snd_soc_pcm_runtime *runtime) snd_soc_dapm_nc_pin(dapm, "LINEOUTR");
snd_soc_dapm_enable_pin(dapm, "Headphone");
snd_soc_dapm_disable_pin(dapm, "Speaker");
snd_soc_dapm_sync(dapm);
There's nothing in this patch that ever enables the speaker...
All these two output device can be enabled and disabled independently.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/mid-x86/mrst_machine.c | 15 +++++++++++++-- 1 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/sound/soc/mid-x86/mrst_machine.c b/sound/soc/mid-x86/mrst_machine.c index 0bce388..b4d542e 100644 --- a/sound/soc/mid-x86/mrst_machine.c +++ b/sound/soc/mid-x86/mrst_machine.c @@ -36,6 +36,11 @@ #include <sound/pcm_params.h> #include <sound/soc.h>
+static const struct snd_kcontrol_new mrst_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + /* * GPIO pin to power on/off Internal Speaker */ @@ -85,13 +90,19 @@ static int mrst_audio_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret;
+ ret = snd_soc_add_controls(codec, + mrst_snd_controls, + ARRAY_SIZE(mrst_snd_controls)); + if (ret) + return ret; + snd_soc_dapm_nc_pin(dapm, "LINEINL"); snd_soc_dapm_nc_pin(dapm, "LINEINR"); snd_soc_dapm_nc_pin(dapm, "LINEOUTL"); snd_soc_dapm_nc_pin(dapm, "LINEOUTR");
- snd_soc_dapm_enable_pin(dapm, "Headphone"); - snd_soc_dapm_disable_pin(dapm, "Speaker"); + snd_soc_dapm_disable_pin(dapm, "Headphone"); + snd_soc_dapm_enable_pin(dapm, "Speaker");
snd_soc_dapm_sync(dapm);
On Wed, May 04, 2011 at 09:45:19PM +0800, Lu Guanqun wrote:
- ret = snd_soc_add_controls(codec,
mrst_snd_controls,
ARRAY_SIZE(mrst_snd_controls));
- if (ret)
return ret;
It'd be better to use the data based init stuff to add the controls.
On Thu, May 05, 2011 at 12:22:32AM +0800, Mark Brown wrote:
On Wed, May 04, 2011 at 09:45:19PM +0800, Lu Guanqun wrote:
- ret = snd_soc_add_controls(codec,
mrst_snd_controls,
ARRAY_SIZE(mrst_snd_controls));
- if (ret)
return ret;
It'd be better to use the data based init stuff to add the controls.
OK. Will change that.
From: Wang Xingchao xingchao.wang@intel.com
Signed-off-by: Wang Xingchao xingchao.wang@intel.com --- sound/soc/codecs/upd9976.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/upd9976.c b/sound/soc/codecs/upd9976.c index cd68633..dc7920c 100644 --- a/sound/soc/codecs/upd9976.c +++ b/sound/soc/codecs/upd9976.c @@ -305,6 +305,13 @@ static struct snd_soc_dai_driver upd9976_dais[] = { SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 | SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32), }, + .capture = { + .stream_name = "Audio Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_48000, + .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16), + }, .ops = &upd9976_audio_dai_ops, }, };
On Wed, May 04, 2011 at 09:45:25PM +0800, Lu Guanqun wrote:
From: Wang Xingchao xingchao.wang@intel.com
Signed-off-by: Wang Xingchao xingchao.wang@intel.com
Better to just squash this into the original patch, no point in splitting it out.
From: Wang Xingchao xingchao.wang@intel.com
Signed-off-by: Wang Xingchao xingchao.wang@intel.com --- sound/soc/mid-x86/sst_platform.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c index ce0ac3c..7985cfe 100644 --- a/sound/soc/mid-x86/sst_platform.c +++ b/sound/soc/mid-x86/sst_platform.c @@ -123,6 +123,12 @@ struct snd_soc_dai_driver sst_platform_dai[] = { SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 | SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32), }, + .capture = { + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_48000, + .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16), + }, }, };
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/codecs/upd9976.c | 67 ++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 67 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/upd9976.c b/sound/soc/codecs/upd9976.c index dc7920c..fb3a4fc 100644 --- a/sound/soc/codecs/upd9976.c +++ b/sound/soc/codecs/upd9976.c @@ -81,6 +81,29 @@ static const struct snd_kcontrol_new upd9976_hp_spkr_mixer_right_controls[] = { SOC_DAPM_SINGLE("Audio DAC Right", UPD9976_HPRMIXSEL, 3, 1, 1), };
+/* PCM2 Left Mux */ +static const char *upd9976_pcm2_left_mux_texts[] = {"AADC Left", "DMIC"}; +static const struct soc_enum upd9976_pcm2_left_mux_enum = + SOC_ENUM_SINGLE(UPD9976_ADCSAMPLERATE, 5, 2, + upd9976_pcm2_left_mux_texts); +static const struct snd_kcontrol_new upd9976_pcm2_left_mux_control = + SOC_DAPM_ENUM("PCM2 Left Mux", upd9976_pcm2_left_mux_enum); + +/* PCM2 Right Mux */ +static const char *upd9976_pcm2_right_mux_texts[] = {"AADC Right", "DMIC"}; +static const struct soc_enum upd9976_pcm2_right_mux_enum = + SOC_ENUM_SINGLE(UPD9976_ADCSAMPLERATE, 4, 2, + upd9976_pcm2_right_mux_texts); +static const struct snd_kcontrol_new upd9976_pcm2_right_mux_control = + SOC_DAPM_ENUM("PCM2 Right Mux", upd9976_pcm2_right_mux_enum); + +/* PCM2 Mux */ +static const char *upd9976_pcm2_mux_texts[] = {"No Mix", "Mix"}; +static const struct soc_enum upd9976_pcm2_mux_enum = + SOC_ENUM_SINGLE(UPD9976_ADCSAMPLERATE, 3, 2, upd9976_pcm2_mux_texts); +static const struct snd_kcontrol_new upd9976_pcm2_mux_control = + SOC_DAPM_ENUM("PCM2 Mux", upd9976_pcm2_mux_enum); + static const struct snd_soc_dapm_widget upd9976_dapm_widgets[] = { /* Input */ SND_SOC_DAPM_INPUT("LINEINL"), @@ -117,12 +140,34 @@ static const struct snd_soc_dapm_widget upd9976_dapm_widgets[] = { upd9976_hp_spkr_mixer_right_controls, ARRAY_SIZE(upd9976_hp_spkr_mixer_right_controls)),
+ SND_SOC_DAPM_MIXER("PCM2 IN Mix", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("PCM2 IN No Mix", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Mux */ + SND_SOC_DAPM_MUX("PCM2 Left", SND_SOC_NOPM, 0, 0, + &upd9976_pcm2_left_mux_control), + SND_SOC_DAPM_MUX("PCM2 Right", SND_SOC_NOPM, 0, 0, + &upd9976_pcm2_right_mux_control), + SND_SOC_DAPM_MUX("PCM2 Mux", SND_SOC_NOPM, 0, 0, + &upd9976_pcm2_mux_control), + /* PGA */ SND_SOC_DAPM_PGA("HP Playback Left", UPD9976_DRVPOWERCTRL, 2, 0, NULL, 0), SND_SOC_DAPM_PGA("HP Playback Right", UPD9976_DRVPOWERCTRL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AADC Left", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("AADC Right", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA("DMIC Gain", UPD9976_DMICCTRL1, 7, 0, NULL, 0), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_OUT("PCM2 Out", "Audio Capture", 0, SND_SOC_NOPM, 0, + 0), + + /* Supply */ + SND_SOC_DAPM_SUPPLY("DMIC Supply", UPD9976_POWERCTRL2, 1, 0, NULL, 0), };
static const struct snd_soc_dapm_route upd9976_dapm_routes[] = { @@ -141,6 +186,28 @@ static const struct snd_soc_dapm_route upd9976_dapm_routes[] = { {"HPOUTL", NULL, "HP Playback Left"}, {"HPOUTR", NULL, "HP Playback Right"},
+ {"DMICDAT", NULL, "DMIC Supply"}, + {"DMIC Gain", NULL, "DMICDAT"}, + + {"AADC Left", NULL, "AADC"}, + {"AADC Right", NULL, "AADC"}, + + {"PCM2 Left", "DMIC", "DMIC Gain"}, + {"PCM2 Left", "AADC Left", "AADC Left"}, + + {"PCM2 Right", "DMIC", "DMIC Gain"}, + {"PCM2 Right", "AADC Right", "AADC Right"}, + + {"PCM2 IN Mix", NULL, "PCM2 Left"}, + {"PCM2 IN No Mix", NULL, "PCM2 Left"}, + + {"PCM2 IN No Mix", NULL, "PCM2 Right"}, + {"PCM2 IN Mix", NULL, "PCM2 Right"}, + + {"PCM2 Mux", "No Mix", "PCM2 IN No Mix"}, + {"PCM2 Mux", "Mix", "PCM2 IN Mix"}, + + {"PCM2 Out", NULL, "PCM2 Mux"}, };
static int upd9976_audio_digital_mute(struct snd_soc_dai *dai, int mute)
On Wed, May 04, 2011 at 09:45:34PM +0800, Lu Guanqun wrote:
Signed-off-by: Lu Guanqun guanqun.lu@intel.com
- SND_SOC_DAPM_MIXER("PCM2 IN Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("PCM2 IN No Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
Mixer instead of Mix?
- /* Mux */
- SND_SOC_DAPM_MUX("PCM2 Left", SND_SOC_NOPM, 0, 0,
&upd9976_pcm2_left_mux_control),
- SND_SOC_DAPM_MUX("PCM2 Right", SND_SOC_NOPM, 0, 0,
&upd9976_pcm2_right_mux_control),
Append Mux.
/* PGA */ SND_SOC_DAPM_PGA("HP Playback Left", UPD9976_DRVPOWERCTRL, 2, 0, NULL, 0), SND_SOC_DAPM_PGA("HP Playback Right", UPD9976_DRVPOWERCTRL, 1, 0, NULL, 0),
- SND_SOC_DAPM_PGA("AADC Left", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("AADC Right", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("DMIC Gain", UPD9976_DMICCTRL1, 7, 0, NULL, 0),
Append PGA.
Thanks, Dimitris
On Wed, May 04, 2011 at 11:03:39PM +0800, Dimitris Papastamos wrote:
On Wed, May 04, 2011 at 09:45:34PM +0800, Lu Guanqun wrote:
Signed-off-by: Lu Guanqun guanqun.lu@intel.com
- SND_SOC_DAPM_MIXER("PCM2 IN Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("PCM2 IN No Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
Mixer instead of Mix?
- /* Mux */
- SND_SOC_DAPM_MUX("PCM2 Left", SND_SOC_NOPM, 0, 0,
&upd9976_pcm2_left_mux_control),
- SND_SOC_DAPM_MUX("PCM2 Right", SND_SOC_NOPM, 0, 0,
&upd9976_pcm2_right_mux_control),
Append Mux.
/* PGA */ SND_SOC_DAPM_PGA("HP Playback Left", UPD9976_DRVPOWERCTRL, 2, 0, NULL, 0), SND_SOC_DAPM_PGA("HP Playback Right", UPD9976_DRVPOWERCTRL, 1, 0, NULL, 0),
- SND_SOC_DAPM_PGA("AADC Left", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("AADC Right", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("DMIC Gain", UPD9976_DMICCTRL1, 7, 0, NULL, 0),
Append PGA.
Seems I'm not clear on the naming :) Thanks for correction. I'll change these names.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/codecs/upd9976.c | 19 +++++++++++++++++++ 1 files changed, 19 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/upd9976.c b/sound/soc/codecs/upd9976.c index fb3a4fc..8b2fff7 100644 --- a/sound/soc/codecs/upd9976.c +++ b/sound/soc/codecs/upd9976.c @@ -81,6 +81,13 @@ static const struct snd_kcontrol_new upd9976_hp_spkr_mixer_right_controls[] = { SOC_DAPM_SINGLE("Audio DAC Right", UPD9976_HPRMIXSEL, 3, 1, 1), };
+/* Analog Right Mux */ +static const char *upd9976_analog_mux_texts[] = {"MIC", "LineIn"}; +static const struct soc_enum upd9976_ar_mux_enum = + SOC_ENUM_SINGLE(UPD9976_LIRSEL, 7, 2, upd9976_analog_mux_texts); +static const struct snd_kcontrol_new upd9976_ar_mux_control = + SOC_DAPM_ENUM("Route", upd9976_ar_mux_enum); + /* PCM2 Left Mux */ static const char *upd9976_pcm2_left_mux_texts[] = {"AADC Left", "DMIC"}; static const struct soc_enum upd9976_pcm2_left_mux_enum = @@ -144,6 +151,9 @@ static const struct snd_soc_dapm_widget upd9976_dapm_widgets[] = { SND_SOC_DAPM_MIXER("PCM2 IN No Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
/* Mux */ + SND_SOC_DAPM_MUX("Analog Right", UPD9976_POWERCTRL1, 4, 0, + &upd9976_ar_mux_control), + SND_SOC_DAPM_MUX("PCM2 Left", SND_SOC_NOPM, 0, 0, &upd9976_pcm2_left_mux_control), SND_SOC_DAPM_MUX("PCM2 Right", SND_SOC_NOPM, 0, 0, @@ -162,6 +172,8 @@ static const struct snd_soc_dapm_widget upd9976_dapm_widgets[] = {
SND_SOC_DAPM_PGA("DMIC Gain", UPD9976_DMICCTRL1, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIC2 In", UPD9976_POWERCTRL1, 2, 0, NULL, 0), + /* Audio Interface */ SND_SOC_DAPM_AIF_OUT("PCM2 Out", "Audio Capture", 0, SND_SOC_NOPM, 0, 0), @@ -186,6 +198,13 @@ static const struct snd_soc_dapm_route upd9976_dapm_routes[] = { {"HPOUTL", NULL, "HP Playback Left"}, {"HPOUTR", NULL, "HP Playback Right"},
+ {"MIC2 In", NULL, "MIC2"}, + + {"Analog Right", "MIC", "MIC2 In"}, + {"Analog Right", "LineIn", "LINEINR"}, + + {"AADC", NULL, "Analog Right"}, + {"DMICDAT", NULL, "DMIC Supply"}, {"DMIC Gain", NULL, "DMICDAT"},
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/codecs/upd9976.c | 23 +++++++++++++++++++++++ 1 files changed, 23 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/upd9976.c b/sound/soc/codecs/upd9976.c index 8b2fff7..69d83ea 100644 --- a/sound/soc/codecs/upd9976.c +++ b/sound/soc/codecs/upd9976.c @@ -60,6 +60,23 @@ static inline int upd9976_write(struct snd_soc_codec *codec, return ret; }
+static int upd9976_mic2_bias_event(struct snd_soc_dapm_widget *widget, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = widget->codec; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, UPD9976_AUXDBNC, 0xff); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_write(codec, UPD9976_AUXDBNC, 0x0); + break; + } + + return 0; +} + /* * Mixing Volume: from -25 dB to 6 dB in 1 dB steps. */ @@ -161,6 +178,12 @@ static const struct snd_soc_dapm_widget upd9976_dapm_widgets[] = { SND_SOC_DAPM_MUX("PCM2 Mux", SND_SOC_NOPM, 0, 0, &upd9976_pcm2_mux_control),
+ /* Microphose Bias */ + SND_SOC_DAPM_MICBIAS("MIC1 Bias", UPD9976_MICCTRL, 6, 0), + SND_SOC_DAPM_MICBIAS_E("MIC2 Bias", UPD9976_MICCTRL, 4, 0, + upd9976_mic2_bias_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + /* PGA */ SND_SOC_DAPM_PGA("HP Playback Left", UPD9976_DRVPOWERCTRL, 2, 0, NULL, 0),
On Wed, May 04, 2011 at 09:45:44PM +0800, Lu Guanqun wrote:
Signed-off-by: Lu Guanqun guanqun.lu@intel.com
In general for most of these things just squash them into the original driver for submission, no point in splitting them out if you're not actually doing initial submission.
On Thu, May 05, 2011 at 12:25:34AM +0800, Mark Brown wrote:
On Wed, May 04, 2011 at 09:45:44PM +0800, Lu Guanqun wrote:
Signed-off-by: Lu Guanqun guanqun.lu@intel.com
In general for most of these things just squash them into the original driver for submission, no point in splitting them out if you're not actually doing initial submission.
OK. I was trying to split things into smaller patches so that you maintainers have a easier time to review them.
So in the next version, there will be less number of patches. :)
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/codecs/upd9976.c | 28 ++++++++++++++++++++++++++++ sound/soc/codecs/upd9976.h | 2 ++ 2 files changed, 30 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/upd9976.c b/sound/soc/codecs/upd9976.c index 69d83ea..9231cee 100644 --- a/sound/soc/codecs/upd9976.c +++ b/sound/soc/codecs/upd9976.c @@ -60,6 +60,34 @@ static inline int upd9976_write(struct snd_soc_codec *codec, return ret; }
+void upd9976_jack_detection(struct snd_soc_jack *jack, u8 interrupt_status) +{ + int status = 0; + unsigned int value = 0; + struct snd_soc_codec *codec = jack->codec; + int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET; + + if (interrupt_status & 0x3) + value = snd_soc_read(codec, UPD9976_SAUXINT); + + if (interrupt_status & 0x1 && value == 0x1) + status |= SND_JACK_HEADSET; + + if (interrupt_status & 0x2 && value == 0x2) + status |= SND_JACK_HEADPHONE; + + if (interrupt_status & 0x4) + status |= SND_JACK_HEADSET | SND_JACK_BTN_0; + + if (interrupt_status & 0x8) + status |= SND_JACK_HEADSET | SND_JACK_BTN_1; + + snd_soc_jack_report(jack, status, mask); + if (status & (SND_JACK_BTN_0 | SND_JACK_BTN_1)) + snd_soc_jack_report(jack, SND_JACK_HEADSET, mask); +} +EXPORT_SYMBOL_GPL(upd9976_jack_detection); + static int upd9976_mic2_bias_event(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kcontrol, int event) { diff --git a/sound/soc/codecs/upd9976.h b/sound/soc/codecs/upd9976.h index ab2ea15..090dd0b 100644 --- a/sound/soc/codecs/upd9976.h +++ b/sound/soc/codecs/upd9976.h @@ -80,4 +80,6 @@
#define UPD9976_SAUXINT 0x132
+void upd9976_jack_detection(struct snd_soc_jack *jack, u8 interrupt_status); + #endif
On Wed, May 04, 2011 at 09:45:50PM +0800, Lu Guanqun wrote:
+void upd9976_jack_detection(struct snd_soc_jack *jack, u8 interrupt_status) +{
- int status = 0;
- unsigned int value = 0;
- struct snd_soc_codec *codec = jack->codec;
- int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET;
- if (interrupt_status & 0x3)
value = snd_soc_read(codec, UPD9976_SAUXINT);
+EXPORT_SYMBOL_GPL(upd9976_jack_detection);
I'd expect that the driver would also manage the work to do with handling the interrupt as well.
On Thu, May 05, 2011 at 12:32:31AM +0800, Mark Brown wrote:
On Wed, May 04, 2011 at 09:45:50PM +0800, Lu Guanqun wrote:
+void upd9976_jack_detection(struct snd_soc_jack *jack, u8 interrupt_status) +{
- int status = 0;
- unsigned int value = 0;
- struct snd_soc_codec *codec = jack->codec;
- int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET;
- if (interrupt_status & 0x3)
value = snd_soc_read(codec, UPD9976_SAUXINT);
+EXPORT_SYMBOL_GPL(upd9976_jack_detection);
I'd expect that the driver would also manage the work to do with handling the interrupt as well.
Yes, I see some wm* codec driver handles the interrupt directly in CODEC. I'm not sure whether it's better handled in codec driver or machine driver. Now it's confirmed by you, I'll change it correspondingly.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/mid-x86/mrst_machine.c | 12 ++++++++++++ 1 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mid-x86/mrst_machine.c b/sound/soc/mid-x86/mrst_machine.c index b4d542e..d70b26e 100644 --- a/sound/soc/mid-x86/mrst_machine.c +++ b/sound/soc/mid-x86/mrst_machine.c @@ -39,6 +39,9 @@ static const struct snd_kcontrol_new mrst_snd_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Speaker"), + + SOC_DAPM_PIN_SWITCH("Headset MIC"), + SOC_DAPM_PIN_SWITCH("Internal MIC"), };
/* @@ -59,6 +62,9 @@ static int mrst_speaker_event(struct snd_soc_dapm_widget *widget, static const struct snd_soc_dapm_widget mrst_audio_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_SPK("Speaker", mrst_speaker_event), + + SND_SOC_DAPM_MIC("Headset MIC", NULL), + SND_SOC_DAPM_MIC("Internal MIC", NULL), };
static const struct snd_soc_dapm_route mrst_audio_map[] = { @@ -70,6 +76,9 @@ static const struct snd_soc_dapm_route mrst_audio_map[] = {
{"Speaker", NULL, "PREOUTL"}, {"Speaker", NULL, "PREOUTR"}, + + {"MIC2", NULL, "Headset MIC"}, + {"DMICDAT", NULL, "Internal MIC"}, };
static int mrst_audio_init(struct snd_soc_pcm_runtime *runtime) @@ -104,6 +113,9 @@ static int mrst_audio_init(struct snd_soc_pcm_runtime *runtime) snd_soc_dapm_disable_pin(dapm, "Headphone"); snd_soc_dapm_enable_pin(dapm, "Speaker");
+ snd_soc_dapm_disable_pin(dapm, "Headset MIC"); + snd_soc_dapm_enable_pin(dapm, "Internal MIC"); + snd_soc_dapm_sync(dapm);
return 0;
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/mid-x86/mrst_machine.c | 142 ++++++++++++++++++++++++++++++++++++++ 1 files changed, 142 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mid-x86/mrst_machine.c b/sound/soc/mid-x86/mrst_machine.c index d70b26e..96df9a1 100644 --- a/sound/soc/mid-x86/mrst_machine.c +++ b/sound/soc/mid-x86/mrst_machine.c @@ -31,10 +31,83 @@ #include <linux/device.h> #include <linux/slab.h> #include <linux/gpio.h> +#include <linux/list.h> #include <linux/io.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/jack.h> +#include <../codecs/upd9976.h> + +/* Jack Detection */ +static struct snd_soc_jack mrst_jack; +struct mrst_jack_context { + struct list_head head; + spinlock_t lock; + void __iomem *interrupt_base; +}; + +struct mrst_jack_node { + struct list_head list; + u8 interrupt_status; +}; + +static struct snd_soc_jack_pin mrst_jack_pins = { + .pin = "Headphone", + .mask = SND_JACK_HEADSET, +}; + +static irqreturn_t mrst_jack_intr_handler(int irq, void *dev) +{ + struct mrst_jack_context *context = dev; + u8 interrupt_status; + struct mrst_jack_node *node; + unsigned long flags; + + memcpy_fromio(&interrupt_status, + context->interrupt_base, + sizeof(u8)); + + /* check if it's valid */ + if (!(interrupt_status & 0xf)) + return IRQ_NONE; + + node = kmalloc(sizeof(*node), GFP_ATOMIC); + if (!node) + return IRQ_NONE; + + INIT_LIST_HEAD(&node->list); + node->interrupt_status = interrupt_status; + + spin_lock_irqsave(&context->lock, flags); + list_add_tail(&node->list, &context->head); + spin_unlock_irqrestore(&context->lock, flags); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t mrst_jack_detection(int irq, void *data) +{ + struct mrst_jack_context *context = data; + u8 interrupt_status; + unsigned long flags; + struct mrst_jack_node *node; + + spin_lock_irqsave(&context->lock, flags); + if (list_empty(&context->head)) { + spin_unlock_irqrestore(&context->lock, flags); + return IRQ_HANDLED; + } + node = list_first_entry(&context->head, struct mrst_jack_node, list); + interrupt_status = node->interrupt_status; + list_del(&node->list); + kfree(node); + spin_unlock_irqrestore(&context->lock, flags); + + upd9976_jack_detection(&mrst_jack, interrupt_status); + + return IRQ_HANDLED; +}
static const struct snd_kcontrol_new mrst_snd_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), @@ -116,8 +189,22 @@ static int mrst_audio_init(struct snd_soc_pcm_runtime *runtime) snd_soc_dapm_disable_pin(dapm, "Headset MIC"); snd_soc_dapm_enable_pin(dapm, "Internal MIC");
+ snd_soc_dapm_force_enable_pin(dapm, "MIC2 Bias"); + snd_soc_dapm_sync(dapm);
+ ret = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack", + (SND_JACK_HEADSET | + SND_JACK_BTN_0 | + SND_JACK_BTN_1), + &mrst_jack); + if (ret) + return ret; + + ret = snd_soc_jack_add_pins(&mrst_jack, 1, &mrst_jack_pins); + if (ret) + return ret; + return 0; }
@@ -159,6 +246,9 @@ static struct snd_soc_card snd_soc_card_mrst = {
static int __devinit snd_mrst_audio_probe(struct platform_device *pdev) { + int irq; + struct resource *irq_mem; + struct mrst_jack_context *jack_context = NULL; int ret;
if (pdev->dev.platform_data) { @@ -168,14 +258,59 @@ static int __devinit snd_mrst_audio_probe(struct platform_device *pdev) mrst_gpio_amp = 0; }
+ irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto fail_get_irq; + } + + jack_context = kzalloc(sizeof(*jack_context), GFP_KERNEL); + if (!jack_context) { + ret = -ENOMEM; + goto fail_get_irq; + } + spin_lock_init(&jack_context->lock); + INIT_LIST_HEAD(&jack_context->head); + + irq_mem = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "IRQ_BASE"); + if (!irq_mem) { + ret = -ENODEV; + goto fail_alloc_context; + } + + jack_context->interrupt_base = ioremap_nocache(irq_mem->start, + resource_size(irq_mem)); + if (!jack_context->interrupt_base) { + ret = -ENOMEM; + goto fail_alloc_context; + } + + ret = request_threaded_irq(irq, + mrst_jack_intr_handler, + mrst_jack_detection, + IRQF_SHARED, + pdev->dev.driver->name, + jack_context); + if (ret) + goto fail_request_irq; + snd_soc_card_mrst.dev = &pdev->dev; ret = snd_soc_register_card(&snd_soc_card_mrst); if (ret) goto fail_register_card;
+ platform_set_drvdata(pdev, jack_context); + return 0;
fail_register_card: + free_irq(irq, jack_context); +fail_request_irq: + iounmap(jack_context->interrupt_base); +fail_alloc_context: + kfree(jack_context); +fail_get_irq: if (mrst_gpio_amp) gpio_free(mrst_gpio_amp); return ret; @@ -183,9 +318,16 @@ fail_register_card:
static int __devexit snd_mrst_audio_remove(struct platform_device *pdev) { + struct mrst_jack_context *jack_context = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + free_irq(irq, jack_context); + iounmap(jack_context->interrupt_base); + kfree(jack_context); if (mrst_gpio_amp) gpio_free(mrst_gpio_amp); snd_soc_unregister_card(&snd_soc_card_mrst); + return 0; }
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/codecs/upd9976.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/upd9976.c b/sound/soc/codecs/upd9976.c index 9231cee..a8f9e4a 100644 --- a/sound/soc/codecs/upd9976.c +++ b/sound/soc/codecs/upd9976.c @@ -114,6 +114,7 @@ static const struct snd_kcontrol_new upd9976_snd_controls[] = { SOC_DOUBLE_R_TLV("Headphone & Speaker Volume", UPD9976_HPSPRLVOL, UPD9976_HPSPRRVOL, 0, 0x1f, 1, mixer_tlv), + SOC_SINGLE("Internal Mic Switch", UPD9976_DMICCTRL1, 6, 1, 0), };
static const struct snd_kcontrol_new upd9976_hp_spkr_mixer_left_controls[] = {
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/codecs/upd9976.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/upd9976.c b/sound/soc/codecs/upd9976.c index a8f9e4a..f345db1 100644 --- a/sound/soc/codecs/upd9976.c +++ b/sound/soc/codecs/upd9976.c @@ -115,6 +115,8 @@ static const struct snd_kcontrol_new upd9976_snd_controls[] = { UPD9976_HPSPRLVOL, UPD9976_HPSPRRVOL, 0, 0x1f, 1, mixer_tlv), SOC_SINGLE("Internal Mic Switch", UPD9976_DMICCTRL1, 6, 1, 0), + SOC_DOUBLE_R("Analog Mic Switch", UPD9976_LILSEL, UPD9976_LIRSEL, + 6, 1, 0), };
static const struct snd_kcontrol_new upd9976_hp_spkr_mixer_left_controls[] = {
On Moorestown, DMIC's output is set to PCM2 mono.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/mid-x86/mrst_machine.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mid-x86/mrst_machine.c b/sound/soc/mid-x86/mrst_machine.c index 96df9a1..5f19df5 100644 --- a/sound/soc/mid-x86/mrst_machine.c +++ b/sound/soc/mid-x86/mrst_machine.c @@ -160,6 +160,9 @@ static int mrst_audio_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_dapm_context *dapm = &codec->dapm; int ret;
+ /* DMIC configuration: mono output to PCM2 */ + snd_soc_update_bits(codec, UPD9976_MICCTRL, BIT(1)|BIT(0), 0x1); + ret = snd_soc_dapm_new_controls(dapm, mrst_audio_widgets, ARRAY_SIZE(mrst_audio_widgets));
The Moorestown uses MIC2 pseudo-differential configuration.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/mid-x86/mrst_machine.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mid-x86/mrst_machine.c b/sound/soc/mid-x86/mrst_machine.c index 5f19df5..7ea5bc5 100644 --- a/sound/soc/mid-x86/mrst_machine.c +++ b/sound/soc/mid-x86/mrst_machine.c @@ -163,6 +163,9 @@ static int mrst_audio_init(struct snd_soc_pcm_runtime *runtime) /* DMIC configuration: mono output to PCM2 */ snd_soc_update_bits(codec, UPD9976_MICCTRL, BIT(1)|BIT(0), 0x1);
+ /* Set MIC2 to pseudo-differential */ + snd_soc_update_bits(codec, UPD9976_MICSELVOL, BIT(4), BIT(4)); + ret = snd_soc_dapm_new_controls(dapm, mrst_audio_widgets, ARRAY_SIZE(mrst_audio_widgets));
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/codecs/upd9976.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/upd9976.c b/sound/soc/codecs/upd9976.c index f345db1..6fc9902 100644 --- a/sound/soc/codecs/upd9976.c +++ b/sound/soc/codecs/upd9976.c @@ -110,6 +110,11 @@ static int upd9976_mic2_bias_event(struct snd_soc_dapm_widget *widget, */ static DECLARE_TLV_DB_SCALE(mixer_tlv, -2500, 100, 0);
+/* + * Analog Volume: from -25 dB to 6 dB in 1 dB steps. + */ +static DECLARE_TLV_DB_SCALE(analog_tlv, -2500, 100, 0); + static const struct snd_kcontrol_new upd9976_snd_controls[] = { SOC_DOUBLE_R_TLV("Headphone & Speaker Volume", UPD9976_HPSPRLVOL, UPD9976_HPSPRRVOL, @@ -117,6 +122,9 @@ static const struct snd_kcontrol_new upd9976_snd_controls[] = { SOC_SINGLE("Internal Mic Switch", UPD9976_DMICCTRL1, 6, 1, 0), SOC_DOUBLE_R("Analog Mic Switch", UPD9976_LILSEL, UPD9976_LIRSEL, 6, 1, 0), + SOC_DOUBLE_R_TLV("Analog Capture Volume", + UPD9976_LILSEL, UPD9976_LILSEL, + 0, 0x1f, 1, analog_tlv), };
static const struct snd_kcontrol_new upd9976_hp_spkr_mixer_left_controls[] = {
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/codecs/upd9976.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/upd9976.c b/sound/soc/codecs/upd9976.c index 6fc9902..a35b891 100644 --- a/sound/soc/codecs/upd9976.c +++ b/sound/soc/codecs/upd9976.c @@ -115,6 +115,11 @@ static DECLARE_TLV_DB_SCALE(mixer_tlv, -2500, 100, 0); */ static DECLARE_TLV_DB_SCALE(analog_tlv, -2500, 100, 0);
+/* + * DMIC Volume: from -63 dB to 1 dB in 1 db steps. + */ +static DECLARE_TLV_DB_SCALE(dmic_tlv, -6300, 100, 1); + static const struct snd_kcontrol_new upd9976_snd_controls[] = { SOC_DOUBLE_R_TLV("Headphone & Speaker Volume", UPD9976_HPSPRLVOL, UPD9976_HPSPRRVOL, @@ -125,6 +130,8 @@ static const struct snd_kcontrol_new upd9976_snd_controls[] = { SOC_DOUBLE_R_TLV("Analog Capture Volume", UPD9976_LILSEL, UPD9976_LILSEL, 0, 0x1f, 1, analog_tlv), + SOC_SINGLE_TLV("Internal Mic Capture Volume", + UPD9976_DMICCTRL1, 0, 0x3f, 1, dmic_tlv), };
static const struct snd_kcontrol_new upd9976_hp_spkr_mixer_left_controls[] = {
participants (5)
-
Dimitris Papastamos
-
Koul, Vinod
-
Lu Guanqun
-
Mark Brown
-
Takashi Iwai