[alsa-devel] [PATCH v2 00/10] ASoC Support 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
v2: - change pr_err to dev_err - change DAPM widgets' name (adding correct suffix) - change to data based init of controls, widgets and route in machine driver - add Master Volume - change original "HP & Spker Mixer Volume" to "PCM Volume" - squash some patches into one, resulting in less number of patches - move jack handling from machine driver to codec driver - in codec probe, set the bias level to standby instead of off
Thanks for all the reviewers: - Mark Brown - Dimitris Papastamos - Takashi Iwai - Koul Vinod
Any more comments are welcome. :)
---
Lu Guanqun (8): ASoC: upd9976: Add Renesas uPD9976 codec driver ASoC: mrst_machine: add moorestown machine driver ASoC: upd9976: add DMIC support ASoC: upd9976: add Analog MIC support ASoC: upd9976: add jack detection function ASoC: mrst_machine: add capture functionality ASoC: mrst_machine: add jack detection support ASoC: mrst_machine: add initial config to machine driver
Wang Xingchao (2): ASoC: sst_platform: modify current cpu dai driver to suit the needs for moorestown platform ASoC: upd9976: add capture ability for dai driver
sound/soc/codecs/Kconfig | 4 sound/soc/codecs/Makefile | 2 sound/soc/codecs/upd9976.c | 697 ++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/upd9976.h | 87 +++++ sound/soc/mid-x86/Kconfig | 12 + sound/soc/mid-x86/Makefile | 2 sound/soc/mid-x86/mrst_machine.c | 228 ++++++++++++ sound/soc/mid-x86/sst_platform.c | 13 - 8 files changed, 1041 insertions(+), 4 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..7642356 --- /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) + dev_err(codec->dev, + "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) + dev_err(codec->dev, + "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); + +/* + * Audio DAC Volume: From -84 dB to 10.5 dB in 0.75 steps. + */ +static DECLARE_TLV_DB_SCALE(adac_tlv, -8400, 75, 0); +static const struct snd_kcontrol_new upd9976_snd_controls[] = { + SOC_DOUBLE_R_TLV("Master Volume", + UPD9976_AUDIOLVOL, UPD9976_AUDIORVOL, + 0, 0x7f, 1, adac_tlv), + SOC_DOUBLE_R_TLV("PCM 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 Switch", UPD9976_HPLMIXSEL, 4, 1, 1), + SOC_DAPM_SINGLE("Audio DAC Right Switch", UPD9976_HPLMIXSEL, 3, 1, 1), +}; + +static const struct snd_kcontrol_new upd9976_hp_spkr_mixer_right_controls[] = { + SOC_DAPM_SINGLE("Audio DAC Left Switch", UPD9976_HPRMIXSEL, 4, 1, 1), + SOC_DAPM_SINGLE("Audio DAC Right Switch", 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)), + + /* Microphose Bias */ + SND_SOC_DAPM_MICBIAS("MIC1 Bias", UPD9976_MICCTRL, 6, 0), + SND_SOC_DAPM_MICBIAS("MIC2 Bias", UPD9976_MICCTRL, 4, 0), + + /* PGA */ + SND_SOC_DAPM_PGA("HP Playback Left PGA", UPD9976_DRVPOWERCTRL, + 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP Playback Right PGA", UPD9976_DRVPOWERCTRL, + 1, 0, NULL, 0), + +}; + +static const struct snd_soc_dapm_route upd9976_dapm_routes[] = { + {"HP Spkr Mixer Left", "Audio DAC Left Switch", "ADAC"}, + {"HP Spkr Mixer Left", "Audio DAC Right Switch", "ADAC"}, + + {"HP Spkr Mixer Right", "Audio DAC Left Switch", "ADAC"}, + {"HP Spkr Mixer Right", "Audio DAC Right Switch", "ADAC"}, + + {"PREOUTL", NULL, "HP Spkr Mixer Left"}, + {"PREOUTR", NULL, "HP Spkr Mixer Right"}, + + {"HP Playback Left PGA", NULL, "HPINL"}, + {"HP Playback Right PGA", NULL, "HPINR"}, + + {"HPOUTL", NULL, "HP Playback Left PGA"}, + {"HPOUTR", NULL, "HP Playback Right PGA"}, + +}; + +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 rates[] = { 8000, 11025, 12000, 16000, 22050, + 24000, 32000, 0, 44100, 48000 }; + unsigned int rate; + unsigned int tmp; + int i; + + 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); + + rate = params_rate(params); + if (!rate) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(rates); i++) { + if (rates[i] == rate) { + tmp = i; + break; + } + } + if (i == ARRAY_SIZE(rates)) + 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_STANDBY); + + return 0; +} + +static int upd9976_codec_remove(struct snd_soc_codec *codec) +{ + upd9976_set_bias_level(codec, SND_SOC_BIAS_OFF); + + 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 Fri, May 06, 2011 at 01:46:03PM +0800, Lu Guanqun wrote:
+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)
dev_err(codec->dev,
"upd9976 read of 0x%x failed, error: %d\n", reg, ret);
- return value;
+}
Please factor this stuff out.
+/*
- Mixing Volume: from -25 dB to 6 dB in 1 dB steps.
- */
+static DECLARE_TLV_DB_SCALE(mixer_tlv, -2500, 100, 0);
+/*
- Audio DAC Volume: From -84 dB to 10.5 dB in 0.75 steps.
- */
+static DECLARE_TLV_DB_SCALE(adac_tlv, -8400, 75, 0); +static const struct snd_kcontrol_new upd9976_snd_controls[] = {
Use of blank lines here is really odd.
- SOC_DOUBLE_R_TLV("Master Volume",
UPD9976_AUDIOLVOL, UPD9976_AUDIORVOL,
0, 0x7f, 1, adac_tlv),
- SOC_DOUBLE_R_TLV("PCM Volume",
UPD9976_HPSPRLVOL, UPD9976_HPSPRRVOL,
0, 0x1f, 1, mixer_tlv),
PCM would usually be a digital audio stream but this is a control for a mixer.
+static struct snd_soc_dai_driver upd9976_dais[] = { +{
- .name = "upd9976-audio",
Previous issue with naming still applies.
- 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;
This is all very magic and lots of what's going on (especially with VAUDIOCNT) looks like it's actually trying to fiddle with bitmasks.
From: Wang Xingchao xingchao.wang@intel.com
Signed-off-by: Wang Xingchao xingchao.wang@intel.com Signed-off-by: Lu Guanqun guanqun.lu@intel.com Acked-by: Koul Vinod vinod.koul@intel.com --- sound/soc/mid-x86/sst_platform.c | 13 +++++++++---- 1 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c index 139db15..afa5eab 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), }, }, {
This machine driver glues upd9976 codec driver and sst_platform driver.
Playback via headphone and internal speaker is supported. To note: This internal speaker needs an extra GPIO to control its power. And these two output devices can be enabled and disabled independently.
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 | 194 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 208 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..10e4d40 --- /dev/null +++ b/sound/soc/mid-x86/mrst_machine.c @@ -0,0 +1,194 @@ +/* + * 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_kcontrol_new mrst_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +/* + * 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[] = { + {"HPINL", NULL, "PREOUTL"}, + {"HPINR", NULL, "PREOUTR"}, + + {"Headphone", NULL, "HPOUTL"}, + {"Headphone", NULL, "HPOUTR"}, + + {"Speaker", NULL, "PREOUTL"}, + {"Speaker", NULL, "PREOUTR"}, +}; + +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; + + 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_disable_pin(dapm, "Headphone"); + snd_soc_dapm_enable_pin(dapm, "Speaker"); + + 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 = "Headset-cpu-dai", + .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), + + .controls = mrst_snd_controls, + .num_controls = ARRAY_SIZE(mrst_snd_controls), + .dapm_widgets = mrst_audio_widgets, + .num_dapm_widgets = ARRAY_SIZE(mrst_audio_widgets), + .dapm_routes = mrst_audio_map, + .num_dapm_routes = ARRAY_SIZE(mrst_audio_map), +}; + +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) + goto fail_register_card; + + 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; +} + +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");
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 7be10ae..5b8d047 100644 --- a/sound/soc/codecs/upd9976.c +++ b/sound/soc/codecs/upd9976.c @@ -303,6 +303,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, }, };
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/codecs/upd9976.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 76 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/upd9976.c b/sound/soc/codecs/upd9976.c index 5b8d047..a668b47 100644 --- a/sound/soc/codecs/upd9976.c +++ b/sound/soc/codecs/upd9976.c @@ -71,6 +71,12 @@ static DECLARE_TLV_DB_SCALE(mixer_tlv, -2500, 100, 0); * Audio DAC Volume: From -84 dB to 10.5 dB in 0.75 steps. */ static DECLARE_TLV_DB_SCALE(adac_tlv, -8400, 75, 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("Master Volume", UPD9976_AUDIOLVOL, UPD9976_AUDIORVOL, @@ -78,6 +84,9 @@ static const struct snd_kcontrol_new upd9976_snd_controls[] = { SOC_DOUBLE_R_TLV("PCM Volume", UPD9976_HPSPRLVOL, UPD9976_HPSPRRVOL, 0, 0x1f, 1, mixer_tlv), + SOC_SINGLE("Internal Mic Switch", UPD9976_DMICCTRL1, 6, 1, 0), + 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[] = { @@ -90,6 +99,29 @@ static const struct snd_kcontrol_new upd9976_hp_spkr_mixer_right_controls[] = { SOC_DAPM_SINGLE("Audio DAC Right Switch", 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"), @@ -130,12 +162,34 @@ static const struct snd_soc_dapm_widget upd9976_dapm_widgets[] = { SND_SOC_DAPM_MICBIAS("MIC1 Bias", UPD9976_MICCTRL, 6, 0), SND_SOC_DAPM_MICBIAS("MIC2 Bias", UPD9976_MICCTRL, 4, 0),
+ SND_SOC_DAPM_MIXER("PCM2 In Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("PCM2 In No Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Mux */ + SND_SOC_DAPM_MUX("PCM2 Left Mux", SND_SOC_NOPM, 0, 0, + &upd9976_pcm2_left_mux_control), + SND_SOC_DAPM_MUX("PCM2 Right Mux", 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 PGA", UPD9976_DRVPOWERCTRL, 2, 0, NULL, 0), SND_SOC_DAPM_PGA("HP Playback Right PGA", UPD9976_DRVPOWERCTRL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AADC Left PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("AADC Right PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA("DMIC Gain PGA", 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[] = { @@ -154,6 +208,28 @@ static const struct snd_soc_dapm_route upd9976_dapm_routes[] = { {"HPOUTL", NULL, "HP Playback Left PGA"}, {"HPOUTR", NULL, "HP Playback Right PGA"},
+ {"DMICDAT", NULL, "DMIC Supply"}, + {"DMIC Gain PGA", NULL, "DMICDAT"}, + + {"AADC Left PGA", NULL, "AADC"}, + {"AADC Right PGA", NULL, "AADC"}, + + {"PCM2 Left Mux", "DMIC", "DMIC Gain PGA"}, + {"PCM2 Left Mux", "AADC Left", "AADC Left PGA"}, + + {"PCM2 Right Mux", "DMIC", "DMIC Gain PGA"}, + {"PCM2 Right Mux", "AADC Right", "AADC Right PGA"}, + + {"PCM2 In Mixer", NULL, "PCM2 Left Mux"}, + {"PCM2 In No Mixer", NULL, "PCM2 Left Mux"}, + + {"PCM2 In Mixer", NULL, "PCM2 Right Mux"}, + {"PCM2 In No Mixer", NULL, "PCM2 Right Mux"}, + + {"PCM2 Mux", "Mix", "PCM2 In Mixer"}, + {"PCM2 Mux", "No Mix", "PCM2 In No Mixer"}, + + {"PCM2 Out", NULL, "PCM2 Mux"}, };
static int upd9976_audio_digital_mute(struct snd_soc_dai *dai, int mute)
On Fri, May 06, 2011 at 01:46:24PM +0800, Lu Guanqun wrote:
Signed-off-by: Lu Guanqun guanqun.lu@intel.com
As previously mentioned please merge these trivial feature patches into the original driver unless there's some purpose in splitting them out. In this case it looks like the split is the cause of one of the review issues in the earlier post.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/codecs/upd9976.c | 29 +++++++++++++++++++++++++++++ 1 files changed, 29 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/upd9976.c b/sound/soc/codecs/upd9976.c index a668b47..cbcfbb2 100644 --- a/sound/soc/codecs/upd9976.c +++ b/sound/soc/codecs/upd9976.c @@ -77,6 +77,11 @@ static DECLARE_TLV_DB_SCALE(adac_tlv, -8400, 75, 0); */ static DECLARE_TLV_DB_SCALE(dmic_tlv, -6300, 100, 1);
+/* + * 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("Master Volume", UPD9976_AUDIOLVOL, UPD9976_AUDIORVOL, @@ -87,6 +92,11 @@ static const struct snd_kcontrol_new upd9976_snd_controls[] = { SOC_SINGLE("Internal Mic Switch", UPD9976_DMICCTRL1, 6, 1, 0), SOC_SINGLE_TLV("Internal Mic Capture Volume", UPD9976_DMICCTRL1, 0, 0x3f, 1, dmic_tlv), + 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[] = { @@ -99,6 +109,13 @@ static const struct snd_kcontrol_new upd9976_hp_spkr_mixer_right_controls[] = { SOC_DAPM_SINGLE("Audio DAC Right Switch", 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 = @@ -166,6 +183,9 @@ static const struct snd_soc_dapm_widget upd9976_dapm_widgets[] = { SND_SOC_DAPM_MIXER("PCM2 In No Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
/* Mux */ + SND_SOC_DAPM_MUX("Analog Right Mux", UPD9976_POWERCTRL1, 4, 0, + &upd9976_ar_mux_control), + SND_SOC_DAPM_MUX("PCM2 Left Mux", SND_SOC_NOPM, 0, 0, &upd9976_pcm2_left_mux_control), SND_SOC_DAPM_MUX("PCM2 Right Mux", SND_SOC_NOPM, 0, 0, @@ -184,6 +204,8 @@ static const struct snd_soc_dapm_widget upd9976_dapm_widgets[] = {
SND_SOC_DAPM_PGA("DMIC Gain PGA", UPD9976_DMICCTRL1, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIC2 In PGA", UPD9976_POWERCTRL1, 2, 0, NULL, 0), + /* Audio Interface */ SND_SOC_DAPM_AIF_OUT("PCM2 Out", "Audio Capture", 0, SND_SOC_NOPM, 0, 0), @@ -208,6 +230,13 @@ static const struct snd_soc_dapm_route upd9976_dapm_routes[] = { {"HPOUTL", NULL, "HP Playback Left PGA"}, {"HPOUTR", NULL, "HP Playback Right PGA"},
+ {"MIC2 In PGA", NULL, "MIC2"}, + + {"Analog Right Mux", "MIC", "MIC2 In PGA"}, + {"Analog Right Mux", "LineIn", "LINEINR"}, + + {"AADC", NULL, "Analog Right Mux"}, + {"DMICDAT", NULL, "DMIC Supply"}, {"DMIC Gain PGA", NULL, "DMICDAT"},
All the interrupt handling is done here in codec driver.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/codecs/upd9976.c | 182 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/upd9976.h | 4 + 2 files changed, 184 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/upd9976.c b/sound/soc/codecs/upd9976.c index 4a790c5..c1b08f9 100644 --- a/sound/soc/codecs/upd9976.c +++ b/sound/soc/codecs/upd9976.c @@ -27,6 +27,8 @@ */ #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/io.h> +#include <linux/list.h> #include <asm/intel_scu_ipc.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -37,6 +39,97 @@ #include <sound/jack.h> #include "upd9976.h"
+struct upd9976_priv { + int irq; + struct resource *irq_mem; + void __iomem *int_base; + struct snd_soc_jack *jack; + int jack_init; + struct list_head pending_status; + spinlock_t lock; +}; + +struct upd9976_jack_node { + struct list_head list; + u8 interrupt_status; +}; + +static irqreturn_t upd9976_jack_intr_handler(int irq, void *dev) +{ + struct upd9976_priv *upd9976 = dev; + u8 interrupt_status; + struct upd9976_jack_node *node; + unsigned long flags; + + memcpy_fromio(&interrupt_status, + upd9976->int_base, + sizeof(u8)); + + /* check if it's valid */ + if (!(interrupt_status & 0xf)) + return IRQ_NONE; + + node = kzalloc(sizeof(*node), GFP_ATOMIC); + if (!node) + return IRQ_NONE; + + INIT_LIST_HEAD(&node->list); + node->interrupt_status = interrupt_status; + + spin_lock_irqsave(&upd9976->lock, flags); + list_add_tail(&node->list, &upd9976->pending_status); + spin_unlock_irqrestore(&upd9976->lock, flags); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t upd9976_jack_thread_handler(int irq, void *data) +{ + struct upd9976_priv *upd9976 = data; + struct snd_soc_jack *jack = upd9976->jack; + struct snd_soc_codec *codec = jack->codec; + struct upd9976_jack_node *node; + u8 interrupt_status; + unsigned long flags; + int status = 0; + unsigned int value = 0; + int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET; + + spin_lock_irqsave(&upd9976->lock, flags); + if (list_empty(&upd9976->pending_status)) { + spin_unlock_irqrestore(&upd9976->lock, flags); + return IRQ_HANDLED; + } + node = list_first_entry(&upd9976->pending_status, + struct upd9976_jack_node, + list); + interrupt_status = node->interrupt_status; + list_del(&node->list); + kfree(node); + spin_unlock_irqrestore(&upd9976->lock, flags); + + 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); + + return IRQ_HANDLED; +} + static inline unsigned int upd9976_read(struct snd_soc_codec *codec, unsigned int reg) { @@ -62,6 +155,24 @@ static inline int upd9976_write(struct snd_soc_codec *codec, return ret; }
+int upd9976_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack, + unsigned int debounce) +{ + struct upd9976_priv *upd9976 = snd_soc_codec_get_drvdata(codec); + + if (!upd9976->jack_init) + return -ENODEV; + + upd9976->jack = jack; + + /* set debounce time */ + snd_soc_write(codec, UPD9976_AUXDBNC, debounce); + + return 0; +} +EXPORT_SYMBOL_GPL(upd9976_jack_detect); + /* * Mixing Volume: from -25 dB to 6 dB in 1 dB steps. */ @@ -452,13 +563,50 @@ static int upd9976_set_bias_level(struct snd_soc_codec *codec,
static int upd9976_codec_probe(struct snd_soc_codec *codec) { + struct upd9976_priv *upd9976 = snd_soc_codec_get_drvdata(codec); + upd9976_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ /* disable jack detection first, will be enabled later */ + snd_soc_update_bits(codec, UPD9976_MICCTRL, BIT(4), 0); + + spin_lock_init(&upd9976->lock); + INIT_LIST_HEAD(&upd9976->pending_status); + + upd9976->jack_init = 0; + + if (upd9976->irq < 0 || !upd9976->irq_mem) + return 0; + + upd9976->int_base = ioremap_nocache(upd9976->irq_mem->start, + resource_size(upd9976->irq_mem)); + if (!upd9976->int_base) + return 0; + + if (request_threaded_irq(upd9976->irq, + upd9976_jack_intr_handler, + upd9976_jack_thread_handler, + IRQF_SHARED, + "jack detection", + upd9976)) { + iounmap(upd9976->int_base); + return 0; + } + + upd9976->jack_init = 1; + return 0; }
static int upd9976_codec_remove(struct snd_soc_codec *codec) { + struct upd9976_priv *upd9976 = snd_soc_codec_get_drvdata(codec); + + if (upd9976->jack_init) { + free_irq(upd9976->irq, upd9976); + iounmap(upd9976->int_base); + } + upd9976_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0; @@ -481,13 +629,43 @@ static struct snd_soc_codec_driver upd9976_codec = {
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)); + struct upd9976_priv *upd9976; + int err; + + upd9976 = kzalloc(sizeof(*upd9976), GFP_KERNEL); + if (!upd9976) { + err = -ENOMEM; + goto fail; + } + + upd9976->irq = platform_get_irq(pdev, 0); + upd9976->irq_mem = platform_get_resource_byname(pdev, + IORESOURCE_MEM, + "IRQ_BASE"); + platform_set_drvdata(pdev, upd9976); + + err = snd_soc_register_codec(&pdev->dev, &upd9976_codec, + upd9976_dais, ARRAY_SIZE(upd9976_dais)); + if (err) + goto fail_register_codec; + + return 0; + +fail_register_codec: + platform_set_drvdata(pdev, NULL); + kfree(upd9976); +fail: + return err; }
static int __devexit upd9976_device_remove(struct platform_device *pdev) { + struct upd9976_priv *upd9976 = platform_get_drvdata(pdev); + snd_soc_unregister_codec(&pdev->dev); + platform_set_drvdata(pdev, NULL); + kfree(upd9976); + return 0; }
diff --git a/sound/soc/codecs/upd9976.h b/sound/soc/codecs/upd9976.h index ab2ea15..b1bb8d0 100644 --- a/sound/soc/codecs/upd9976.h +++ b/sound/soc/codecs/upd9976.h @@ -80,4 +80,8 @@
#define UPD9976_SAUXINT 0x132
+int upd9976_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack, + unsigned int debounce); + #endif
On Fri, May 06, 2011 at 01:46:34PM +0800, Lu Guanqun wrote:
- 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;
It's very strange that you do a mix of checks with and without the == - it doesn't matter either way but it'd be clearer to be consistent.
- if (upd9976->irq < 0 || !upd9976->irq_mem)
return 0;
It'd seem better to have this condition the other way around so if you need to add more conditional stuff things will be clearer.
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 10e4d40..131deff 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) @@ -85,6 +94,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;
The jack processing logic only pulls the trigger, all the heavy work such as interrupt handling is done in codec driver.
We need to enable MIC2 Bias to be able to get the jack interrupts.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/mid-x86/mrst_machine.c | 16 ++++++++++++++++ 1 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mid-x86/mrst_machine.c b/sound/soc/mid-x86/mrst_machine.c index 131deff..eb52a2b 100644 --- a/sound/soc/mid-x86/mrst_machine.c +++ b/sound/soc/mid-x86/mrst_machine.c @@ -35,6 +35,10 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/jack.h> +#include <../codecs/upd9976.h> + +static struct snd_soc_jack mrst_jack;
static const struct snd_kcontrol_new mrst_snd_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), @@ -85,6 +89,7 @@ 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;
snd_soc_dapm_nc_pin(dapm, "LINEINL"); snd_soc_dapm_nc_pin(dapm, "LINEINR"); @@ -99,6 +104,17 @@ static int mrst_audio_init(struct snd_soc_pcm_runtime *runtime)
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; + + if (!upd9976_jack_detect(codec, &mrst_jack, 0xff)) + snd_soc_dapm_force_enable_pin(dapm, "MIC2 Bias"); + return 0; }
On Moorestown, DMIC's output is set to PCM2 mono. And the Moorestown uses MIC2 pseudo-differential configuration.
Signed-off-by: Lu Guanqun guanqun.lu@intel.com --- sound/soc/mid-x86/mrst_machine.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mid-x86/mrst_machine.c b/sound/soc/mid-x86/mrst_machine.c index eb52a2b..806d18c 100644 --- a/sound/soc/mid-x86/mrst_machine.c +++ b/sound/soc/mid-x86/mrst_machine.c @@ -91,6 +91,12 @@ 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); + + /* Set MIC2 to pseudo-differential */ + snd_soc_update_bits(codec, UPD9976_MICSELVOL, BIT(4), BIT(4)); + snd_soc_dapm_nc_pin(dapm, "LINEINL"); snd_soc_dapm_nc_pin(dapm, "LINEINR"); snd_soc_dapm_nc_pin(dapm, "LINEOUTL");
On Fri, May 06, 2011 at 01:46:49PM +0800, Lu Guanqun wrote:
- /* 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));
These look like regular routing controls to me... If they do need to be set with magic writes they should be platform data but things like pseudo differential inputs are normally just represented as two single ended inputs for which userspace just happens to choose a path that looks differential.
On Fri, May 06, 2011 at 06:17:27PM +0800, Mark Brown wrote:
On Fri, May 06, 2011 at 01:46:03PM +0800, Lu Guanqun wrote:
+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)
dev_err(codec->dev,
"upd9976 read of 0x%x failed, error: %d\n", reg, ret);
- return value;
+}
Please factor this stuff out.
OK. Patch sent in another thread. Please give some comments whether it's the right way to achieve this.
+/*
- Mixing Volume: from -25 dB to 6 dB in 1 dB steps.
- */
+static DECLARE_TLV_DB_SCALE(mixer_tlv, -2500, 100, 0);
+/*
- Audio DAC Volume: From -84 dB to 10.5 dB in 0.75 steps.
- */
+static DECLARE_TLV_DB_SCALE(adac_tlv, -8400, 75, 0); +static const struct snd_kcontrol_new upd9976_snd_controls[] = {
Use of blank lines here is really odd.
I don't see a blank line between upd9976_snd_controls and Master Volume. Do you mean the blank line between the above comment and DECLARE_TLV_DB_SCALE line? I'll remove these blank lines.
- SOC_DOUBLE_R_TLV("Master Volume",
UPD9976_AUDIOLVOL, UPD9976_AUDIORVOL,
0, 0x7f, 1, adac_tlv),
- SOC_DOUBLE_R_TLV("PCM Volume",
UPD9976_HPSPRLVOL, UPD9976_HPSPRRVOL,
0, 0x1f, 1, mixer_tlv),
PCM would usually be a digital audio stream but this is a control for a mixer.
OK. I'll change it back to "Headphone Speaker Mixer Volume".
+static struct snd_soc_dai_driver upd9976_dais[] = { +{
- .name = "upd9976-audio",
Previous issue with naming still applies.
Will change it to upd9976-hifi, thanks for your suggestion.
- 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;
This is all very magic and lots of what's going on (especially with VAUDIOCNT) looks like it's actually trying to fiddle with bitmasks.
I'll define some bitmasks so that these magic number will go away, and others can grasp the idea what's going on...
Generally, it's used to disable/enable power save mode, power on/off audio rail... etc.
On Fri, May 06, 2011 at 07:07:31PM +0800, Mark Brown wrote:
On Fri, May 06, 2011 at 01:46:24PM +0800, Lu Guanqun wrote:
Signed-off-by: Lu Guanqun guanqun.lu@intel.com
As previously mentioned please merge these trivial feature patches into the original driver unless there's some purpose in splitting them out. In this case it looks like the split is the cause of one of the review issues in the earlier post.
No problem. I'll make them about three pathes, one for codec driver, one for platform driver, one for machine driver.
On Fri, May 06, 2011 at 08:53:11PM +0800, Mark Brown wrote:
On Fri, May 06, 2011 at 01:46:34PM +0800, Lu Guanqun wrote:
- 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;
It's very strange that you do a mix of checks with and without the == - it doesn't matter either way but it'd be clearer to be consistent.
OK. Let me make the code more clear. how about this?
+ if (interrupt_status & 0x1) { + if (snd_soc_read(codec, UPD9976_SAUXINT) == 0x1) + status |= SND_JACK_HEADSET; + } + + if (interrupt_status & 0x2) { + if (snd_soc_read(codec, UPD9976_SAUXINT) == 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;
- if (upd9976->irq < 0 || !upd9976->irq_mem)
return 0;
It'd seem better to have this condition the other way around so if you need to add more conditional stuff things will be clearer.
changing it to `if (upd9976->ira >= 0 && !upd9976->irq_mem) is logically ok, but it introduces more indentation for a large block of code...
On Fri, May 06, 2011 at 08:54:40PM +0800, Mark Brown wrote:
On Fri, May 06, 2011 at 01:46:49PM +0800, Lu Guanqun wrote:
- /* 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));
These look like regular routing controls to me... If they do need to be set with magic writes they should be platform data but things like pseudo differential inputs are normally just represented as two single ended inputs for which userspace just happens to choose a path that looks differential.
The above DMIC output might fit routing control in some way, but I think the MIC2 configuration is hardware specific, this is not related to routing quite much. Let me check whether it's easy to be implemented as a routing control or platform data...
On Sat, May 07, 2011 at 10:21:12PM +0800, Lu Guanqun wrote:
On Fri, May 06, 2011 at 06:17:27PM +0800, Mark Brown wrote:
On Fri, May 06, 2011 at 01:46:03PM +0800, Lu Guanqun wrote:
+static DECLARE_TLV_DB_SCALE(adac_tlv, -8400, 75, 0); +static const struct snd_kcontrol_new upd9976_snd_controls[] = {
Use of blank lines here is really odd.
I don't see a blank line between upd9976_snd_controls and Master Volume.
Yes, exactly. There's randomly omitted vertical space which makes things hard to read.
Do you mean the blank line between the above comment and DECLARE_TLV_DB_SCALE line? I'll remove these blank lines.
No, don't do that.
OK. Let me make the code more clear. how about this?
That's fine.
- if (upd9976->irq < 0 || !upd9976->irq_mem)
return 0;
It'd seem better to have this condition the other way around so if you need to add more conditional stuff things will be clearer.
changing it to `if (upd9976->ira >= 0 && !upd9976->irq_mem) is logically ok, but it introduces more indentation for a large block of code...
Put it in a function if that's an issue.
These look like regular routing controls to me... If they do need to be set with magic writes they should be platform data but things like pseudo differential inputs are normally just represented as two single ended inputs for which userspace just happens to choose a path that looks differential.
The above DMIC output might fit routing control in some way, but I think the MIC2 configuration is hardware specific, this is not related to routing quite much. Let me check whether it's easy to be implemented as a routing control or platform data...
Really, this is *very* common - a pseudo differential input is just two single ended inputs that happen to be used to fake a differential input on a given board. It's likely that a board will use the same setup most of the time (though sometimes it does change) but they should still not be configuring this with magic register writes in the machine driver.
On Sat, May 07, 2011 at 10:27:35PM +0800, Mark Brown wrote:
changing it to `if (upd9976->ira >= 0 && !upd9976->irq_mem) is logically ok, but it introduces more indentation for a large block of code...
Put it in a function if that's an issue.
OK.
These look like regular routing controls to me... If they do need to be set with magic writes they should be platform data but things like pseudo differential inputs are normally just represented as two single ended inputs for which userspace just happens to choose a path that looks differential.
The above DMIC output might fit routing control in some way, but I think the MIC2 configuration is hardware specific, this is not related to routing quite much. Let me check whether it's easy to be implemented as a routing control or platform data...
Really, this is *very* common - a pseudo differential input is just two single ended inputs that happen to be used to fake a differential input on a given board. It's likely that a board will use the same setup most of the time (though sometimes it does change) but they should still not be configuring this with magic register writes in the machine driver.
As this should not be configured in machine driver, I'll make it a control so that user is about to change it (this should only be set once, and leave it there untouched).
participants (2)
-
Lu Guanqun
-
Mark Brown